diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php index cea2272bd..9b1f1c061 100644 --- a/src/applications/files/controller/PhabricatorFileInfoController.php +++ b/src/applications/files/controller/PhabricatorFileInfoController.php @@ -1,164 +1,165 @@ phid = $data['phid']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $file = id(new PhabricatorFileQuery()) ->setViewer($user) ->withPHIDs(array($this->phid)) ->executeOne(); if (!$file) { return new Aphront404Response(); } $this->loadHandles(array($file->getAuthorPHID())); $phid = $file->getPHID(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addCrumb( id(new PhabricatorCrumbView()) ->setName('F'.$file->getID()) ->setHref($this->getApplicationURI("/info/{$phid}/"))); $header = id(new PhabricatorHeaderView()) ->setObjectName('F'.$file->getID()) ->setHeader($file->getName()); $actions = $this->buildActionView($file); $properties = $this->buildPropertyView($file); return $this->buildApplicationPage( array( $crumbs, $header, $actions, $properties, ), array( 'title' => $file->getName(), 'device' => true, )); } private function buildActionView(PhabricatorFile $file) { $request = $this->getRequest(); $user = $request->getUser(); $id = $file->getID(); $view = id(new PhabricatorActionListView()) ->setUser($user) ->setObject($file); if ($file->isViewableInBrowser()) { $view->addAction( id(new PhabricatorActionView()) ->setName(pht('View File')) ->setIcon('preview') ->setHref($file->getViewURI())); } else { $view->addAction( id(new PhabricatorActionView()) ->setUser($user) ->setRenderAsForm(true) + ->setDownload(true) ->setName(pht('Download File')) ->setIcon('download') ->setHref($file->getViewURI())); } $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Delete File')) ->setIcon('delete') ->setHref($this->getApplicationURI("/delete/{$id}/")) ->setWorkflow(true)); return $view; } private function buildPropertyView(PhabricatorFile $file) { $request = $this->getRequest(); $user = $request->getUser(); $view = id(new PhabricatorPropertyListView()); if ($file->getAuthorPHID()) { $view->addProperty( pht('Author'), $this->getHandle($file->getAuthorPHID())->renderLink()); } $view->addProperty( pht('Created'), phabricator_datetime($file->getDateCreated(), $user)); $view->addProperty( pht('Size'), phabricator_format_bytes($file->getByteSize())); $view->addSectionHeader(pht('Technical Details')); $view->addProperty( pht('Mime Type'), phutil_escape_html($file->getMimeType())); $view->addProperty( pht('Engine'), phutil_escape_html($file->getStorageEngine())); $view->addProperty( pht('Format'), phutil_escape_html($file->getStorageFormat())); $view->addProperty( pht('Handle'), phutil_escape_html($file->getStorageHandle())); $metadata = $file->getMetadata(); if (!empty($metadata)) { $view->addSectionHeader(pht('Metadata')); foreach ($metadata as $key => $value) { $view->addProperty( PhabricatorFile::getMetadataName($key), phutil_escape_html($value)); } } if ($file->isViewableImage()) { // TODO: Clean this up after Pholio (dark backgrounds, standardization, // etc.) $image = phutil_tag( 'img', array( 'src' => $file->getViewURI(), 'class' => 'phabricator-property-list-image', )); $linked_image = phutil_tag( 'a', array( 'href' => $file->getViewURI(), ), $image); $view->addTextContent($linked_image); } return $view; } } diff --git a/src/view/AphrontTagView.php b/src/view/AphrontTagView.php index 5b276e11f..0b3f17a09 100644 --- a/src/view/AphrontTagView.php +++ b/src/view/AphrontTagView.php @@ -1,158 +1,158 @@ workflow = $workflow; return $this; } public function getWorkflow() { return $this->workflow; } public function setMustCapture($must_capture) { $this->mustCapture = $must_capture; return $this; } public function getMustCapture() { return $this->mustCapture; } final public function setMetadata(array $metadata) { $this->metadata = $metadata; return $this; } final public function getMetadata() { return $this->metadata; } final public function setStyle($style) { $this->style = $style; return $this; } final public function getStyle() { return $this->style; } final public function addSigil($sigil) { $this->sigils[] = $sigil; return $this; } - final public function getSigil() { - return $this->sigil; + final public function getSigils() { + return $this->sigils; } public function addClass($class) { $this->classes[] = $class; return $this; } public function getClass() { return $this->class; } public function setID($id) { $this->id = $id; return $this; } public function getID() { return $this->id; } protected function getTagName() { return 'div'; } protected function getTagAttributes() { return array(); } protected function getTagContent() { return $this->renderChildren(); } protected function willRender() { return; } final public function render() { $this->willRender(); $attributes = $this->getTagAttributes(); $implode = array('class', 'sigil'); foreach ($implode as $attr) { if (isset($attributes[$attr])) { if (is_array($attributes[$attr])) { $attributes[$attr] = implode(' ', $attributes[$attr]); } } } if (!is_array($attributes)) { $class = get_class($this); throw new Exception( "View '{$class}' did not return an array from getTagAttributes()!"); } $sigils = $this->sigils; if ($this->workflow) { $sigils[] = 'workflow'; } $tag_view_attributes = array( 'id' => $this->id, 'class' => $this->classes ? implode(' ', $this->classes) : null, 'style' => $this->style, 'meta' => $this->metadata, 'sigil' => $sigils ? implode(' ', $sigils) : null, 'mustcapture' => $this->mustCapture, ); foreach ($tag_view_attributes as $key => $value) { if ($value === null) { continue; } if (!isset($attributes[$key])) { $attributes[$key] = $value; continue; } switch ($key) { case 'class': case 'sigil': $attributes[$key] = $attributes[$key].' '.$value; break; default: // Use the explicitly set value rather than the tag default value. $attributes[$key] = $value; break; } } return javelin_render_tag( $this->getTagName(), $attributes, $this->getTagContent()); } } diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php index 1dca03e2c..8b0041f00 100644 --- a/src/view/form/AphrontFormView.php +++ b/src/view/form/AphrontFormView.php @@ -1,114 +1,112 @@ flexible = $flexible; return $this; } public function setID($id) { $this->id = $id; return $this; } public function setAction($action) { $this->action = $action; return $this; } public function setMethod($method) { $this->method = $method; return $this; } public function setEncType($enc_type) { $this->encType = $enc_type; return $this; } public function addHiddenInput($key, $value) { $this->data[$key] = $value; return $this; } public function setWorkflow($workflow) { $this->workflow = $workflow; return $this; } public function addSigil($sigil) { $this->sigils[] = $sigil; return $this; } public function render() { if ($this->flexible) { require_celerity_resource('phabricator-form-view-css'); } require_celerity_resource('aphront-form-view-css'); - Javelin::initBehavior('aphront-form-disable-on-submit'); - $layout = new AphrontFormLayoutView(); if (!$this->flexible) { $layout ->setBackgroundShading(true) ->setPadded(true); } $layout ->appendChild($this->renderDataInputs()) ->appendChild($this->renderChildren()); if (!$this->user) { throw new Exception('You must pass the user to AphrontFormView.'); } $sigils = $this->sigils; if ($this->workflow) { $sigils[] = 'workflow'; } return phabricator_render_form( $this->user, array( 'class' => $this->flexible ? 'phabricator-form-view' : null, 'action' => $this->action, 'method' => $this->method, 'enctype' => $this->encType, 'sigil' => $sigils ? implode(' ', $sigils) : null, 'id' => $this->id, ), $layout->render()); } private function renderDataInputs() { $inputs = array(); foreach ($this->data as $key => $value) { if ($value === null) { continue; } $inputs[] = phutil_tag( 'input', array( 'type' => 'hidden', 'name' => $key, 'value' => $value, )); } return implode("\n", $inputs); } } diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php index 53c2c1e94..bb030b60c 100644 --- a/src/view/layout/PhabricatorActionView.php +++ b/src/view/layout/PhabricatorActionView.php @@ -1,138 +1,156 @@ download = $download; + return $this; + } + + public function getDownload() { + return $this->download; + } public function setHref($href) { $this->href = $href; return $this; } public function setIcon($icon) { $this->icon = $icon; return $this; } public function setName($name) { $this->name = $name; return $this; } public function setDisabled($disabled) { $this->disabled = $disabled; return $this; } public function setWorkflow($workflow) { $this->workflow = $workflow; return $this; } public function setRenderAsForm($form) { $this->renderAsForm = $form; return $this; } public function render() { $icon = null; if ($this->icon) { $suffix = ''; if ($this->disabled) { $suffix = '-grey'; } require_celerity_resource('sprite-icon-css'); $icon = phutil_tag( 'span', array( 'class' => 'phabricator-action-view-icon sprite-icon '. 'action-'.$this->icon.$suffix, ), ''); } if ($this->href) { if ($this->renderAsForm) { if (!$this->user) { throw new Exception( 'Call setUser() when rendering an action as a form.'); } $item = javelin_tag( 'button', array( 'class' => 'phabricator-action-view-item', ), $this->name); + $sigils = array(); + if ($this->workflow) { + $sigils[] = 'workflow'; + } + if ($this->download) { + $sigils[] = 'download'; + } + $item = phabricator_render_form( $this->user, array( 'action' => $this->href, 'method' => 'POST', - 'sigil' => $this->workflow ? 'workflow' : null, + 'sigil' => implode(' ', $sigils), ), $item); } else { $item = javelin_tag( 'a', array( 'href' => $this->href, 'class' => 'phabricator-action-view-item', 'sigil' => $this->workflow ? 'workflow' : null, ), $this->name); } } else { $item = phutil_tag( 'span', array( 'class' => 'phabricator-action-view-item', ), $this->name); } $classes = array(); $classes[] = 'phabricator-action-view'; if ($this->disabled) { $classes[] = 'phabricator-action-view-disabled'; } return phutil_tag( 'li', array( 'class' => implode(' ', $classes), ), array($icon, $item)); } public static function getAvailableIcons() { $root = dirname(phutil_get_library_root('phabricator')); $path = $root.'/resources/sprite/manifest/icon.json'; $data = Filesystem::readFile($path); $manifest = json_decode($data, true); $results = array(); $prefix = 'action-'; foreach ($manifest['sprites'] as $sprite) { $name = $sprite['name']; if (preg_match('/-(white|grey)$/', $name)) { continue; } if (!strncmp($name, $prefix, strlen($prefix))) { $results[] = substr($name, strlen($prefix)); } } return $results; } } diff --git a/src/view/page/PhabricatorStandardPageView.php b/src/view/page/PhabricatorStandardPageView.php index b1ce431b1..26ae9f3c0 100644 --- a/src/view/page/PhabricatorStandardPageView.php +++ b/src/view/page/PhabricatorStandardPageView.php @@ -1,362 +1,381 @@ applicationMenu = $application_menu; return $this; } public function getApplicationMenu() { return $this->applicationMenu; } public function setApplicationName($application_name) { $this->applicationName = $application_name; return $this; } public function setDisableConsole($disable) { $this->disableConsole = $disable; return $this; } public function getApplicationName() { return $this->applicationName; } public function setBaseURI($base_uri) { $this->baseURI = $base_uri; return $this; } public function getBaseURI() { return $this->baseURI; } public function setShowChrome($show_chrome) { $this->showChrome = $show_chrome; return $this; } public function getShowChrome() { return $this->showChrome; } public function setSearchDefaultScope($search_default_scope) { $this->searchDefaultScope = $search_default_scope; return $this; } public function getSearchDefaultScope() { return $this->searchDefaultScope; } public function appendPageObjects(array $objs) { foreach ($objs as $obj) { $this->pageObjects[] = $obj; } } public function getTitle() { $use_glyph = true; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); if ($user && $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_TITLES) !== 'glyph') { $use_glyph = false; } } return ($use_glyph ? $this->getGlyph() : '['.$this->getApplicationName().']'). ' '.parent::getTitle(); } protected function willRenderPage() { parent::willRenderPage(); if (!$this->getRequest()) { throw new Exception( "You must set the Request to render a PhabricatorStandardPageView."); } $console = $this->getConsole(); require_celerity_resource('phabricator-core-css'); require_celerity_resource('phabricator-zindex-css'); require_celerity_resource('phabricator-core-buttons-css'); require_celerity_resource('sprite-gradient-css'); require_celerity_resource('phabricator-standard-page-view'); Javelin::initBehavior('workflow', array()); - $current_token = null; $request = $this->getRequest(); + $user = null; if ($request) { $user = $request->getUser(); - if ($user) { - $current_token = $user->getCSRFToken(); - $download_form = phabricator_render_form_magic($user); - $default_img_uri = - PhabricatorEnv::getCDNURI( - '/rsrc/image/icon/fatcow/document_black.png' - ); - - Javelin::initBehavior( - 'lightbox-attachments', - array( - 'defaultImageUri' => $default_img_uri, - 'downloadForm' => $download_form, - )); - } } + if ($user) { + $default_img_uri = + PhabricatorEnv::getCDNURI( + '/rsrc/image/icon/fatcow/document_black.png' + ); + $download_form = phabricator_render_form( + $user, + array( + 'action' => '#', + 'method' => 'POST', + 'class' => 'lightbox-download-form', + 'sigil' => 'download', + ), + phutil_tag( + 'button', + array(), + pht('Download'))); + + Javelin::initBehavior( + 'lightbox-attachments', + array( + 'defaultImageUri' => $default_img_uri, + 'downloadForm' => $download_form, + )); + } + + Javelin::initBehavior('aphront-form-disable-on-submit'); Javelin::initBehavior('toggle-class', array()); Javelin::initBehavior('konami', array()); + + $current_token = null; + if ($user) { + $current_token = $user->getCSRFToken(); + } + Javelin::initBehavior( 'refresh-csrf', array( 'tokenName' => AphrontRequest::getCSRFTokenName(), 'header' => AphrontRequest::getCSRFHeaderName(), 'current' => $current_token, )); + Javelin::initBehavior('device'); if ($console) { require_celerity_resource('aphront-dark-console-css'); Javelin::initBehavior( 'dark-console', array( 'uri' => '/~/', 'request_uri' => $request ? (string) $request->getRequestURI() : '/', )); // Change this to initBehavior when there is some behavior to initialize require_celerity_resource('javelin-behavior-error-log'); } $menu = id(new PhabricatorMainMenuView()) ->setUser($request->getUser()) ->setDefaultSearchScope($this->getSearchDefaultScope()); if ($this->getController()) { $menu->setController($this->getController()); } if ($this->getApplicationMenu()) { $menu->setApplicationMenu($this->getApplicationMenu()); } $this->menuContent = $menu->render(); } protected function getHead() { $monospaced = PhabricatorEnv::getEnvConfig('style.monospace'); $request = $this->getRequest(); if ($request) { $user = $request->getUser(); if ($user) { $monospaced = nonempty( $user->loadPreferences()->getPreference( PhabricatorUserPreferences::PREFERENCE_MONOSPACED), $monospaced); } } $response = CelerityAPI::getStaticResourceResponse(); $head = array( parent::getHead(), '', $response->renderSingleResource('javelin-magical-init'), ); return implode("\n", $head); } public function setGlyph($glyph) { $this->glyph = $glyph; return $this; } public function getGlyph() { return $this->glyph; } protected function willSendResponse($response) { $response = parent::willSendResponse($response); $console = $this->getRequest()->getApplicationConfiguration()->getConsole(); if ($console) { $response = str_replace( '', $console->render($this->getRequest()), $response); } return $response; } protected function getBody() { $console = $this->getConsole(); $user = null; $request = $this->getRequest(); if ($request) { $user = $request->getUser(); } $header_chrome = null; if ($this->getShowChrome()) { $header_chrome = $this->menuContent; } $developer_warning = null; if (PhabricatorEnv::getEnvConfig('phabricator.show-error-callout') && DarkConsoleErrorLogPluginAPI::getErrors()) { $developer_warning = '
'. pht( 'This page raised PHP errors. Find them in DarkConsole '. 'or the error log.'). '
'; } // Render the "you have unresolved setup issues..." warning. $setup_warning = null; if ($user && $user->getIsAdmin()) { $open = PhabricatorSetupCheck::getOpenSetupIssueCount(); if ($open) { $setup_warning = phutil_tag( 'div', array( 'class' => 'setup-warning-callout', ), phutil_tag( 'a', array( 'href' => '/config/issue/', ), pht('You have %d unresolved setup issue(s)...', $open))); } } return phutil_render_tag( 'div', array( 'id' => 'base-page', 'class' => 'phabricator-standard-page', ), $developer_warning. $setup_warning. $header_chrome. '
'. ($console ? '' : null). parent::getBody(). '
'. '
'); } protected function getTail() { $request = $this->getRequest(); $user = $request->getUser(); $container = null; if ($user->isLoggedIn()) { $aphlict_object_id = celerity_generate_unique_node_id(); $aphlict_container_id = celerity_generate_unique_node_id(); $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); $client_uri = new PhutilURI($client_uri); if ($client_uri->getDomain() == 'localhost') { $this_host = $this->getRequest()->getHost(); $this_host = new PhutilURI('http://'.$this_host.'/'); $client_uri->setDomain($this_host->getDomain()); } $enable_debug = PhabricatorEnv::getEnvConfig('notification.debug'); Javelin::initBehavior( 'aphlict-listen', array( 'id' => $aphlict_object_id, 'containerID' => $aphlict_container_id, 'server' => $client_uri->getDomain(), 'port' => $client_uri->getPort(), 'debug' => $enable_debug, 'pageObjects' => array_fill_keys($this->pageObjects, true), )); $container = phutil_tag( 'div', array( 'id' => $aphlict_container_id, 'style' => 'position: absolute; width: 0; height: 0;', ), ''); } $response = CelerityAPI::getStaticResourceResponse(); $tail = array( parent::getTail(), $container, $response->renderHTMLFooter(), ); return implode("\n", $tail); } protected function getBodyClasses() { $classes = array(); if (!$this->getShowChrome()) { $classes[] = 'phabricator-chromeless-page'; } $agent = idx($_SERVER, 'HTTP_USER_AGENT'); // Try to guess the device resolution based on UA strings to avoid a flash // of incorrectly-styled content. $device_guess = 'device-desktop'; if (preg_match('@iPhone|iPod|(Android.*Chrome/[.0-9]* Mobile)@', $agent)) { $device_guess = 'device-phone device'; } else if (preg_match('@iPad|(Android.*Chrome/)@', $agent)) { $device_guess = 'device-tablet device'; } $classes[] = $device_guess; return implode(' ', $classes); } private function getConsole() { if ($this->disableConsole) { return null; } return $this->getRequest()->getApplicationConfiguration()->getConsole(); } } diff --git a/webroot/rsrc/js/application/core/behavior-form.js b/webroot/rsrc/js/application/core/behavior-form.js index 25ebb59e7..9e4942d53 100644 --- a/webroot/rsrc/js/application/core/behavior-form.js +++ b/webroot/rsrc/js/application/core/behavior-form.js @@ -1,63 +1,69 @@ /** * @requires javelin-behavior javelin-stratcom javelin-dom * @provides javelin-behavior-aphront-form-disable-on-submit */ JX.behavior('aphront-form-disable-on-submit', function(config) { var restore = []; var root = null; var new_tab = false; JX.Stratcom.listen('click', 'tag:button', function(e) { var raw = e.getRawEvent(); new_tab = (raw.altKey || raw.ctrlKey || raw.metaKey || raw.shiftKey); }); JX.Stratcom.listen('submit', 'tag:form', function(e) { if (e.getNode('workflow')) { // Don't activate for forms with workflow, the workflow behavior will // handle it. return; } root = e.getNode('tag:form'); + // If the form is a "download" form, don't disable it on submit because + // we won't transition off the page. + if (JX.Stratcom.hasSigil(root, 'download')) { + return; + } + // Open the form to a new tab in Firefox explicitly (automatic in Chrome). // We render some buttons as links so users may be confused that the links // don't open to new tabs with Ctrl+click as normal links. root.target = (new_tab ? '_blank' : ''); if (new_tab) { return; } if (root._disabled) { e.kill(); } root._disabled = true; var buttons = JX.DOM.scry(root, 'button'); for (var ii = 0; ii < buttons.length; ii++) { if (!buttons[ii].disabled) { buttons[ii].disabled = 'disabled'; JX.DOM.alterClass(buttons[ii], 'disabled', true); restore.push(buttons[ii]); } } }); JX.Stratcom.listen('unload', null, function(e) { // Reenable everything on page unload so we don't bfcache back to a page // that has disabled forms. for (var ii = 0; ii < restore.length; ii++) { restore[ii].disabled = ''; JX.DOM.alterClass(restore[ii], 'disabled', false); root._disabled = false; } if (root) { delete root._disabled; } restore = []; root = null; }); }); diff --git a/webroot/rsrc/js/application/core/behavior-lightbox-attachments.js b/webroot/rsrc/js/application/core/behavior-lightbox-attachments.js index f299a6f9d..55d6bb4d2 100644 --- a/webroot/rsrc/js/application/core/behavior-lightbox-attachments.js +++ b/webroot/rsrc/js/application/core/behavior-lightbox-attachments.js @@ -1,265 +1,247 @@ /** * @provides javelin-behavior-lightbox-attachments * @requires javelin-behavior * javelin-stratcom * javelin-dom * javelin-mask * javelin-util * phabricator-busy */ JX.behavior('lightbox-attachments', function (config) { var lightbox = null; var prev = null; var next = null; var x_margin = 40; var y_margin = 100; - var downloadForm = JX.$H(config.downloadForm); + var downloadForm = JX.$H(config.downloadForm).getFragment().firstChild; function loadLightBox(e) { if (!e.isNormalClick()) { return; } if (JX.Stratcom.pass()) { return; } e.prevent(); var links = JX.DOM.scry(document, 'a', 'lightboxable'); var phids = {}; var data; for (var i = 0; i < links.length; i++) { data = JX.Stratcom.getData(links[i]); phids[data.phid] = links[i]; } // Now that we have the big picture phid situation sorted out, figure // out how the actual node the user clicks fits into that big picture // and build some pretty UI to show the attachment. var target = e.getNode('lightboxable'); var target_data = JX.Stratcom.getData(target); var total = JX.keys(phids).length; var current = 1; var past_target = false; for (var phid in phids) { if (past_target) { next = phids[phid]; break; } else if (phid == target_data.phid) { past_target = true; } else { prev = phids[phid]; current++; } } var img_uri = ''; var extra_status = ''; var name_element = ''; // for now, this conditional is always true // revisit if / when we decide to add non-images to lightbox view if (target_data.viewable) { img_uri = target_data.uri; } else { img_uri = config.defaultImageUri; extra_status = ' Image may not be representative of actual attachment.'; name_element = JX.$N('div', { className : 'attachment-name' }, target_data.name ); } var img = JX.$N('img', { className : 'loading', alt : target_data.name, title : target_data.name } ); lightbox = JX.$N('div', { className : 'lightbox-attachment', sigil: 'lightbox-attachment' }, img ); JX.DOM.appendContent(lightbox, name_element); var closeIcon = JX.$N('a', { className : 'lightbox-close', href : '#' } ); JX.DOM.listen(closeIcon, 'click', null, closeLightBox); JX.DOM.appendContent(lightbox, closeIcon); var leftIcon = ''; if (next) { leftIcon = JX.$N('a', { className : 'lightbox-right', href : '#' } ); JX.DOM.listen(leftIcon, 'click', null, JX.bind(null, loadAnotherLightBox, next) ); } JX.DOM.appendContent(lightbox, leftIcon); var rightIcon = ''; if (prev) { rightIcon = JX.$N('a', { className : 'lightbox-left', href : '#' } ); JX.DOM.listen(rightIcon, 'click', null, JX.bind(null, loadAnotherLightBox, prev) ); } JX.DOM.appendContent(lightbox, rightIcon); var statusSpan = JX.$N('span', { className: 'lightbox-status-txt' }, 'Image '+current+' of '+total+'.'+extra_status ); - var form = JX.$N('form', - { - action : target_data.dUri, - method : 'POST', - className : 'lightbox-download-form' - }, - downloadForm - ); - JX.DOM.appendContent(form, JX.$N('button', {}, 'Download')); - JX.DOM.listen(form, - 'click', - null, - function (e) { - e.prevent(); - form.submit(); - // Firefox and probably IE need this trick to work. - // Removing a form from the DOM while its submitting is - // tricky business. - setTimeout(JX.bind(null, closeLightBox, e), 0); - } - ); + var downloadSpan = JX.$N('span', { className : 'lightbox-download' - }, - form - ); + }); var statusHTML = JX.$N('div', { className : 'lightbox-status' }, [statusSpan, downloadSpan] ); JX.DOM.appendContent(lightbox, statusHTML); JX.DOM.alterClass(document.body, 'lightbox-attached', true); JX.Mask.show('jx-dark-mask'); + + downloadForm.action = target_data.dUri; + downloadSpan.appendChild(downloadForm); + document.body.appendChild(lightbox); JX.Busy.start(); img.onload = function() { JX.DOM.alterClass(img, 'loading', false); JX.Busy.done(); } img.src = img_uri; } // TODO - make this work with KeyboardShortcut, which means // making an uninstall / de-register for KeyboardShortcut function lightBoxHandleKeyDown(e) { if (!lightbox) { return; } var raw = e.getRawEvent(); if (raw.altKey || raw.ctrlKey || raw.metaKey) { return; } if (JX.Stratcom.pass()) { return; } var handler = JX.bag; switch (e.getSpecialKey()) { case 'esc': handler = closeLightBox; break; case 'right': if (next) { handler = JX.bind(null, loadAnotherLightBox, next); } break; case 'left': if (prev) { handler = JX.bind(null, loadAnotherLightBox, prev); } break; } return handler(e); } function closeLightBox(e) { if (!lightbox) { return; } e.prevent(); JX.DOM.remove(lightbox); JX.Mask.hide(); JX.DOM.alterClass(document.body, 'lightbox-attached', false); lightbox = null; prev = null; next = null; } function loadAnotherLightBox(el, e) { if (!el) { return; } e.prevent(); closeLightBox(e); el.click(); } JX.Stratcom.listen( 'click', ['lightboxable', 'tag:a'], loadLightBox); JX.Stratcom.listen( 'keydown', null, lightBoxHandleKeyDown); // When the user clicks the background, close the lightbox. JX.Stratcom.listen( 'click', 'lightbox-attachment', function (e) { if (!lightbox) { return; } if (e.getTarget() != e.getNode('lightbox-attachment')) { // Don't close if they clicked some other element, like the image // itself or the next/previous arrows. return; } closeLightBox(e); e.kill(); }); });