mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Bring in JX.Workflow and the inline commenting behavior, plus sync Javelin.
This commit is contained in:
parent
4faad5b3f1
commit
9dac0ed9f1
26 changed files with 934 additions and 246 deletions
|
@ -1,3 +1,19 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Placeholder so I don't forget about this, hopefully.
|
|
@ -9,7 +9,7 @@
|
|||
celerity_register_resource_map(array(
|
||||
'aphront-dialog-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/771b987d/rsrc/css/aphront/dialog-view.css',
|
||||
'uri' => '/res/d98e6292/rsrc/css/aphront/dialog-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -188,6 +188,15 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/css/core/core.css',
|
||||
),
|
||||
'phabricator-core-dialog-css' =>
|
||||
array(
|
||||
'uri' => '/res/d9580553/rsrc/css/core/dialog.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/core/dialog.css',
|
||||
),
|
||||
'phabricator-remarkup-css' =>
|
||||
array(
|
||||
'uri' => '/res/786989c3/rsrc/css/core/remarkup.css',
|
||||
|
@ -226,6 +235,16 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/differential/behavior-comment-preview.js',
|
||||
),
|
||||
'javelin-behavior-differential-edit-inline-comments' =>
|
||||
array(
|
||||
'uri' => '/res/51d7da98/rsrc/js/application/differential/behavior-edit-inline-comments.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-lib-dev',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/differential/behavior-edit-inline-comments.js',
|
||||
),
|
||||
'javelin-behavior-differential-populate' =>
|
||||
array(
|
||||
'uri' => '/res/f7efbf62/rsrc/js/application/differential/behavior-populate.js',
|
||||
|
@ -246,9 +265,9 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/differential/behavior-show-more.js',
|
||||
),
|
||||
'javelin-init-dev' =>
|
||||
'javelin-magical-init' =>
|
||||
array(
|
||||
'uri' => '/res/c57a9e89/rsrc/js/javelin/init.dev.js',
|
||||
'uri' => '/res/76614f84/rsrc/js/javelin/init.dev.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -257,7 +276,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-init-prod' =>
|
||||
array(
|
||||
'uri' => '/res/f0172c54/rsrc/js/javelin/init.min.js',
|
||||
'uri' => '/res/ce6bff38/rsrc/js/javelin/init.min.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -266,7 +285,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-lib-dev' =>
|
||||
array(
|
||||
'uri' => '/res/3e747182/rsrc/js/javelin/javelin.dev.js',
|
||||
'uri' => '/res/29c9b6b4/rsrc/js/javelin/javelin.dev.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -275,7 +294,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-lib-prod' =>
|
||||
array(
|
||||
'uri' => '/res/9438670e/rsrc/js/javelin/javelin.min.js',
|
||||
'uri' => '/res/ef13c830/rsrc/js/javelin/javelin.min.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -284,7 +303,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-typeahead-dev' =>
|
||||
array(
|
||||
'uri' => '/res/c81c0f01/rsrc/js/javelin/typeahead.dev.js',
|
||||
'uri' => '/res/6de6ae59/rsrc/js/javelin/typeahead.dev.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -293,17 +312,35 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-typeahead-prod' =>
|
||||
array(
|
||||
'uri' => '/res/1da2d984/rsrc/js/javelin/typeahead.min.js',
|
||||
'uri' => '/res/593d9bb8/rsrc/js/javelin/typeahead.min.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/js/javelin/typeahead.min.js',
|
||||
),
|
||||
'javelin-workflow-dev' =>
|
||||
array(
|
||||
'uri' => '/res/2d740661/rsrc/js/javelin/workflow.dev.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/js/javelin/workflow.dev.js',
|
||||
),
|
||||
'javelin-workflow-prod' =>
|
||||
array(
|
||||
'uri' => '/res/b758e0a0/rsrc/js/javelin/workflow.min.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/js/javelin/workflow.min.js',
|
||||
),
|
||||
), array (
|
||||
'packages' =>
|
||||
array (
|
||||
'd348c79d' =>
|
||||
'dc2390af' =>
|
||||
array (
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -320,7 +357,7 @@ celerity_register_resource_map(array(
|
|||
9 => 'aphront-typeahead-control-css',
|
||||
10 => 'phabricator-directory-css',
|
||||
),
|
||||
'uri' => '/res/pkg/d348c79d/core.pkg.css',
|
||||
'uri' => '/res/pkg/dc2390af/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'69b11588' =>
|
||||
|
@ -340,17 +377,17 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'reverse' =>
|
||||
array (
|
||||
'phabricator-core-css' => 'd348c79d',
|
||||
'phabricator-core-buttons-css' => 'd348c79d',
|
||||
'phabricator-standard-page-view' => 'd348c79d',
|
||||
'aphront-dialog-view-css' => 'd348c79d',
|
||||
'aphront-form-view-css' => 'd348c79d',
|
||||
'aphront-panel-view-css' => 'd348c79d',
|
||||
'aphront-side-nav-view-css' => 'd348c79d',
|
||||
'aphront-table-view-css' => 'd348c79d',
|
||||
'aphront-tokenizer-control-css' => 'd348c79d',
|
||||
'aphront-typeahead-control-css' => 'd348c79d',
|
||||
'phabricator-directory-css' => 'd348c79d',
|
||||
'phabricator-core-css' => 'dc2390af',
|
||||
'phabricator-core-buttons-css' => 'dc2390af',
|
||||
'phabricator-standard-page-view' => 'dc2390af',
|
||||
'aphront-dialog-view-css' => 'dc2390af',
|
||||
'aphront-form-view-css' => 'dc2390af',
|
||||
'aphront-panel-view-css' => 'dc2390af',
|
||||
'aphront-side-nav-view-css' => 'dc2390af',
|
||||
'aphront-table-view-css' => 'dc2390af',
|
||||
'aphront-tokenizer-control-css' => 'dc2390af',
|
||||
'aphront-typeahead-control-css' => 'dc2390af',
|
||||
'phabricator-directory-css' => 'dc2390af',
|
||||
'differential-core-view-css' => '69b11588',
|
||||
'differential-changeset-view-css' => '69b11588',
|
||||
'differential-revision-detail-css' => '69b11588',
|
||||
|
|
|
@ -90,6 +90,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffTableOfContentsView' => 'applications/differential/view/difftableofcontents',
|
||||
'DifferentialDiffViewController' => 'applications/differential/controller/diffview',
|
||||
'DifferentialHunk' => 'applications/differential/storage/hunk',
|
||||
'DifferentialInlineComment' => 'applications/differential/storage/inlinecomment',
|
||||
'DifferentialLintStatus' => 'applications/differential/constants/lintstatus',
|
||||
'DifferentialMail' => 'applications/differential/mail/base',
|
||||
'DifferentialMarkupEngineFactory' => 'applications/differential/parser/markup',
|
||||
|
@ -264,6 +265,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffTableOfContentsView' => 'AphrontView',
|
||||
'DifferentialDiffViewController' => 'DifferentialController',
|
||||
'DifferentialHunk' => 'DifferentialDAO',
|
||||
'DifferentialInlineComment' => 'DifferentialDAO',
|
||||
'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail',
|
||||
'DifferentialReviewRequestMail' => 'DifferentialMail',
|
||||
'DifferentialRevision' => 'DifferentialDAO',
|
||||
|
|
|
@ -79,6 +79,8 @@ class DifferentialRevisionViewController extends DifferentialController {
|
|||
|
||||
$changeset_view = new DifferentialChangesetListView();
|
||||
$changeset_view->setChangesets($changesets);
|
||||
$changeset_view->setEditable(true);
|
||||
$changeset_view->setRevision($revision);
|
||||
|
||||
$comment_form = new DifferentialAddCommentView();
|
||||
$comment_form->setRevision($revision);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class DifferentialInlineComment extends DifferentialDAO {
|
||||
|
||||
protected $revisionID;
|
||||
protected $commentID;
|
||||
protected $authorPHID;
|
||||
|
||||
protected $changesetID;
|
||||
protected $isNewFile;
|
||||
|
||||
protected $lineNumber;
|
||||
protected $lineLength;
|
||||
|
||||
protected $content;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/differential/storage/base');
|
||||
|
||||
|
||||
phutil_require_source('DifferentialInlineComment.php');
|
|
@ -19,12 +19,24 @@
|
|||
class DifferentialChangesetListView extends AphrontView {
|
||||
|
||||
private $changesets = array();
|
||||
private $editable;
|
||||
private $revision;
|
||||
|
||||
public function setChangesets($changesets) {
|
||||
$this->changesets = $changesets;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEditable($editable) {
|
||||
$this->editable = $editable;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRevision(DifferentialRevision $revision) {
|
||||
$this->revision = $revision;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
require_celerity_resource('differential-changeset-view-css');
|
||||
|
||||
|
@ -105,20 +117,14 @@ class DifferentialChangesetListView extends AphrontView {
|
|||
Javelin::initBehavior('differential-show-more', array(
|
||||
'uri' => '/differential/changeset/',
|
||||
));
|
||||
/*
|
||||
|
||||
|
||||
Javelin::initBehavior('differential-context', array(
|
||||
'uri' => $render_uri,
|
||||
));
|
||||
|
||||
if ($edit) {
|
||||
require_static('remarkup-css');
|
||||
Javelin::initBehavior('differential-inline', array(
|
||||
'uri' => '/differential/feedback/'.$revision->getID().'/',
|
||||
if ($this->editable) {
|
||||
$revision = $this->revision;
|
||||
Javelin::initBehavior('differential-edit-inline-comments', array(
|
||||
'uri' => '/differential/inline/edit/'.$revision->getID().'/',
|
||||
));
|
||||
}
|
||||
*/
|
||||
|
||||
return
|
||||
'<div class="differential-review-stage">'.
|
||||
implode("\n", $output).
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/metamta/adapter/phpmailerlite');
|
||||
phutil_require_module('phabricator', 'applications/metamta/storage/base');
|
||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||
phutil_require_module('phabricator', 'infrastructure/env');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
|
|
@ -24,31 +24,24 @@ function javelin_render_tag(
|
|||
if (isset($attributes['sigil']) ||
|
||||
isset($attributes['meta']) ||
|
||||
isset($attributes['mustcapture'])) {
|
||||
$classes = array();
|
||||
foreach ($attributes as $k => $v) {
|
||||
switch ($k) {
|
||||
case 'sigil':
|
||||
$classes[] = 'FN_'.$v;
|
||||
$attributes['data-sigil'] = $v;
|
||||
unset($attributes[$k]);
|
||||
break;
|
||||
case 'meta':
|
||||
$response = CelerityAPI::getStaticResourceResponse();
|
||||
$id = $response->addMetadata($v);
|
||||
$classes[] = 'FD_'.$id;
|
||||
$attributes['data-meta'] = $id;
|
||||
unset($attributes[$k]);
|
||||
break;
|
||||
case 'mustcapture':
|
||||
$classes[] = 'FI_CAPTURE';
|
||||
$attributes['data-mustcapture'] = '1';
|
||||
unset($attributes[$k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($attributes['class'])) {
|
||||
$classes[] = $attributes['class'];
|
||||
}
|
||||
|
||||
$attributes['class'] = implode(' ', $classes);
|
||||
}
|
||||
|
||||
return phutil_render_tag($tag, $attributes, $content);
|
||||
|
|
|
@ -68,12 +68,13 @@ class AphrontDialogView extends AphrontView {
|
|||
'Cancel');
|
||||
}
|
||||
|
||||
return phutil_render_tag(
|
||||
return javelin_render_tag(
|
||||
'form',
|
||||
array(
|
||||
'class' => 'aphront-dialog-view',
|
||||
'action' => $this->submitURI,
|
||||
'method' => 'post',
|
||||
'sigil' => 'jx-dialog',
|
||||
),
|
||||
'<input type="hidden" name="__form__" value="1" />'.
|
||||
'<div class="aphront-dialog-head">'.
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
|
|
@ -70,6 +70,7 @@ class PhabricatorStandardPageView extends AphrontPageView {
|
|||
require_celerity_resource('phabricator-standard-page-view');
|
||||
|
||||
require_celerity_resource('javelin-lib-dev');
|
||||
require_celerity_resource('javelin-workflow-dev');
|
||||
|
||||
$this->bodyContent = $this->renderChildren();
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
* @provides aphront-dialog-view-css
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.aphront-dialog-view {
|
||||
width: 480px;
|
||||
padding: 8px;
|
||||
|
@ -40,3 +38,23 @@
|
|||
margin-left: .5em;
|
||||
}
|
||||
|
||||
.jx-client-dialog {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.jx-mask {
|
||||
opacity: .5;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=75)";
|
||||
filter: alpha(opacity=75);
|
||||
background: #999;
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
|
|
@ -119,3 +119,14 @@
|
|||
margin: 0.5em 0;
|
||||
padding: 10px 0px 20px;
|
||||
}
|
||||
|
||||
.differential-reticle {
|
||||
background: #ffeeaa;
|
||||
border: 1px solid #ffcc00;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
opacity: 0.5;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
|
|
55
webroot/rsrc/css/core/dialog.css
Normal file
55
webroot/rsrc/css/core/dialog.css
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @provides phabricator-core-dialog-css
|
||||
*/
|
||||
|
||||
.jx-dialog {
|
||||
display: block;
|
||||
width: 480px;
|
||||
padding: 8px;
|
||||
background: #666;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.jx-client-dialog {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.jx-dialog .dialog-title {
|
||||
background: #6d84b4;
|
||||
border: none;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
padding: 5px 12px 6px;
|
||||
color: #ffffff;
|
||||
}
|
||||
.jx-dialog .dialog-body {
|
||||
background: #ffffff;
|
||||
padding: 16px 12px;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.jx-dialog .dialog-foot {
|
||||
border: none;
|
||||
background: #ededed;
|
||||
padding: .5em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.jx-dialog button {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.jx-dialog input {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.jx-dialog .fields {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.jx-dialog input.block {
|
||||
display: block;
|
||||
margin: 3px 0 0 0;
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/**
|
||||
* @provides javelin-behavior-differential-edit-inline-comments
|
||||
* @requires javelin-lib-dev
|
||||
*/
|
||||
|
||||
JX.behavior('differential-edit-inline-comments', function(config) {
|
||||
|
||||
var selecting = false;
|
||||
var reticle = JX.$N('div', {className: 'differential-reticle'});
|
||||
JX.DOM.hide(reticle);
|
||||
document.body.appendChild(reticle);
|
||||
|
||||
var origin = null;
|
||||
var target = null;
|
||||
var root = null;
|
||||
var changeset = null;
|
||||
var workflow = false;
|
||||
var is_new = false;
|
||||
|
||||
function updateReticle() {
|
||||
var top = origin;
|
||||
var bot = target;
|
||||
if (JX.$V(top).y > JX.$V(bot).y) {
|
||||
var tmp = top;
|
||||
top = bot;
|
||||
bot = tmp;
|
||||
}
|
||||
|
||||
var code = target.nextSibling;
|
||||
|
||||
var pos = JX.$V(top).add(1 + JX.$V.getDim(target).x, 0);
|
||||
var dim = JX.$V.getDim(code).add(-4, 0);
|
||||
dim.y = (JX.$V(bot).y - pos.y) + JX.$V.getDim(bot).y;
|
||||
|
||||
pos.setPos(reticle);
|
||||
dim.setDim(reticle);
|
||||
|
||||
JX.DOM.show(reticle);
|
||||
}
|
||||
|
||||
function hideReticle() {
|
||||
JX.DOM.hide(reticle);
|
||||
}
|
||||
|
||||
function finishSelect() {
|
||||
selecting = false;
|
||||
workflow = false;
|
||||
hideReticle();
|
||||
}
|
||||
|
||||
function drawInlineComment(table, anchor, r) {
|
||||
copyRows(table, JX.$N('div', JX.HTML(r.markup)), anchor);
|
||||
finishSelect();
|
||||
}
|
||||
|
||||
function isNewFile(node) {
|
||||
return node.parentNode.firstChild != node;
|
||||
}
|
||||
|
||||
function getRowNumber(th_node) {
|
||||
try {
|
||||
return parseInt(th_node.id.match(/^C\d+[ON]L(\d+)$/)[1], 10);
|
||||
} catch (x) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'mousedown',
|
||||
['differential-changeset', 'tag:th'],
|
||||
function(e) {
|
||||
if (workflow ||
|
||||
selecting ||
|
||||
getRowNumber(e.getTarget()) === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
selecting = true;
|
||||
root = e.getNode('differential-changeset');
|
||||
|
||||
origin = target = e.getTarget();
|
||||
|
||||
var data = e.getNodeData('differential-changeset');
|
||||
if (isNewFile(target)) {
|
||||
changeset = data.oid;
|
||||
} else {
|
||||
changeset = data.nid;
|
||||
}
|
||||
|
||||
updateReticle();
|
||||
|
||||
e.kill();
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'mouseover',
|
||||
['differential-changeset', 'tag:th'],
|
||||
function(e) {
|
||||
if (!selecting ||
|
||||
workflow ||
|
||||
(getRowNumber(e.getTarget()) === undefined) ||
|
||||
(isNewFile(e.getTarget()) != isNewFile(origin)) ||
|
||||
(e.getNode('differential-changeset') !== root)) {
|
||||
return;
|
||||
}
|
||||
|
||||
target = e.getTarget();
|
||||
|
||||
updateReticle();
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'mouseup',
|
||||
null,
|
||||
function(e) {
|
||||
if (workflow || !selecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
var o = getRowNumber(origin);
|
||||
var t = getRowNumber(target);
|
||||
|
||||
var insert;
|
||||
var len;
|
||||
if (t < o) {
|
||||
len = (o - t);
|
||||
o = t;
|
||||
insert = origin.parentNode;
|
||||
} else {
|
||||
len = (t - o);
|
||||
insert = target.parentNode;
|
||||
}
|
||||
|
||||
var data = {
|
||||
op: 'new',
|
||||
changeset: changeset,
|
||||
number: o,
|
||||
length: len,
|
||||
is_new: isNewFile(target) ? 1 : 0
|
||||
};
|
||||
|
||||
workflow = true;
|
||||
|
||||
var w = new JX.Workflow(config.uri, data)
|
||||
.setHandler(function(r) {
|
||||
// Skip over any rows which contain inline feedback. Don't mimic this!
|
||||
// We're shipping around raw HTML here for performance reasons, but
|
||||
// normally you should use sigils to encode this kind of data on
|
||||
// the document.
|
||||
var target = insert.nextSibling;
|
||||
while (target &&
|
||||
(!JX.DOM.isType(target, 'tr')
|
||||
|| target.className.indexOf('inline') !== -1)) {
|
||||
target = target.nextSibling;
|
||||
}
|
||||
drawInlineComment(insert.parentNode, target, r);
|
||||
finishSelect();
|
||||
JX.Stratcom.invoke('inline-comment-update',
|
||||
null,
|
||||
{id : r.inlineCommentID});
|
||||
})
|
||||
.setCloseHandler(finishSelect);
|
||||
|
||||
|
||||
w.listen('error', function(e) {
|
||||
// TODO: uh, tell the user I guess
|
||||
finishSelect();
|
||||
JX.Stratcom.context().stop();
|
||||
});
|
||||
|
||||
w.start();
|
||||
|
||||
e.kill();
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
['mouseover', 'mouseout'],
|
||||
'inline-comment',
|
||||
function(e) {
|
||||
if (selecting || workflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.getType() == 'mouseout') {
|
||||
hideReticle();
|
||||
} else {
|
||||
var data = e.getNodeData('inline-comment');
|
||||
var change = e.getNodeData('differential-changeset');
|
||||
|
||||
root = e.getNode('differential-changeset');
|
||||
|
||||
var prefix = 'C' + change;
|
||||
|
||||
if (data.is_new) {
|
||||
prefix += 'NL';
|
||||
} else {
|
||||
prefix += 'OL';
|
||||
}
|
||||
|
||||
origin = JX.$(prefix + data.number);
|
||||
target = JX.$(prefix + (data.number + data.length));
|
||||
|
||||
updateReticle();
|
||||
}
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
[['inline-comment', 'delete'],
|
||||
['inline-comment', 'edit']],
|
||||
function(e) {
|
||||
var data = {
|
||||
op: e.getNode('edit') ? 'edit' : 'delete',
|
||||
id: e.getNodeData('inline-comment').id
|
||||
};
|
||||
new JX.Workflow(config.uri, data)
|
||||
.setHandler(function(r) {
|
||||
var base_row = e.getNode('inline-comment').parentNode.parentNode;
|
||||
if (data.op == 'edit' && r.markup) {
|
||||
drawInlineComment(base_row.parentNode, base_row, r);
|
||||
}
|
||||
JX.DOM.remove(base_row);
|
||||
JX.Stratcom.invoke('differential-inline-comment-update');
|
||||
})
|
||||
.start();
|
||||
e.kill();
|
||||
});
|
||||
|
||||
});
|
|
@ -1,4 +1,3 @@
|
|||
/** @provides javelin-init-dev */
|
||||
/**
|
||||
* Javelin core; installs Javelin and Stratcom event delegation.
|
||||
*
|
||||
|
@ -31,7 +30,6 @@
|
|||
JX.__rawEventQueue = function(what) {
|
||||
master_event_queue.push(what);
|
||||
|
||||
|
||||
// Evade static analysis - JX.Stratcom
|
||||
var Stratcom = JX['Stratcom'];
|
||||
if (Stratcom && Stratcom.ready) {
|
||||
|
@ -63,7 +61,8 @@
|
|||
var target = what.srcElement || what.target;
|
||||
if (target &&
|
||||
(what.type in {click: 1, submit: 1}) &&
|
||||
(/ FI_CAPTURE /).test(' ' + target.className + ' ')) {
|
||||
target.getAttribute &&
|
||||
target.getAttribute('data-mustcapture') === '1') {
|
||||
what.returnValue = false;
|
||||
what.preventDefault && what.preventDefault();
|
||||
document.body.id = 'event_capture';
|
||||
|
|
2
webroot/rsrc/js/javelin/init.min.js
vendored
2
webroot/rsrc/js/javelin/init.min.js
vendored
|
@ -1,2 +1,2 @@
|
|||
/** @provides javelin-init-prod */
|
||||
(function(){if(window.JX)return;window.JX={};window.__DEV__=window.__DEV__||0;var d=false;var f=[];var e=[];var h=document.documentElement;var b=!!h.addEventListener;JX.__rawEventQueue=function(o){e.push(o);var j=JX.Stratcom;if(j&&j.ready){var m=e;e=[];for(var l=0;l<m.length;++l){var k=m[l];try{var test=k.type;}catch(p){continue;}if(!d&&k.type=='domready'){document.body&&(document.body.id=null);d=true;for(var l=0;l<f.length;l++)f[l]();}j.dispatch(k);}}else{var n=o.srcElement||o.target;if(n&&(o.type in {click:1,submit:1})&&(/ FI_CAPTURE /).test(' '+n.className+' ')){o.returnValue=false;o.preventDefault&&o.preventDefault();document.body.id='event_capture';if(!add_event_listener&&document.createEventObject){e.pop();e.push(document.createEventObject(o));}return false;}}};JX.enableDispatch=function(j,k){if(j.addEventListener){j.addEventListener(k,JX.__rawEventQueue,true);}else if(j.attachEvent)j.attachEvent('on'+k,JX.__rawEventQueue);};var a=['click','change','keypress','mousedown','mouseover','mouseout','mouseup','keydown','drop','dragenter','dragleave','dragover'];if(!b)a.push('focusin','focusout');if(window.opera){a.push('focus','blur');}else a.push('submit');for(var c=0;c<a.length;++c)JX.enableDispatch(h,a[c]);var i=[('onpagehide' in window)?'pagehide':'unload','resize','focus','blur'];for(var c=0;c<i.length;++c)JX.enableDispatch(window,i[c]);JX.__simulate=function(k,event){if(!b){var j={target:k,type:event};JX.__rawEventQueue(j);if(j.returnValue===false)return false;}};if(b){document.addEventListener('DOMContentLoaded',function(){JX.__rawEventQueue({type:'domready'});},true);}else{var g="if (this.readyState == 'complete') {"+"JX.__rawEventQueue({type: 'domready'});"+"}";document.write('<script'+' defer="defer"'+' src="javascript:void(0)"'+' onreadystatechange="'+g+'"'+'><\/sc'+'ript\>');}JX.onload=function(j){if(d){j();}else f.push(j);};})();
|
||||
(function(){if(window.JX)return;window.JX={};window.__DEV__=window.__DEV__||0;var d=false;var f=[];var e=[];var h=document.documentElement;var b=!!h.addEventListener;JX.__rawEventQueue=function(o){e.push(o);var j=JX.Stratcom;if(j&&j.ready){var m=e;e=[];for(var l=0;l<m.length;++l){var k=m[l];try{var test=k.type;}catch(p){continue;}if(!d&&k.type=='domready'){document.body&&(document.body.id=null);d=true;for(var l=0;l<f.length;l++)f[l]();}j.dispatch(k);}}else{var n=o.srcElement||o.target;if(n&&(o.type in {click:1,submit:1})&&n.getAttribute&&n.getAttribute('data-mustcapture')==='1'){o.returnValue=false;o.preventDefault&&o.preventDefault();document.body.id='event_capture';if(!add_event_listener&&document.createEventObject){e.pop();e.push(document.createEventObject(o));}return false;}}};JX.enableDispatch=function(j,k){if(j.addEventListener){j.addEventListener(k,JX.__rawEventQueue,true);}else if(j.attachEvent)j.attachEvent('on'+k,JX.__rawEventQueue);};var a=['click','change','keypress','mousedown','mouseover','mouseout','mouseup','keydown','drop','dragenter','dragleave','dragover'];if(!b)a.push('focusin','focusout');if(window.opera){a.push('focus','blur');}else a.push('submit');for(var c=0;c<a.length;++c)JX.enableDispatch(h,a[c]);var i=[('onpagehide' in window)?'pagehide':'unload','resize','focus','blur'];for(var c=0;c<i.length;++c)JX.enableDispatch(window,i[c]);JX.__simulate=function(k,event){if(!b){var j={target:k,type:event};JX.__rawEventQueue(j);if(j.returnValue===false)return false;}};if(b){document.addEventListener('DOMContentLoaded',function(){JX.__rawEventQueue({type:'domready'});},true);}else{var g="if (this.readyState == 'complete') {"+"JX.__rawEventQueue({type: 'domready'});"+"}";document.write('<script'+' defer="defer"'+' src="javascript:void(0)"'+' onreadystatechange="'+g+'"'+'><\/sc'+'ript\>');}JX.onload=function(j){if(d){j();}else f.push(j);};})();
|
||||
|
|
|
@ -824,7 +824,6 @@ JX.install('Event', {
|
|||
* }
|
||||
* });
|
||||
*
|
||||
*
|
||||
* @return string|null ##null## if there is no associated special key,
|
||||
* or one of the strings 'delete', 'tab', 'return',
|
||||
* 'esc', 'left', 'up', 'right', or 'down'.
|
||||
|
@ -836,21 +835,17 @@ JX.install('Event', {
|
|||
return null;
|
||||
}
|
||||
|
||||
var c = r.keyCode;
|
||||
do {
|
||||
c = JX.Event._keymap[c] || null;
|
||||
} while (c && JX.Event._keymap[c])
|
||||
|
||||
return c;
|
||||
return JX.Event._keymap[r.keyCode] || null;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the node corresponding to the specified key in this event's node map.
|
||||
* This is a simple helper method that makes the API for accessing nodes
|
||||
* less ugly.
|
||||
*
|
||||
* JX.Stratcom.listen('click', 'tag:a', function(e) {
|
||||
* var a = e.getNode('nearest:a');
|
||||
* var a = e.getNode('tag:a');
|
||||
* // do something with the link that was clicked
|
||||
* });
|
||||
*
|
||||
|
@ -864,8 +859,27 @@ JX.install('Event', {
|
|||
*/
|
||||
getNode : function(key) {
|
||||
return this.getNodes()[key] || null;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the metadata associated with the node that corresponds to the key
|
||||
* in this event's node map. This is a simple helper method that makes
|
||||
* the API for accessing metadata associated with specific nodes less ugly.
|
||||
*
|
||||
* JX.Stratcom.listen('click', 'tag:a', function(event) {
|
||||
* var anchorData = event.getNodeData('tag:a');
|
||||
* // do something with the metadata of the link that was clicked
|
||||
* });
|
||||
*
|
||||
* @param string sigil or stratcom node key
|
||||
* @return dict dictionary of the node's metadata
|
||||
* @task info
|
||||
*/
|
||||
getNodeData : function(key) {
|
||||
// Evade static analysis - JX.Stratcom
|
||||
return JX['Stratcom'].getData(this.getNode(key));
|
||||
}
|
||||
},
|
||||
|
||||
statics : {
|
||||
|
@ -878,10 +892,10 @@ JX.install('Event', {
|
|||
38 : 'up',
|
||||
39 : 'right',
|
||||
40 : 'down',
|
||||
63232 : 38,
|
||||
63233 : 40,
|
||||
62234 : 37,
|
||||
62235 : 39
|
||||
63232 : 'up',
|
||||
63233 : 'down',
|
||||
62234 : 'left',
|
||||
62235 : 'right'
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1008,6 +1022,7 @@ JX.install('Event', {
|
|||
* @task listen Listening to Events
|
||||
* @task handle Responding to Events
|
||||
* @task sigil Managing Sigils
|
||||
* @task meta Managing Metadata
|
||||
* @task internal Internals
|
||||
*/
|
||||
JX.install('Stratcom', {
|
||||
|
@ -1016,8 +1031,6 @@ JX.install('Stratcom', {
|
|||
_targets : {},
|
||||
_handlers : [],
|
||||
_need : {},
|
||||
_matchName : /\bFN_([^ ]+)/,
|
||||
_matchData : /\bFD_([^ ]+)_([^ ]+)/,
|
||||
_auto : '*',
|
||||
_data : {},
|
||||
_execContext : [],
|
||||
|
@ -1039,13 +1052,13 @@ JX.install('Stratcom', {
|
|||
|
||||
/**
|
||||
* Within each datablock, data is identified by a unique index. The data
|
||||
* pointer on a node looks like this:
|
||||
* pointer (data-meta attribute) on a node looks like this:
|
||||
*
|
||||
* FD_1_2
|
||||
* 1_2
|
||||
*
|
||||
* ...where 1 is the block, and 2 is the index within that block. Normally,
|
||||
* blocks are filled on the server side, so index allocation takes place
|
||||
* there. However, when data is provided with JX.Stratcom.sigilize(), we
|
||||
* there. However, when data is provided with JX.Stratcom.addData(), we
|
||||
* need to allocate indexes on the client.
|
||||
*/
|
||||
_dataIndex : 0,
|
||||
|
@ -1185,9 +1198,12 @@ JX.install('Stratcom', {
|
|||
if (path[kk] == 'tag:#document') {
|
||||
throw new Error(
|
||||
'JX.Stratcom.listen(..., "tag:#document", ...): ' +
|
||||
'listen for document events as "tag:window", not ' +
|
||||
'"tag:#document", in order to get consistent behavior ' +
|
||||
'across browsers.');
|
||||
'listen for all events using null, not "tag:#document"');
|
||||
}
|
||||
if (path[kk] == 'tag:window') {
|
||||
throw new Error(
|
||||
'JX.Stratcom.listen(..., "tag:window", ...): ' +
|
||||
'listen for window events using null, not "tag:window"');
|
||||
}
|
||||
}
|
||||
if (!type_target[path[kk]]) {
|
||||
|
@ -1219,29 +1235,27 @@ JX.install('Stratcom', {
|
|||
* @task internal
|
||||
*/
|
||||
dispatch : function(event) {
|
||||
// TODO: simplify this :P
|
||||
var target;
|
||||
try {
|
||||
target = event.srcElement || event.target;
|
||||
if (target === window || (!target || target.nodeName == '#document')) {
|
||||
target = {nodeName: 'window'};
|
||||
}
|
||||
} catch (x) {
|
||||
target = null;
|
||||
}
|
||||
|
||||
var path = [];
|
||||
var nodes = {};
|
||||
var push = function(key, node) {
|
||||
// we explicitly only store the first occurrence of each key
|
||||
if (!(key in nodes)) {
|
||||
if (!nodes.hasOwnProperty(key)) {
|
||||
nodes[key] = node;
|
||||
path.push(key);
|
||||
}
|
||||
};
|
||||
|
||||
var target = event.srcElement || event.target;
|
||||
|
||||
// Since you can only listen by tag, id or sigil, which are all
|
||||
// attributes of an Element (the DOM interface), we unset the target
|
||||
// if it isn't an Element (window and Document are Nodes but not Elements)
|
||||
if (!target || !target.getAttribute) {
|
||||
target = null;
|
||||
}
|
||||
|
||||
var cursor = target;
|
||||
while (cursor) {
|
||||
while (cursor && cursor.getAttribute) {
|
||||
push('tag:' + cursor.nodeName.toLowerCase(), cursor);
|
||||
|
||||
var id = cursor.id;
|
||||
|
@ -1249,11 +1263,12 @@ JX.install('Stratcom', {
|
|||
push('id:' + id, cursor);
|
||||
}
|
||||
|
||||
var source = cursor.className || '';
|
||||
// className is an SVGAnimatedString for SVG elements, use baseVal
|
||||
var token = ((source.baseVal || source).match(this._matchName) || [])[1];
|
||||
if (token) {
|
||||
push(token, cursor);
|
||||
var sigils = cursor.getAttribute('data-sigil');
|
||||
if (sigils) {
|
||||
sigils = sigils.split(' ');
|
||||
for (var ii = 0; ii < sigils.length; ii++) {
|
||||
push(sigils[ii], cursor);
|
||||
}
|
||||
}
|
||||
|
||||
cursor = cursor.parentNode;
|
||||
|
@ -1264,16 +1279,10 @@ JX.install('Stratcom', {
|
|||
etype = this._typeMap[etype];
|
||||
}
|
||||
|
||||
var data = {};
|
||||
for (var key in nodes) {
|
||||
data[key] = this.getData(nodes[key]);
|
||||
}
|
||||
|
||||
var proxy = new JX.Event()
|
||||
.setRawEvent(event)
|
||||
.setType(etype)
|
||||
.setTarget(target)
|
||||
.setData(data)
|
||||
.setNodes(nodes)
|
||||
.setPath(path.reverse());
|
||||
|
||||
|
@ -1408,40 +1417,6 @@ JX.install('Stratcom', {
|
|||
},
|
||||
|
||||
|
||||
/**
|
||||
* Attach a sigil (and, optionally, metadata) to a node. Note that you can
|
||||
* not overwrite, remove or replace a sigil.
|
||||
*
|
||||
* @param Node Node without any sigil.
|
||||
* @param string Sigil to name the node with.
|
||||
* @param object? Optional metadata object to attach to the node.
|
||||
* @return void
|
||||
* @task sigil
|
||||
*/
|
||||
sigilize : function(node, sigil, data) {
|
||||
if (__DEV__) {
|
||||
if (node.className.match(this._matchName)) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.sigilize(<node>, ' + sigil + ', ...): ' +
|
||||
'node already has a sigil, sigils may not be overwritten.');
|
||||
}
|
||||
if (typeof data != 'undefined' &&
|
||||
(data === null || typeof data != 'object')) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.sigilize(..., ..., <nonobject>): ' +
|
||||
'data to attach to node is not an object. You must use ' +
|
||||
'objects, not primitives, for metadata.');
|
||||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
JX.Stratcom._setData(node, data);
|
||||
}
|
||||
|
||||
node.className = 'FN_' + sigil + ' ' + node.className;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a node has a specific sigil.
|
||||
*
|
||||
|
@ -1452,14 +1427,44 @@ JX.install('Stratcom', {
|
|||
* @task sigil
|
||||
*/
|
||||
hasSigil : function(node, sigil) {
|
||||
if (!node.className) {
|
||||
// Some nodes don't have a className, notably 'document'. We hit
|
||||
// 'document' when following .parentNode chains, e.g. in
|
||||
// JX.DOM.nearest(), so exit early if we don't have a className to avoid
|
||||
// fataling on 'node.className.match' being undefined.
|
||||
return false;
|
||||
if (__DEV__) {
|
||||
if (!node || !node.getAttribute) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.hasSigil(<non-element>, ...): ' +
|
||||
'node is not an element. Most likely, you\'re passing window or ' +
|
||||
'document, which are not elements and can\'t have sigils.');
|
||||
}
|
||||
return (node.className.match(this._matchName) || [])[1] == sigil;
|
||||
}
|
||||
|
||||
var sigils = node.getAttribute('data-sigil');
|
||||
return sigils && (' ' + sigils + ' ').indexOf(' ' + sigil + ' ') > -1;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Add a sigil to a node.
|
||||
*
|
||||
* @param Node Node to add the sigil to.
|
||||
* @param string Sigil to name the node with.
|
||||
* @return void
|
||||
* @task sigil
|
||||
*/
|
||||
addSigil: function(node, sigil) {
|
||||
if (__DEV__) {
|
||||
if (!node || !node.getAttribute) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.addSigil(<non-element>, ...): ' +
|
||||
'node is not an element. Most likely, you\'re passing window or ' +
|
||||
'document, which are not elements and can\'t have sigils.');
|
||||
}
|
||||
}
|
||||
|
||||
var sigils = node.getAttribute('data-sigil');
|
||||
if (sigils && !JX.Stratcom.hasSigil(node, sigil)) {
|
||||
sigil = sigils + ' ' + sigil;
|
||||
}
|
||||
|
||||
node.setAttribute('data-sigil', sigil);
|
||||
},
|
||||
|
||||
|
||||
|
@ -1467,60 +1472,75 @@ JX.install('Stratcom', {
|
|||
* Retrieve a node's metadata.
|
||||
*
|
||||
* @param Node Node from which to retrieve data.
|
||||
* @return object Data attached to the node, or an empty dictionary if
|
||||
* the node has no data attached. In this case, the empty
|
||||
* dictionary is set as the node's metadata -- i.e.,
|
||||
* subsequent calls to getData() will retrieve the same
|
||||
* object.
|
||||
*
|
||||
* @task sigil
|
||||
* @return object Data attached to the node. If no data has been attached
|
||||
* to the node yet, an empty object will be returned, but
|
||||
* subsequent calls to this method will always retrieve the
|
||||
* same object.
|
||||
* @task meta
|
||||
*/
|
||||
getData : function(node) {
|
||||
if (__DEV__) {
|
||||
if (!node) {
|
||||
if (!node || !node.getAttribute) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.getData(<empty>): ' +
|
||||
'you must provide a node to get associated data from.');
|
||||
'JX.Stratcom.getData(<non-element>): ' +
|
||||
'node is not an element. Most likely, you\'re passing window or ' +
|
||||
'document, which are not elements and can\'t have data.');
|
||||
}
|
||||
}
|
||||
|
||||
var matches = (node.className || '').match(this._matchData);
|
||||
if (matches) {
|
||||
var block = this._data[matches[1]];
|
||||
var index = matches[2];
|
||||
var meta_id = (node.getAttribute('data-meta') || '').split('_');
|
||||
if (meta_id[0] && meta_id[1]) {
|
||||
var block = this._data[meta_id[0]];
|
||||
var index = meta_id[1];
|
||||
if (block && (index in block)) {
|
||||
return block[index];
|
||||
}
|
||||
}
|
||||
|
||||
return JX.Stratcom._setData(node, {});
|
||||
var data = {};
|
||||
if (!this._data[1]) { // data block 1 is reserved for JavaScript
|
||||
this._data[1] = {};
|
||||
}
|
||||
this._data[1][this._dataIndex] = data;
|
||||
node.setAttribute('data-meta', '1_' + (this._dataIndex++));
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Add data to a node's metadata.
|
||||
*
|
||||
* @param Node Node which data should be attached to.
|
||||
* @param object Data to add to the node's metadata.
|
||||
* @return object Data attached to the node that is returned by
|
||||
* JX.Stratcom.getData().
|
||||
* @task meta
|
||||
*/
|
||||
addData : function(node, data) {
|
||||
if (__DEV__) {
|
||||
if (!node || !node.getAttribute) {
|
||||
throw new Error(
|
||||
'JX.Stratcom.addData(<non-element>, ...): ' +
|
||||
'node is not an element. Most likely, you\'re passing window or ' +
|
||||
'document, which are not elements and can\'t have sigils.');
|
||||
}
|
||||
if (!data || typeof data != 'object') {
|
||||
throw new Error(
|
||||
'JX.Stratcom.addData(..., <nonobject>): ' +
|
||||
'data to attach to node is not an object. You must use ' +
|
||||
'objects, not primitives, for metadata.');
|
||||
}
|
||||
}
|
||||
|
||||
return JX.copy(JX.Stratcom.getData(node), data);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @task internal
|
||||
*/
|
||||
allocateMetadataBlock : function() {
|
||||
return this._dataBlock++;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach metadata to a node. This data can later be retrieved through
|
||||
* @{JX.Stratcom.getData()}, or @{JX.Event.getData()}.
|
||||
*
|
||||
* @param Node Node which data should be attached to.
|
||||
* @param object Data to attach.
|
||||
* @return object Attached data.
|
||||
*
|
||||
* @task internal
|
||||
*/
|
||||
_setData : function(node, data) {
|
||||
if (!this._data[1]) { // data block 1 is reserved for javascript
|
||||
this._data[1] = {};
|
||||
}
|
||||
this._data[1][this._dataIndex] = data;
|
||||
node.className = 'FD_1_' + (this._dataIndex++) + ' ' + node.className;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1535,7 +1555,7 @@ JX.install('Stratcom', {
|
|||
|
||||
JX.behavior = function(name, control_function) {
|
||||
if (__DEV__) {
|
||||
if (name in JX.behavior._behaviors) {
|
||||
if (JX.behavior._behaviors.hasOwnProperty(name)) {
|
||||
throw new Error(
|
||||
'JX.behavior("'+name+'", ...): '+
|
||||
'behavior is already registered.');
|
||||
|
@ -1566,7 +1586,7 @@ JX.initBehaviors = function(map) {
|
|||
}
|
||||
var configs = map[name];
|
||||
if (!configs.length) {
|
||||
if (name in JX.behavior._initialized) {
|
||||
if (JX.behavior._initialized.hasOwnProperty(name)) {
|
||||
continue;
|
||||
} else {
|
||||
configs = [null];
|
||||
|
@ -1827,7 +1847,7 @@ JX.install('Request', {
|
|||
},
|
||||
|
||||
initialize : function() {
|
||||
JX.Stratcom.listen('unload', 'tag:window', JX.Request.shutdown);
|
||||
JX.Stratcom.listen('unload', null, JX.Request.shutdown);
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -2410,8 +2430,12 @@ JX.$N = function(tag, attr, content) {
|
|||
}
|
||||
|
||||
if (attr.sigil) {
|
||||
JX.Stratcom.sigilize(node, attr.sigil, attr.meta);
|
||||
JX.Stratcom.addSigil(node, attr.sigil);
|
||||
delete attr.sigil;
|
||||
}
|
||||
|
||||
if (attr.meta) {
|
||||
JX.Stratcom.addData(node, attr.meta);
|
||||
delete attr.meta;
|
||||
}
|
||||
|
||||
|
@ -2421,17 +2445,6 @@ JX.$N = function(tag, attr, content) {
|
|||
'$N(' + tag + ', ...): ' +
|
||||
'use the key "meta" to specify metadata, not "data" or "metadata".');
|
||||
}
|
||||
if (attr.meta) {
|
||||
throw new Error(
|
||||
'$N(' + tag + ', ...): ' +
|
||||
'if you specify "meta" metadata, you must also specify a "sigil".');
|
||||
}
|
||||
}
|
||||
|
||||
// prevent sigil from being wiped by blind copying the className
|
||||
if (attr.className) {
|
||||
JX.DOM.alterClass(node, attr.className, true);
|
||||
delete attr.className;
|
||||
}
|
||||
|
||||
JX.copy(node, attr);
|
||||
|
@ -2595,7 +2608,7 @@ JX.install('DOM', {
|
|||
* @author jgabbard
|
||||
*/
|
||||
nearest : function(node, sigil) {
|
||||
while (node && !JX.Stratcom.hasSigil(node, sigil)) {
|
||||
while (node && node.getAttribute && !JX.Stratcom.hasSigil(node, sigil)) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
return node;
|
||||
|
|
3
webroot/rsrc/js/javelin/javelin.min.js
vendored
3
webroot/rsrc/js/javelin/javelin.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -86,7 +86,7 @@ JX.install('Typeahead', {
|
|||
'mousedown',
|
||||
'tag:a',
|
||||
JX.bind(this, function(e) {
|
||||
this._choose(e.getTarget());
|
||||
this._choose(e.getNode('tag:a'));
|
||||
e.prevent();
|
||||
}));
|
||||
|
||||
|
@ -601,8 +601,14 @@ JX.install('TypeaheadSource', {
|
|||
var n = Math.min(this.getMaximumResultCount(), hits.length);
|
||||
var nodes = [];
|
||||
for (var kk = 0; kk < n; kk++) {
|
||||
var data = this._raw[hits[kk]];
|
||||
nodes.push(JX.$N(
|
||||
nodes.push(this.createNode(this._raw[hits[kk]]));
|
||||
}
|
||||
|
||||
this._typeahead.showResults(nodes);
|
||||
},
|
||||
|
||||
createNode : function(data) {
|
||||
return JX.$N(
|
||||
'a',
|
||||
{
|
||||
href: data.uri,
|
||||
|
@ -610,11 +616,10 @@ JX.install('TypeaheadSource', {
|
|||
rel: data.id,
|
||||
className: 'jx-result'
|
||||
},
|
||||
data.display));
|
||||
}
|
||||
|
||||
this._typeahead.showResults(nodes);
|
||||
data.display
|
||||
);
|
||||
},
|
||||
|
||||
normalize : function(str) {
|
||||
return (this.getNormalizer() || JX.bag())(str);
|
||||
},
|
||||
|
@ -850,11 +855,7 @@ JX.install('Tokenizer', {
|
|||
this._tokens = [];
|
||||
this._tokenMap = {};
|
||||
|
||||
var focus = JX.$N('input', {
|
||||
className: 'jx-tokenizer-input',
|
||||
type: 'text',
|
||||
value: this._orig.value
|
||||
});
|
||||
var focus = this.buildInput(this._orig.value);
|
||||
this._focus = focus;
|
||||
|
||||
JX.DOM.listen(
|
||||
|
@ -870,8 +871,8 @@ JX.install('Tokenizer', {
|
|||
JX.bind(
|
||||
this,
|
||||
function(e) {
|
||||
if (e.getNodes().remove) {
|
||||
this._remove(e.getData().token.key);
|
||||
if (e.getNode('remove')) {
|
||||
this._remove(e.getNodeData('token').key);
|
||||
} else if (e.getTarget() == this._root) {
|
||||
this.focus();
|
||||
}
|
||||
|
@ -1011,20 +1012,7 @@ JX.install('Tokenizer', {
|
|||
|
||||
var focus = this._focus;
|
||||
var root = this._root;
|
||||
|
||||
var token = JX.$N('a', {
|
||||
className: 'jx-tokenizer-token'
|
||||
}, value);
|
||||
|
||||
var input = JX.$N('input', {
|
||||
type: 'hidden',
|
||||
value: key,
|
||||
name: this._orig.name+'['+(this._seq++)+']'
|
||||
});
|
||||
|
||||
var remove = JX.$N('a', {
|
||||
className: 'jx-tokenizer-x'
|
||||
}, JX.HTML('×'));
|
||||
var token = this.buildToken(key, value);
|
||||
|
||||
this._tokenMap[key] = {
|
||||
value : value,
|
||||
|
@ -1033,17 +1021,42 @@ JX.install('Tokenizer', {
|
|||
};
|
||||
this._tokens.push(key);
|
||||
|
||||
JX.Stratcom.sigilize(token, 'token', {key : key});
|
||||
JX.Stratcom.sigilize(remove, 'remove');
|
||||
|
||||
token.appendChild(input);
|
||||
token.appendChild(remove);
|
||||
|
||||
root.insertBefore(token, focus);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
buildInput: function(value) {
|
||||
return JX.$N('input', {
|
||||
className: 'jx-tokenizer-input',
|
||||
type: 'text',
|
||||
value: value
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a token based on a key and value. The "token" and "remove"
|
||||
* sigils are observed by a listener in start().
|
||||
*/
|
||||
buildToken: function(key, value) {
|
||||
var input = JX.$N('input', {
|
||||
type: 'hidden',
|
||||
value: key,
|
||||
name: this._orig.name + '[' + (this._seq++) + ']'
|
||||
});
|
||||
|
||||
var remove = JX.$N('a', {
|
||||
className: 'jx-tokenizer-x',
|
||||
sigil: 'remove'
|
||||
}, JX.HTML('×'));
|
||||
|
||||
return JX.$N('a', {
|
||||
className: 'jx-tokenizer-token',
|
||||
sigil: 'token',
|
||||
meta: {key: key}
|
||||
}, [value, input, remove]);
|
||||
},
|
||||
|
||||
getTokens : function() {
|
||||
var result = {};
|
||||
for (var key in this._tokenMap) {
|
||||
|
|
2
webroot/rsrc/js/javelin/typeahead.min.js
vendored
2
webroot/rsrc/js/javelin/typeahead.min.js
vendored
File diff suppressed because one or more lines are too long
239
webroot/rsrc/js/javelin/workflow.dev.js
Normal file
239
webroot/rsrc/js/javelin/workflow.dev.js
Normal file
|
@ -0,0 +1,239 @@
|
|||
/** @provides javelin-workflow-dev */
|
||||
|
||||
/**
|
||||
* @requires javelin-install javelin-vector javelin-dom
|
||||
* @provides javelin-mask
|
||||
* @javelin
|
||||
*/
|
||||
|
||||
/**
|
||||
* Show a transparent "mask" over the page; used by Workflow to draw visual
|
||||
* attention to modal dialogs.
|
||||
*/
|
||||
JX.install('Mask', {
|
||||
statics : {
|
||||
_depth : 0,
|
||||
_mask : null,
|
||||
show : function() {
|
||||
if (!JX.Mask._depth) {
|
||||
JX.Mask._mask = JX.$N('div', {className: 'jx-mask'});
|
||||
document.body.appendChild(JX.Mask._mask);
|
||||
JX.$V.getDocument().setDim(JX.Mask._mask);
|
||||
}
|
||||
++JX.Mask._depth;
|
||||
},
|
||||
hide : function() {
|
||||
--JX.Mask._depth;
|
||||
if (!JX.Mask._depth) {
|
||||
JX.DOM.remove(JX.Mask._mask);
|
||||
JX.Mask._mask = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @requires javelin-stratcom
|
||||
* javelin-request
|
||||
* javelin-dom
|
||||
* javelin-vector
|
||||
* javelin-install
|
||||
* javelin-util
|
||||
* javelin-mask
|
||||
* @provides javelin-workflow
|
||||
* @javelin
|
||||
*/
|
||||
|
||||
JX.install('Workflow', {
|
||||
construct : function(uri, data) {
|
||||
if (__DEV__) {
|
||||
if (!uri || uri == '#') {
|
||||
throw new Error(
|
||||
'new JX.Workflow(<?>, ...): '+
|
||||
'bogus URI provided when creating workflow.');
|
||||
}
|
||||
}
|
||||
this.setURI(uri);
|
||||
this.setData(data || {});
|
||||
},
|
||||
|
||||
events : ['error', 'finally', 'submit'],
|
||||
|
||||
statics : {
|
||||
_stack : [],
|
||||
newFromForm : function(form, data) {
|
||||
var inputs = [].concat(
|
||||
JX.DOM.scry(form, 'input'),
|
||||
JX.DOM.scry(form, 'button'),
|
||||
JX.DOM.scry(form, 'textarea'));
|
||||
|
||||
for (var ii = 0; ii < inputs.length; ii++) {
|
||||
if (inputs[ii].disabled) {
|
||||
delete inputs[ii];
|
||||
} else {
|
||||
inputs[ii].disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
var workflow = new JX.Workflow(
|
||||
form.getAttribute('action'),
|
||||
JX.copy(data || {}, JX.DOM.serialize(form)));
|
||||
workflow.setMethod(form.getAttribute('method'));
|
||||
workflow.listen('finally', function() {
|
||||
for (var ii = 0; ii < inputs.length; ii++) {
|
||||
inputs[ii] && (inputs[ii].disabled = false);
|
||||
}
|
||||
});
|
||||
return workflow;
|
||||
},
|
||||
newFromLink : function(link) {
|
||||
var workflow = new JX.Workflow(link.href);
|
||||
return workflow;
|
||||
},
|
||||
_push : function(workflow) {
|
||||
JX.Mask.show();
|
||||
JX.Workflow._stack.push(workflow);
|
||||
},
|
||||
_pop : function() {
|
||||
var dialog = JX.Workflow._stack.pop();
|
||||
(dialog.getCloseHandler() || JX.bag)();
|
||||
dialog._destroy();
|
||||
JX.Mask.hide();
|
||||
},
|
||||
disable : function() {
|
||||
JX.Workflow._disabled = true;
|
||||
},
|
||||
_onbutton : function(event) {
|
||||
|
||||
if (JX.Stratcom.pass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (JX.Workflow._disabled) {
|
||||
return;
|
||||
}
|
||||
var t = event.getTarget();
|
||||
if (t.name == '__cancel__' || t.name == '__close__') {
|
||||
JX.Workflow._pop();
|
||||
} else {
|
||||
|
||||
var form = event.getNode('jx-dialog');
|
||||
var data = JX.DOM.serialize(form);
|
||||
data[t.name] = true;
|
||||
data.__wflow__ = true;
|
||||
|
||||
var active = JX.Workflow._stack[JX.Workflow._stack.length - 1];
|
||||
var e = active.invoke('submit', {form: form, data: data});
|
||||
if (!e.getStopped()) {
|
||||
active._destroy();
|
||||
active
|
||||
.setURI(form.getAttribute('action') || active.getURI())
|
||||
.setData(data)
|
||||
.start();
|
||||
}
|
||||
}
|
||||
event.prevent();
|
||||
}
|
||||
},
|
||||
|
||||
members : {
|
||||
_root : null,
|
||||
_pushed : false,
|
||||
_onload : function(r) {
|
||||
// It is permissible to send back a falsey redirect to force a page
|
||||
// reload, so we need to take this branch if the key is present.
|
||||
if (r && (typeof r.redirect != 'undefined')) {
|
||||
JX.go(r.redirect, true);
|
||||
} else if (r && r.dialog) {
|
||||
this._push();
|
||||
this._root = JX.$N(
|
||||
'div',
|
||||
{className: 'jx-client-dialog'},
|
||||
JX.HTML(r.dialog));
|
||||
JX.DOM.listen(
|
||||
this._root,
|
||||
'click',
|
||||
'tag:button',
|
||||
JX.Workflow._onbutton);
|
||||
document.body.appendChild(this._root);
|
||||
var d = JX.$V.getDim(this._root);
|
||||
var v = JX.$V.getViewport();
|
||||
var s = JX.$V.getScroll();
|
||||
JX.$V((v.x - d.x) / 2, s.y + 100).setPos(this._root);
|
||||
try {
|
||||
JX.DOM.focus(JX.DOM.find(this._root, 'button', '__default__'));
|
||||
var inputs = JX.DOM.scry(this._root, 'input')
|
||||
.concat(JX.DOM.scry(this._root, 'textarea'));
|
||||
var miny = Number.POSITIVE_INFINITY;
|
||||
var target = null;
|
||||
for (var ii = 0; ii < inputs.length; ++ii) {
|
||||
if (inputs[ii].type != 'hidden') {
|
||||
// Find the topleft-most displayed element.
|
||||
var p = JX.$V(inputs[ii]);
|
||||
if (p.y < miny) {
|
||||
miny = p.y;
|
||||
target = inputs[ii];
|
||||
}
|
||||
}
|
||||
}
|
||||
target && JX.DOM.focus(target);
|
||||
} catch (_ignored) {}
|
||||
} else if (this.getHandler()) {
|
||||
this.getHandler()(r);
|
||||
this._pop();
|
||||
} else if (r) {
|
||||
if (__DEV__) {
|
||||
throw new Error('Response to workflow request went unhandled.');
|
||||
}
|
||||
}
|
||||
},
|
||||
_push : function() {
|
||||
if (!this._pushed) {
|
||||
this._pushed = true;
|
||||
JX.Workflow._push(this);
|
||||
}
|
||||
},
|
||||
_pop : function() {
|
||||
if (this._pushed) {
|
||||
this._pushed = false;
|
||||
JX.Workflow._pop();
|
||||
}
|
||||
},
|
||||
_destroy : function() {
|
||||
if (this._root) {
|
||||
JX.DOM.remove(this._root);
|
||||
this._root = null;
|
||||
}
|
||||
},
|
||||
start : function() {
|
||||
var uri = this.getURI();
|
||||
var method = this.getMethod();
|
||||
var r = new JX.Request(uri, JX.bind(this, this._onload));
|
||||
r.setData(this.getData());
|
||||
r.setDataSerializer(this.getDataSerializer());
|
||||
if (method) {
|
||||
r.setMethod(method);
|
||||
}
|
||||
r.listen('finally', JX.bind(this, this.invoke, 'finally'));
|
||||
r.listen('error', JX.bind(this, function(error) {
|
||||
var e = this.invoke('error', error);
|
||||
if (e.getStopped()) {
|
||||
return;
|
||||
}
|
||||
// TODO: Default error behavior? On Facebook Lite, we just shipped the
|
||||
// user to "/error/". We could emit a blanket 'workflow-failed' type
|
||||
// event instead.
|
||||
}));
|
||||
r.send();
|
||||
}
|
||||
},
|
||||
|
||||
properties : {
|
||||
handler : null,
|
||||
closeHandler : null,
|
||||
data : null,
|
||||
dataSerializer : null,
|
||||
method : null,
|
||||
URI : null
|
||||
}
|
||||
|
||||
});
|
3
webroot/rsrc/js/javelin/workflow.min.js
vendored
Normal file
3
webroot/rsrc/js/javelin/workflow.min.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/** @provides javelin-workflow-prod */
|
||||
|
||||
JX.install('Mask',{statics:{_a:0,_b:null,show:function(){if(!JX.Mask._a){JX.Mask._b=JX.$N('div',{className:'jx-mask'});document.body.appendChild(JX.Mask._b);JX.$V.getDocument().setDim(JX.Mask._b);}++JX.Mask._a;},hide:function(){--JX.Mask._a;if(!JX.Mask._a){JX.DOM.remove(JX.Mask._b);JX.Mask._b=null;}}}});JX.install('Workflow',{construct:function(b,a){this.setURI(b);this.setData(a||{});},events:['error','finally','submit'],statics:{_c:[],newFromForm:function(b,a){var d=[].concat(JX.DOM.scry(b,'input'),JX.DOM.scry(b,'button'),JX.DOM.scry(b,'textarea'));for(var c=0;c<d.length;c++)if(d[c].disabled){delete d[c];}else d[c].disabled=true;var e=new JX.Workflow(b.getAttribute('action'),JX.copy(a||{},JX.DOM.serialize(b)));e.setMethod(b.getAttribute('method'));e.listen('finally',function(){for(var f=0;f<d.length;f++)d[f]&&(d[f].disabled=false);});return e;},newFromLink:function(a){var b=new JX.Workflow(a.href);return b;},_d:function(a){JX.Mask.show();JX.Workflow._c.push(a);},_e:function(){var a=JX.Workflow._c.pop();(a.getCloseHandler()||JX.bag)();a._f();JX.Mask.hide();},disable:function(){JX.Workflow._g=true;},_h:function(event){if(JX.Stratcom.pass())return;if(JX.Workflow._g)return;var e=event.getTarget();if(e.name=='__cancel__'||e.name=='__close__'){JX.Workflow._e();}else{var d=event.getNode('jx-dialog');var b=JX.DOM.serialize(d);b[e.name]=true;b.__wflow__=true;var a=JX.Workflow._c[JX.Workflow._c.length-1];var c=a.invoke('submit',{form:d,data:b});if(!c.getStopped()){a._f();a.setURI(d.getAttribute('action')||a.getURI()).setData(b).start();}}event.prevent();}},members:{_i:null,_j:false,_k:function(c){if(c&&(typeof c.redirect!='undefined')){JX.go(c.redirect,true);}else if(c&&c.dialog){this._d();this._i=JX.$N('div',{className:'jx-client-dialog'},JX.HTML(c.dialog));JX.DOM.listen(this._i,'click','tag:button',JX.Workflow._h);document.body.appendChild(this._i);var b=JX.$V.getDim(this._i);var e=JX.$V.getViewport();var d=JX.$V.getScroll();JX.$V((e.x-b.x)/2,d.y+100).setPos(this._i);try{JX.DOM.focus(JX.DOM.find(this._i,'button','__default__'));var inputs=JX.DOM.scry(this._i,'input').concat(JX.DOM.scry(this._i,'textarea'));var miny=Number.POSITIVE_INFINITY;var target=null;for(var ii=0;ii<inputs.length;++ii)if(inputs[ii].type!='hidden'){var p=JX.$V(inputs[ii]);if(p.y<miny){miny=p.y;target=inputs[ii];}}target&&JX.DOM.focus(target);}catch(a){}}else if(this.getHandler()){this.getHandler()(c);this._e();}},_d:function(){if(!this._j){this._j=true;JX.Workflow._d(this);}},_e:function(){if(this._j){this._j=false;JX.Workflow._e();}},_f:function(){if(this._i){JX.DOM.remove(this._i);this._i=null;}},start:function(){var c=this.getURI();var a=this.getMethod();var b=new JX.Request(c,JX.bind(this,this._k));b.setData(this.getData());b.setDataSerializer(this.getDataSerializer());if(a)b.setMethod(a);b.listen('finally',JX.bind(this,this.invoke,'finally'));b.listen('error',JX.bind(this,function(e){var d=this.invoke('error',e);if(d.getStopped())return;}));b.send();}},properties:{handler:null,closeHandler:null,data:null,dataSerializer:null,method:null,URI:null}});
|
Loading…
Reference in a new issue