diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -116,7 +116,7 @@ 'rsrc/css/font/font-source-sans-pro.css' => '8906c07b', 'rsrc/css/font/phui-font-icon-base.css' => '3dad2ae3', 'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82', - 'rsrc/css/layout/phabricator-hovercard-view.css' => '44394670', + 'rsrc/css/layout/phabricator-hovercard-view.css' => 'f5f18f7d', 'rsrc/css/layout/phabricator-side-menu-view.css' => 'c1db9e9c', 'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894', 'rsrc/css/phui/calendar/phui-calendar-day.css' => '75b8cc4a', @@ -407,7 +407,7 @@ 'rsrc/js/application/passphrase/passphrase-credential-control.js' => '3cb0b2fc', 'rsrc/js/application/phame/phame-post-preview.js' => 'be807912', 'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '9c2623f4', - 'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => 'e58bf807', + 'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => 'a4fc6fe5', 'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf', 'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c', 'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef', @@ -636,7 +636,7 @@ 'javelin-behavior-phabricator-watch-anchor' => '9f36c42d', 'javelin-behavior-phame-post-preview' => 'be807912', 'javelin-behavior-pholio-mock-edit' => '9c2623f4', - 'javelin-behavior-pholio-mock-view' => 'e58bf807', + 'javelin-behavior-pholio-mock-view' => 'a4fc6fe5', 'javelin-behavior-phui-object-box-tabs' => '2bfa2836', 'javelin-behavior-phui-timeline-dropdown-menu' => '4d94d9c3', 'javelin-behavior-policy-control' => '9a340b3d', @@ -734,7 +734,7 @@ 'phabricator-filetree-view-css' => 'fccf9f82', 'phabricator-flag-css' => '5337623f', 'phabricator-hovercard' => '7e8468ae', - 'phabricator-hovercard-view-css' => '44394670', + 'phabricator-hovercard-view-css' => 'f5f18f7d', 'phabricator-keyboard-shortcut' => '1ae869f2', 'phabricator-keyboard-shortcut-manager' => 'c1700f6f', 'phabricator-main-menu-view' => '663e3810', @@ -1659,6 +1659,20 @@ 'javelin-uri', 'phabricator-notification', ), + 'a4fc6fe5' => array( + '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', + ), 'a80d0378' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1907,20 +1921,6 @@ 'javelin-workflow', 'javelin-magical-init', ), - 'e58bf807' => array( - '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', - ), 'e6e25838' => array( 'javelin-install', ), diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -78,16 +78,18 @@ require_celerity_resource('pholio-inline-comments-css'); $comment_form_id = celerity_generate_unique_node_id(); - $output = id(new PholioMockImagesView()) + $mock_view = id(new PholioMockImagesView()) ->setRequestURI($request->getRequestURI()) ->setCommentFormID($comment_form_id) ->setUser($user) ->setMock($mock) ->setImageID($this->imageID); + $this->addExtraQuicksandConfig( + array('mockViewConfig' => $mock_view->getBehaviorConfig())); $output = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Image')) - ->appendChild($output); + ->appendChild($mock_view); $add_comment = $this->buildAddCommentView($mock, $comment_form_id); diff --git a/src/applications/pholio/view/PholioMockImagesView.php b/src/applications/pholio/view/PholioMockImagesView.php --- a/src/applications/pholio/view/PholioMockImagesView.php +++ b/src/applications/pholio/view/PholioMockImagesView.php @@ -7,6 +7,10 @@ private $requestURI; private $commentFormID; + private $panelID; + private $viewportID; + private $behaviorConfig; + public function setCommentFormID($comment_form_id) { $this->commentFormID = $comment_form_id; return $this; @@ -39,25 +43,28 @@ return $this; } - public function render() { - if (!$this->mock) { - throw new Exception('Call setMock() before render()!'); - } - - $mock = $this->mock; + public function getMock() { + return $this->mock; + } - require_celerity_resource('javelin-behavior-pholio-mock-view'); + public function __construct() { + $this->panelID = celerity_generate_unique_node_id(); + $this->viewportID = celerity_generate_unique_node_id(); + } - $images = array(); - $panel_id = celerity_generate_unique_node_id(); - $viewport_id = celerity_generate_unique_node_id(); + public function getBehaviorConfig() { + if (!$this->getMock()) { + throw new Exception('Call setMock() before getBehaviorConfig()!'); + } - $ids = mpull($mock->getImages(), 'getID'); - if ($this->imageID && isset($ids[$this->imageID])) { - $selected_id = $this->imageID; - } else { - $selected_id = head_key($ids); + if ($this->behaviorConfig === null) { + $this->behaviorConfig = $this->calculateBehaviorConfig(); } + return $this->behaviorConfig; + } + + private function calculateBehaviorConfig() { + $mock = $this->getMock(); // TODO: We could maybe do a better job with tailoring this, which is the // image shown on the review stage. @@ -70,6 +77,8 @@ $engine->addObject($image, 'default'); } $engine->process(); + + $images = array(); $current_set = 0; foreach ($mock->getAllImages() as $image) { $file = $image->getFile(); @@ -102,6 +111,13 @@ ); } + $ids = mpull($mock->getImages(), 'getID'); + if ($this->imageID && isset($ids[$this->imageID])) { + $selected_id = $this->imageID; + } else { + $selected_id = head_key($ids); + } + $navsequence = array(); foreach ($mock->getImages() as $image) { $navsequence[] = $image->getID(); @@ -119,10 +135,11 @@ $login_uri = id(new PhutilURI('/login/')) ->setQueryParam('next', (string) $this->getRequestURI()); + $config = array( 'mockID' => $mock->getID(), - 'panelID' => $panel_id, - 'viewportID' => $viewport_id, + 'panelID' => $this->panelID, + 'viewportID' => $this->viewportID, 'commentFormID' => $this->getCommentFormID(), 'images' => $images, 'selectedID' => $selected_id, @@ -133,14 +150,31 @@ 'downloadIcon' => hsprintf('%s', $download_icon), 'currentSetSize' => $current_set, ); - Javelin::initBehavior('pholio-mock-view', $config); + return $config; + } + + public function render() { + if (!$this->getMock()) { + throw new Exception('Call setMock() before render()!'); + } + $mock = $this->getMock(); + + require_celerity_resource('javelin-behavior-pholio-mock-view'); + + $panel_id = $this->panelID; + $viewport_id = $this->viewportID; + + $config = $this->getBehaviorConfig(); + Javelin::initBehavior( + 'pholio-mock-view', + $this->getBehaviorConfig()); $mockview = ''; $mock_wrapper = javelin_tag( 'div', array( - 'id' => $viewport_id, + 'id' => $this->viewportID, 'sigil' => 'mock-viewport', 'class' => 'pholio-mock-image-viewport', ), @@ -157,7 +191,7 @@ $mock_wrapper = javelin_tag( 'div', array( - 'id' => $panel_id, + 'id' => $this->panelID, 'sigil' => 'mock-panel touchable', 'class' => 'pholio-mock-image-panel', ), diff --git a/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js --- a/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js +++ b/webroot/rsrc/js/application/pholio/behavior-pholio-mock-view.js @@ -13,97 +13,20 @@ * javelin-behavior-device * phabricator-keyboard-shortcut */ -JX.behavior('pholio-mock-view', function(config) { +JX.behavior('pholio-mock-view', function(config, statics) { 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) { + for (var ii = 0; ii < statics.images.length; ii++) { + if (statics.images[ii].id == id) { return ii; } } @@ -111,8 +34,8 @@ } function get_image_navindex(id) { - for (var ii = 0; ii < config.navsequence.length; ii++) { - if (config.navsequence[ii] == id) { + for (var ii = 0; ii < statics.navsequence.length; ii++) { + if (statics.navsequence[ii] == id) { return ii; } } @@ -124,7 +47,7 @@ if (idx === null) { return idx; } - return config.images[idx]; + return statics.images[idx]; } function onload_image(id) { @@ -146,11 +69,15 @@ if (idx === null) { return; } - idx = (idx + delta + config.navsequence.length) % config.navsequence.length; - select_image(config.navsequence[idx]); + idx = (idx + delta + statics.navsequence.length) % + statics.navsequence.length; + select_image(statics.navsequence[idx]); } function redraw_image() { + if (!statics.enabled) { + return; + } var new_y; // If we don't have an image yet, just scale the stage relative to the @@ -158,7 +85,7 @@ 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'; + statics.panel.style.height = new_y + 'px'; return; } @@ -167,7 +94,7 @@ // 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); + var w = JX.Vector.getDim(statics.panel); // Leave 24px margins on either side of the image. w.x -= 48; @@ -187,13 +114,13 @@ // 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'; + statics.panel.style.height = new_y + 'px'; - viewport.style.top = Math.floor((new_y - tag.height) / 2) + 'px'; + statics.viewport.style.top = Math.floor((new_y - tag.height) / 2) + 'px'; - stage.endLoad(); + statics.stage.endLoad(); - JX.DOM.setContent(viewport, tag); + JX.DOM.setContent(statics.viewport, tag); redraw_inlines(active_image.id); } @@ -202,7 +129,7 @@ active_image = get_image(image_id); active_image.tag = null; - stage.beginLoad(); + statics.stage.beginLoad(); var img = JX.$N('img', {className: 'pholio-mock-image'}); img.onload = JX.bind(img, onload_image, active_image.id); @@ -223,142 +150,11 @@ } load_inline_comments(); - if (image_id != config.selectedID) { + if (image_id != statics.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)); - - 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), @@ -445,7 +241,7 @@ return; } - stage.clearStage(); + statics.stage.clearStage(); var comment_holder = JX.$('mock-image-description'); JX.DOM.setContent(comment_holder, render_image_info(active_image)); @@ -475,7 +271,7 @@ var inline_selection = render_reticle(classes, 'pholio-mock-comment-icon phui-font-fa fa-comment'); - stage.addReticle(inline_selection, inline.id); + statics.stage.addReticle(inline_selection, inline.id); position_inline_rectangle(inline, inline_selection); } } @@ -528,7 +324,7 @@ d.x *= scale; d.y *= scale; - viewport.appendChild(selection_reticle); + statics.viewport.appendChild(selection_reticle); p.setPos(selection_reticle); d.setDim(selection_reticle); } @@ -548,40 +344,6 @@ }).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 )------------------------------------------------------------- */ @@ -612,7 +374,7 @@ target: '_blank', className: 'pholio-image-button-link' }, - JX.$H(config.fullIcon)))); + JX.$H(statics.fullIcon)))); classes = ['pholio-image-button', 'pholio-image-button-active']; @@ -631,7 +393,7 @@ href: image.downloadURI, className: 'pholio-image-button-link' }, - JX.$H(config.downloadIcon)))); + JX.$H(statics.downloadIcon)))); if (image.title === '') { image.title = 'Untitled Masterpiece'; @@ -643,7 +405,7 @@ info.push(title); if (!image.isObsolete) { - var img_len = config.currentSetSize; + var img_len = statics.currentSetSize; var rev = JX.$N( 'div', {className: 'pholio-image-revision'}, @@ -689,17 +451,6 @@ 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(); - }); - function lightbox_attach() { JX.DOM.alterClass(document.body, 'lightbox-attached', true); JX.Mask.show('jx-dark-mask'); @@ -726,6 +477,9 @@ } function lightbox_resize() { + if (!statics.enabled) { + return; + } if (!lightbox) { return; } @@ -743,23 +497,16 @@ return el; } - JX.Stratcom.listen('click', 'pholio-device-lightbox', lightbox_detach); - JX.Stratcom.listen('resize', null, lightbox_resize); - /* -( Preload )------------------------------------------------------------ */ - var preload = []; - for (var ii = 0; ii < config.images.length; ii++) { - preload.push(config.images[ii].stageURI); - } function preload_next() { - var next_src = preload[0]; + var next_src = statics.preload[0]; if (!next_src) { return; } - preload.splice(0, 1); + statics.preload.splice(0, 1); var img = JX.$N('img'); img.onload = preload_next; @@ -767,7 +514,323 @@ img.src = next_src; } - preload_next(); +/* -( Installaton )-------------------------------------------------------- */ + + + function update_statics(data) { + statics.enabled = true; + + statics.mockID = data.mockID; + statics.commentFormID = data.commentFormID; + statics.images = data.images; + statics.selectedID = data.selectedID; + statics.loggedIn = data.loggedIn; + statics.logInLink = data.logInLink; + statics.navsequence = data.navsequence; + statics.downloadIcon = data.downloadIcon; + statics.fullIcon = data.fullIcon; + statics.currentSetSize = data.currentSetSize; + + statics.stage = (function() { + var loading = false; + var stageElement = JX.$(data.panelID); + var viewElement = JX.$(data.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 + }; + })(); + + statics.panel = JX.$(data.panelID); + statics.viewport = JX.$(data.viewportID); + + select_image(data.selectedID); + + load_inline_comments(); + if (data.loggedIn && data.commentFormID) { + JX.DOM.invoke(JX.$(data.commentFormID), 'shouldRefresh'); + } + redraw_image(); + + statics.preload = []; + for (var ii = 0; ii < data.images.length; ii++) { + statics.preload.push(data.images[ii].stageURI); + } + + preload_next(); + + } + + function install_extra_listeners() { + JX.DOM.listen(statics.panel, 'gesture.swipe.end', null, function(e) { + var data = e.getData(); + + if (data.length <= (JX.Vector.getDim(statics.panel) / 2)) { + // If the user didn't move their finger far enough, don't switch. + return; + } + switch_image(data.direction == 'right' ? -1 : 1); + }); + } + + function install_mock_view() { + 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); + } + }); + + JX.Stratcom.listen( + 'click', + 'mock-thumbnail', + function(e) { + if (!e.isNormalMouseEvent()) { + return; + } + e.kill(); + select_image(e.getNodeData('mock-thumbnail').imageID); + }); + + 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 (!statics.enabled) { + return; + } + 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.$(statics.commentFormID), 'shouldRefresh'); + }; + + new JX.Workflow('/pholio/inline/' + id + '/') + .setHandler(handler) + .start(); + }); + + JX.Stratcom.listen( + 'mouseup', + null, + function(e) { + if (!statics.enabled) { + return; + } + if (!is_dragging) { + return; + } + + is_dragging = false; + if (!statics.loggedIn) { + new JX.Workflow(statics.logInLink).start(); + return; + } + + drag_end = get_image_xy(JX.$V(e)); + + resize_selection(16); + + var data = { + mockID: statics.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.$(statics.commentFormID), 'shouldRefresh'); + }; + + clear_selection(); + + new JX.Workflow('/pholio/inline/', data) + .setHandler(handler) + .start(); + }); + + JX.Stratcom.listen('resize', null, 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(); + + + /* Lightbox listeners */ + 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); + + JX.Stratcom.listen( + 'quicksand-redraw', + null, + function (e) { + var data = e.getData(); + var new_config; + if (!data.newResponse.mockViewConfig) { + statics.enabled = false; + return; + } + if (data.fromServer) { + new_config = data.newResponse.mockViewConfig; + } else { + new_config = statics.mockViewConfigCache[data.newResponseID]; + } + update_statics(new_config); + if (data.fromServer) { + install_extra_listeners(); + } + }); + } + + if (!statics.installed) { + var current_page_id = JX.Quicksand.getCurrentPageID(); + statics.mockViewConfigCache = {}; + statics.mockViewConfigCache[current_page_id] = config; + update_statics(config); + + statics.installed = install_mock_view(); + install_extra_listeners(); + } });