mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +01:00
0efa1ca643
Summary: - When the button is clicked, actually download the file or image. - Add aural hints for the icon-only buttons. - Use a "photo" icon for "view raw image", so the "arrows pointing outward" icon can be used for "fullscreen" some day. Test Plan: Clicked link, got a download. Reviewers: chad Reviewed By: chad Subscribers: epriestley Differential Revision: https://secure.phabricator.com/D9574
759 lines
18 KiB
JavaScript
759 lines
18 KiB
JavaScript
/**
|
|
* @provides javelin-behavior-pholio-mock-view
|
|
* @requires javelin-behavior
|
|
* javelin-util
|
|
* javelin-stratcom
|
|
* javelin-dom
|
|
* javelin-vector
|
|
* javelin-magical-init
|
|
* javelin-request
|
|
* javelin-history
|
|
* javelin-workflow
|
|
* javelin-mask
|
|
* javelin-behavior-device
|
|
* phabricator-keyboard-shortcut
|
|
*/
|
|
JX.behavior('pholio-mock-view', function(config) {
|
|
var is_dragging = false;
|
|
|
|
var drag_begin;
|
|
var drag_end;
|
|
var panel = JX.$(config.panelID);
|
|
var viewport = JX.$(config.viewportID);
|
|
|
|
var selection_reticle;
|
|
var active_image;
|
|
|
|
var inline_comments = {};
|
|
|
|
|
|
/* -( Stage )-------------------------------------------------------------- */
|
|
|
|
|
|
var stage = (function() {
|
|
var loading = false;
|
|
var stageElement = JX.$(config.panelID);
|
|
var viewElement = JX.$(config.viewportID);
|
|
var reticles = [];
|
|
|
|
function begin_load() {
|
|
if (loading) {
|
|
return;
|
|
}
|
|
loading = true;
|
|
clear_stage();
|
|
draw_loading();
|
|
}
|
|
|
|
function end_load() {
|
|
if (!loading) {
|
|
return;
|
|
}
|
|
loading = false;
|
|
draw_loading();
|
|
}
|
|
|
|
function draw_loading() {
|
|
JX.DOM.alterClass(stageElement, 'pholio-image-loading', loading);
|
|
}
|
|
|
|
function add_reticle(reticle, id) {
|
|
mark_ref(reticle, id);
|
|
reticles.push(reticle);
|
|
viewElement.appendChild(reticle);
|
|
}
|
|
|
|
function clear_stage() {
|
|
var ii;
|
|
for (ii = 0; ii < reticles.length; ii++) {
|
|
JX.DOM.remove(reticles[ii]);
|
|
}
|
|
reticles = [];
|
|
}
|
|
|
|
function mark_ref(node, id) {
|
|
JX.Stratcom.addSigil(node, 'pholio-inline-ref');
|
|
JX.Stratcom.addData(node, {inlineID: id});
|
|
}
|
|
|
|
return {
|
|
beginLoad: begin_load,
|
|
endLoad: end_load,
|
|
addReticle: add_reticle,
|
|
clearStage: clear_stage
|
|
};
|
|
})();
|
|
|
|
JX.enableDispatch(document.body, 'mouseenter');
|
|
JX.enableDispatch(document.body, 'mouseleave');
|
|
|
|
JX.Stratcom.listen(
|
|
['mouseenter', 'mouseover'],
|
|
'mock-panel',
|
|
function(e) {
|
|
JX.DOM.alterClass(e.getNode('mock-panel'), 'mock-has-cursor', true);
|
|
});
|
|
|
|
JX.Stratcom.listen('mouseleave', 'mock-panel', function(e) {
|
|
var node = e.getNode('mock-panel');
|
|
if (e.getTarget() == node) {
|
|
JX.DOM.alterClass(node, 'mock-has-cursor', false);
|
|
}
|
|
});
|
|
|
|
function get_image_index(id) {
|
|
for (var ii = 0; ii < config.images.length; ii++) {
|
|
if (config.images[ii].id == id) {
|
|
return ii;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function get_image_navindex(id) {
|
|
for (var ii = 0; ii < config.navsequence.length; ii++) {
|
|
if (config.navsequence[ii] == id) {
|
|
return ii;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function get_image(id) {
|
|
var idx = get_image_index(id);
|
|
if (idx === null) {
|
|
return idx;
|
|
}
|
|
return config.images[idx];
|
|
}
|
|
|
|
function onload_image(id) {
|
|
if (active_image.id != id) {
|
|
// The user has clicked another image before this one loaded, so just
|
|
// bail.
|
|
return;
|
|
}
|
|
|
|
active_image.tag = this;
|
|
redraw_image();
|
|
}
|
|
|
|
function switch_image(delta) {
|
|
if (!active_image) {
|
|
return;
|
|
}
|
|
var idx = get_image_navindex(active_image.id);
|
|
if (idx === null) {
|
|
return;
|
|
}
|
|
idx = (idx + delta + config.navsequence.length) % config.navsequence.length;
|
|
select_image(config.navsequence[idx]);
|
|
}
|
|
|
|
function redraw_image() {
|
|
var new_y;
|
|
|
|
// If we don't have an image yet, just scale the stage relative to the
|
|
// entire viewport height so the jump isn't too jumpy when the image loads.
|
|
if (!active_image || !active_image.tag) {
|
|
new_y = (JX.Vector.getViewport().y * 0.80);
|
|
new_y = Math.max(320, new_y);
|
|
panel.style.height = new_y + 'px';
|
|
|
|
return;
|
|
}
|
|
|
|
var tag = active_image.tag;
|
|
|
|
// If the image is too wide for the viewport, scale it down so it fits.
|
|
// If it is too tall, just let the viewport scroll.
|
|
var w = JX.Vector.getDim(panel);
|
|
|
|
// Leave 24px margins on either side of the image.
|
|
w.x -= 48;
|
|
|
|
var scale = 1;
|
|
if (w.x < tag.naturalWidth) {
|
|
scale = Math.min(scale, w.x / tag.naturalWidth);
|
|
}
|
|
|
|
if (scale < 1) {
|
|
tag.width = Math.floor(scale * tag.naturalWidth);
|
|
tag.height = Math.floor(scale * tag.naturalHeight);
|
|
} else {
|
|
tag.width = tag.naturalWidth;
|
|
tag.height = tag.naturalHeight;
|
|
}
|
|
|
|
// Scale the viewport's vertical size to the image's adjusted size.
|
|
new_y = Math.max(320, tag.height + 48);
|
|
panel.style.height = new_y + 'px';
|
|
|
|
viewport.style.top = Math.floor((new_y - tag.height) / 2) + 'px';
|
|
|
|
stage.endLoad();
|
|
|
|
JX.DOM.setContent(viewport, tag);
|
|
|
|
redraw_inlines(active_image.id);
|
|
}
|
|
|
|
function select_image(image_id) {
|
|
active_image = get_image(image_id);
|
|
active_image.tag = null;
|
|
|
|
stage.beginLoad();
|
|
|
|
var img = JX.$N('img', {className: 'pholio-mock-image'});
|
|
img.onload = JX.bind(img, onload_image, active_image.id);
|
|
img.src = active_image.stageURI;
|
|
|
|
var thumbs = JX.DOM.scry(
|
|
JX.$('pholio-mock-thumb-grid'),
|
|
'a',
|
|
'mock-thumbnail');
|
|
|
|
for(var k in thumbs) {
|
|
var thumb_meta = JX.Stratcom.getData(thumbs[k]);
|
|
|
|
JX.DOM.alterClass(
|
|
thumbs[k],
|
|
'pholio-mock-thumb-grid-current',
|
|
(active_image.id == thumb_meta.imageID));
|
|
}
|
|
|
|
load_inline_comments();
|
|
if (image_id != config.selectedID) {
|
|
JX.History.replace(active_image.pageURI);
|
|
}
|
|
}
|
|
|
|
JX.Stratcom.listen(
|
|
'click',
|
|
'mock-thumbnail',
|
|
function(e) {
|
|
if (!e.isNormalMouseEvent()) {
|
|
return;
|
|
}
|
|
e.kill();
|
|
select_image(e.getNodeData('mock-thumbnail').imageID);
|
|
});
|
|
|
|
select_image(config.selectedID);
|
|
|
|
JX.Stratcom.listen('mousedown', 'mock-viewport', function(e) {
|
|
if (!e.isNormalMouseEvent()) {
|
|
return;
|
|
}
|
|
|
|
if (JX.Device.getDevice() != 'desktop') {
|
|
return;
|
|
}
|
|
|
|
if (JX.Stratcom.pass()) {
|
|
return;
|
|
}
|
|
|
|
if (is_dragging) {
|
|
return;
|
|
}
|
|
|
|
e.kill();
|
|
|
|
if (!active_image.isImage) {
|
|
// If this is a PDF or something like that, we eat the event but we
|
|
// don't let users add inlines to the thumbnail.
|
|
return;
|
|
}
|
|
|
|
is_dragging = true;
|
|
drag_begin = get_image_xy(JX.$V(e));
|
|
drag_end = drag_begin;
|
|
|
|
redraw_selection();
|
|
});
|
|
|
|
|
|
JX.enableDispatch(document.body, 'mousemove');
|
|
JX.Stratcom.listen('mousemove', null, function(e) {
|
|
if (!is_dragging) {
|
|
return;
|
|
}
|
|
drag_end = get_image_xy(JX.$V(e));
|
|
redraw_selection();
|
|
});
|
|
|
|
JX.Stratcom.listen(
|
|
'mousedown',
|
|
'pholio-inline-ref',
|
|
function(e) {
|
|
e.kill();
|
|
|
|
var id = e.getNodeData('pholio-inline-ref').inlineID;
|
|
|
|
var active_id = active_image.id;
|
|
var handler = function(r) {
|
|
var inlines = inline_comments[active_id];
|
|
|
|
for (var ii = 0; ii < inlines.length; ii++) {
|
|
if (inlines[ii].id == id) {
|
|
if (r.id) {
|
|
inlines[ii] = r;
|
|
} else {
|
|
inlines.splice(ii, 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
redraw_inlines(active_id);
|
|
JX.DOM.invoke(JX.$(config.commentFormID), 'shouldRefresh');
|
|
};
|
|
|
|
new JX.Workflow('/pholio/inline/' + id + '/')
|
|
.setHandler(handler)
|
|
.start();
|
|
});
|
|
|
|
JX.Stratcom.listen(
|
|
'mouseup',
|
|
null,
|
|
function(e) {
|
|
if (!is_dragging) {
|
|
return;
|
|
}
|
|
|
|
is_dragging = false;
|
|
if (!config.loggedIn) {
|
|
new JX.Workflow(config.logInLink).start();
|
|
return;
|
|
}
|
|
|
|
drag_end = get_image_xy(JX.$V(e));
|
|
var scale = get_image_scale();
|
|
|
|
resize_selection(16);
|
|
|
|
var data = {
|
|
mockID: config.mockID,
|
|
imageID: active_image.id,
|
|
startX: Math.min(drag_begin.x, drag_end.x),
|
|
startY: Math.min(drag_begin.y, drag_end.y),
|
|
endX: Math.max(drag_begin.x, drag_end.x),
|
|
endY: Math.max(drag_begin.y, drag_end.y)
|
|
};
|
|
|
|
var handler = function(r) {
|
|
if (!inline_comments[active_image.id]) {
|
|
inline_comments[active_image.id] = [];
|
|
}
|
|
inline_comments[active_image.id].push(r);
|
|
|
|
redraw_inlines(active_image.id);
|
|
JX.DOM.invoke(JX.$(config.commentFormID), 'shouldRefresh');
|
|
};
|
|
|
|
clear_selection();
|
|
|
|
new JX.Workflow('/pholio/inline/', data)
|
|
.setHandler(handler)
|
|
.start();
|
|
});
|
|
|
|
function resize_selection(min_size) {
|
|
var start = {
|
|
x: Math.min(drag_begin.x, drag_end.x),
|
|
y: Math.min(drag_begin.y, drag_end.y)
|
|
};
|
|
var end = {
|
|
x: Math.max(drag_begin.x, drag_end.x),
|
|
y: Math.max(drag_begin.y, drag_end.y)
|
|
};
|
|
|
|
var width = end.x - start.x;
|
|
var height = end.y - start.y;
|
|
var addon;
|
|
|
|
if (width < min_size) {
|
|
addon = (min_size-width)/2;
|
|
|
|
start.x = Math.max(0, start.x - addon);
|
|
end.x = Math.min(active_image.tag.naturalWidth, end.x + addon);
|
|
|
|
if (start.x === 0) {
|
|
end.x = Math.min(min_size, active_image.tag.naturalWidth);
|
|
} else if (end.x == active_image.tag.naturalWidth) {
|
|
start.x = Math.max(0, active_image.tag.naturalWidth - min_size);
|
|
}
|
|
}
|
|
|
|
if (height < min_size) {
|
|
addon = (min_size-height)/2;
|
|
|
|
start.y = Math.max(0, start.y - addon);
|
|
end.y = Math.min(active_image.tag.naturalHeight, end.y + addon);
|
|
|
|
if (start.y === 0) {
|
|
end.y = Math.min(min_size, active_image.tag.naturalHeight);
|
|
} else if (end.y == active_image.tag.naturalHeight) {
|
|
start.y = Math.max(0, active_image.tag.naturalHeight - min_size);
|
|
}
|
|
}
|
|
|
|
drag_begin = start;
|
|
drag_end = end;
|
|
redraw_selection();
|
|
}
|
|
|
|
function render_image_header(image) {
|
|
// Render image dimensions and visible size. If we have this infomation
|
|
// from the server we can display some of it immediately; otherwise, we need
|
|
// to wait for the image to load so we can read dimension information from
|
|
// it.
|
|
|
|
var image_x = image.width;
|
|
var image_y = image.height;
|
|
var display_x = null;
|
|
if (image.tag) {
|
|
image_x = image.tag.naturalWidth;
|
|
image_y = image.tag.naturalHeight;
|
|
display_x = image.tag.width;
|
|
}
|
|
|
|
var visible = [];
|
|
if (image_x) {
|
|
if (display_x) {
|
|
var area = Math.round(100 * (display_x / image_x));
|
|
visible.push(
|
|
JX.$N(
|
|
'span',
|
|
{className: 'pholio-visible-size'},
|
|
[area, '%']));
|
|
visible.push(' ');
|
|
}
|
|
visible.push(['(', image_x, ' \u00d7 ', image_y, ')']);
|
|
}
|
|
|
|
return visible;
|
|
}
|
|
|
|
function redraw_inlines(id) {
|
|
if (!active_image) {
|
|
return;
|
|
}
|
|
|
|
if (active_image.id != id) {
|
|
return;
|
|
}
|
|
|
|
stage.clearStage();
|
|
var comment_holder = JX.$('mock-image-description');
|
|
JX.DOM.setContent(comment_holder, render_image_info(active_image));
|
|
|
|
var image_header = JX.$('mock-image-header');
|
|
JX.DOM.setContent(image_header, render_image_header(active_image));
|
|
|
|
var inlines = inline_comments[active_image.id];
|
|
if (!inlines || !inlines.length) {
|
|
return;
|
|
}
|
|
|
|
for (var ii = 0; ii < inlines.length; ii++) {
|
|
var inline = inlines[ii];
|
|
|
|
if (!active_image.tag) {
|
|
// The image itself hasn't loaded yet, so we can't draw the inline
|
|
// reticles.
|
|
continue;
|
|
}
|
|
|
|
var classes = [];
|
|
if (!inline.transactionPHID) {
|
|
classes.push('pholio-mock-reticle-draft phui-font-fa fa-comment');
|
|
} else {
|
|
classes.push('pholio-mock-reticle-final phui-font-fa fa-comment');
|
|
}
|
|
|
|
var inline_selection = render_reticle(classes);
|
|
stage.addReticle(inline_selection, inline.id);
|
|
position_inline_rectangle(inline, inline_selection);
|
|
}
|
|
}
|
|
|
|
function position_inline_rectangle(inline, rect) {
|
|
var scale = get_image_scale();
|
|
|
|
JX.$V(scale * inline.x, scale * inline.y).setPos(rect);
|
|
JX.$V(scale * inline.width, scale * inline.height).setDim(rect);
|
|
}
|
|
|
|
function get_image_xy(p) {
|
|
var img = active_image.tag;
|
|
var imgp = JX.$V(img);
|
|
|
|
var scale = 1 / get_image_scale();
|
|
|
|
var x = scale * Math.max(0, Math.min(p.x - imgp.x, img.width));
|
|
var y = scale * Math.max(0, Math.min(p.y - imgp.y, img.height));
|
|
|
|
return {
|
|
x: x,
|
|
y: y
|
|
};
|
|
}
|
|
|
|
function get_image_scale() {
|
|
var img = active_image.tag;
|
|
return Math.min(
|
|
img.width / img.naturalWidth,
|
|
img.height / img.naturalHeight);
|
|
}
|
|
|
|
function redraw_selection() {
|
|
var classes = ['pholio-mock-reticle-selection'];
|
|
selection_reticle = selection_reticle || render_reticle(classes);
|
|
|
|
var p = JX.$V(
|
|
Math.min(drag_begin.x, drag_end.x),
|
|
Math.min(drag_begin.y, drag_end.y));
|
|
|
|
var d = JX.$V(
|
|
Math.max(drag_begin.x, drag_end.x) - p.x,
|
|
Math.max(drag_begin.y, drag_end.y) - p.y);
|
|
|
|
var scale = get_image_scale();
|
|
|
|
p.x *= scale;
|
|
p.y *= scale;
|
|
d.x *= scale;
|
|
d.y *= scale;
|
|
|
|
viewport.appendChild(selection_reticle);
|
|
p.setPos(selection_reticle);
|
|
d.setDim(selection_reticle);
|
|
}
|
|
|
|
function clear_selection() {
|
|
selection_reticle && JX.DOM.remove(selection_reticle);
|
|
selection_reticle = null;
|
|
}
|
|
|
|
function load_inline_comments() {
|
|
var id = active_image.id;
|
|
var inline_comments_uri = '/pholio/inline/list/' + id + '/';
|
|
|
|
new JX.Request(inline_comments_uri, function(r) {
|
|
inline_comments[id] = r;
|
|
redraw_inlines(id);
|
|
}).send();
|
|
}
|
|
|
|
load_inline_comments();
|
|
if (config.loggedIn && config.commentFormID) {
|
|
JX.DOM.invoke(JX.$(config.commentFormID), 'shouldRefresh');
|
|
}
|
|
|
|
JX.Stratcom.listen('resize', null, redraw_image);
|
|
redraw_image();
|
|
|
|
|
|
/* -( Keyboard Shortcuts )------------------------------------------------- */
|
|
|
|
|
|
new JX.KeyboardShortcut(['j', 'right'], 'Show next image.')
|
|
.setHandler(function() {
|
|
switch_image(1);
|
|
})
|
|
.register();
|
|
|
|
new JX.KeyboardShortcut(['k', 'left'], 'Show previous image.')
|
|
.setHandler(function() {
|
|
switch_image(-1);
|
|
})
|
|
.register();
|
|
|
|
JX.DOM.listen(panel, 'gesture.swipe.end', null, function(e) {
|
|
var data = e.getData();
|
|
|
|
if (data.length <= (JX.Vector.getDim(panel) / 2)) {
|
|
// If the user didn't move their finger far enough, don't switch.
|
|
return;
|
|
}
|
|
|
|
switch_image(data.direction == 'right' ? -1 : 1);
|
|
});
|
|
|
|
/* -( Render )------------------------------------------------------------- */
|
|
|
|
|
|
function render_image_info(image) {
|
|
var info = [];
|
|
|
|
var buttons = [];
|
|
|
|
var classes = ['pholio-image-button'];
|
|
|
|
if (image.isViewable) {
|
|
classes.push('pholio-image-button-active');
|
|
} else {
|
|
classes.push('pholio-image-button-disabled');
|
|
}
|
|
|
|
buttons.push(
|
|
JX.$N(
|
|
'div',
|
|
{
|
|
className: classes.join(' ')
|
|
},
|
|
JX.$N(
|
|
image.isViewable ? 'a' : 'span',
|
|
{
|
|
href: image.fullURI,
|
|
target: '_blank',
|
|
className: 'pholio-image-button-link'
|
|
},
|
|
JX.$H(config.fullIcon))));
|
|
|
|
classes = ['pholio-image-button', 'pholio-image-button-active'];
|
|
|
|
buttons.push(
|
|
JX.$N(
|
|
'form',
|
|
{
|
|
className: classes.join(' '),
|
|
action: image.downloadURI,
|
|
method: 'POST',
|
|
sigil: 'download'
|
|
},
|
|
JX.$N(
|
|
'button',
|
|
{
|
|
href: image.downloadURI,
|
|
className: 'pholio-image-button-link'
|
|
},
|
|
JX.$H(config.downloadIcon))));
|
|
|
|
var title = JX.$N(
|
|
'div',
|
|
{className: 'pholio-image-title'},
|
|
image.title);
|
|
info.push(title);
|
|
|
|
if (!image.isObsolete) {
|
|
var embed = JX.$N(
|
|
'div',
|
|
{className: 'pholio-image-embedding'},
|
|
JX.$H('Embed this image: {M' + config.mockID +
|
|
', image=' + image.id + '}'));
|
|
info.push(embed);
|
|
}
|
|
|
|
for (var ii = 0; ii < info.length; ii++) {
|
|
info[ii] = JX.$N('div', {className: 'pholio-image-info-item'}, info[ii]);
|
|
}
|
|
info = JX.$N('div', {className: 'pholio-image-info'}, info);
|
|
|
|
var desc = JX.$N(
|
|
'div',
|
|
{className: 'pholio-image-description'},
|
|
JX.$H(image.descriptionMarkup));
|
|
|
|
return [buttons, info, desc];
|
|
}
|
|
|
|
function render_reticle(classes) {
|
|
return JX.$N(
|
|
'div',
|
|
{className: ['pholio-mock-reticle'].concat(classes).join(' ')});
|
|
}
|
|
|
|
|
|
/* -( Device Lightbox )---------------------------------------------------- */
|
|
|
|
// On devices, we show images full-size when the user taps them instead of
|
|
// attempting to implement inlines.
|
|
|
|
var lightbox = null;
|
|
|
|
JX.Stratcom.listen('click', 'mock-viewport', function(e) {
|
|
if (!e.isNormalMouseEvent()) {
|
|
return;
|
|
}
|
|
if (JX.Device.getDevice() == 'desktop') {
|
|
return;
|
|
}
|
|
lightbox_attach();
|
|
e.kill();
|
|
});
|
|
|
|
JX.Stratcom.listen('click', 'pholio-device-lightbox', lightbox_detach);
|
|
JX.Stratcom.listen('resize', null, lightbox_resize);
|
|
|
|
function lightbox_attach() {
|
|
JX.DOM.alterClass(document.body, 'lightbox-attached', true);
|
|
JX.Mask.show('jx-dark-mask');
|
|
|
|
lightbox = lightbox_render();
|
|
var image = JX.$N('img');
|
|
image.onload = lightbox_loaded;
|
|
setTimeout(function() {
|
|
image.src = active_image.stageURI;
|
|
}, 1000);
|
|
JX.DOM.setContent(lightbox, image);
|
|
JX.DOM.alterClass(lightbox, 'pholio-device-lightbox-loading', true);
|
|
|
|
lightbox_resize();
|
|
|
|
document.body.appendChild(lightbox);
|
|
}
|
|
|
|
function lightbox_detach() {
|
|
JX.DOM.remove(lightbox);
|
|
JX.Mask.hide();
|
|
JX.DOM.alterClass(document.body, 'lightbox-attached', false);
|
|
lightbox = null;
|
|
}
|
|
|
|
function lightbox_resize(e) {
|
|
if (!lightbox) {
|
|
return;
|
|
}
|
|
JX.Vector.getScroll().setPos(lightbox);
|
|
JX.Vector.getViewport().setDim(lightbox);
|
|
}
|
|
|
|
function lightbox_loaded() {
|
|
JX.DOM.alterClass(lightbox, 'pholio-device-lightbox-loading', false);
|
|
}
|
|
|
|
function lightbox_render() {
|
|
var el = JX.$N('div', {className: 'pholio-device-lightbox'});
|
|
JX.Stratcom.addSigil(el, 'pholio-device-lightbox');
|
|
return el;
|
|
}
|
|
|
|
|
|
/* -( Preload )------------------------------------------------------------ */
|
|
|
|
var preload = [];
|
|
for (var ii = 0; ii < config.images.length; ii++) {
|
|
preload.push(config.images[ii].stageURI);
|
|
}
|
|
|
|
function preload_next() {
|
|
next_src = preload[0];
|
|
if (!next_src) {
|
|
return;
|
|
}
|
|
preload.splice(0, 1);
|
|
|
|
var img = JX.$N('img');
|
|
img.onload = preload_next;
|
|
img.onerror = preload_next;
|
|
img.src = next_src;
|
|
}
|
|
|
|
preload_next();
|
|
|
|
|
|
});
|