From 5d005c9ff87cb240b022c6bdb66b780e6369d2ab Mon Sep 17 00:00:00 2001
From: Iliyan Peychev
+ * An augmentable class, which provides the augmented class with the ability to host plugins.
+ * It adds plug and unplug methods to the augmented class, which can
+ * be used to add or remove plugins from instances of the class.
+ * Plugins can also be added through the constructor configuration object passed to the host class' constructor using
+ * the "plugins" property. Supported values for the "plugins" property are those defined by the plug method.
+ *
+ * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host):
+ * Chart with Multiple Axes
+
+ Alloy - diagram-builder-base Demo
- Alloy - diagram-builder-base Demo
AUI().use('aui-diagram-builder', function(A) {
+ var availableFields = [
+ {
+ type: 'node',
+ label: 'Node',
+ iconClass: 'node-icon'
+ },
+ {
+ type: 'task',
+ label: 'Task',
+ iconClass: 'task-icon'
+ }
+ ];
+
+ node = new A.DiagramNode({
+ bodyContent: 'Node',
+ xy: [400, 200],
+ fields: [
+ {},
+ {}
+ ]
+ });
+
diagramBuilder1 = new A.DiagramBuilder(
{
boundingBox: '#diagrambuilderBB',
srcNode: '#diagrambuilderCB',
// height: 600,
- availableFields: [
+ availableFields: availableFields,
+ fields: [
+ node,
{
- type: 'node',
- label: 'My special label',
- iconClass: 'my-icon'
+ bodyContent: 'Node1',
+ xy: [200, 200],
+ fields: [
+ {},
+ {}
+ ]
},
{
- type: 'node',
- label: 'My special second label',
- iconClass: 'my-second-icon',
- draggable: false
+ bodyContent: 'Node2',
+ fields: [
+ {},
+ {}
+ ]
+ },
+ {
+ xy: [600, 200],
+ bodyContent: 'Node3',
+ fields: [
+ {},
+ {},
+ {},
+ {}
+ ]
}
- ],
- fields: [
- { bodyContent: 'Node1', xy: [200, 200] },
- { bodyContent: 'Node2' }
]
}
).render();
@@ -118,24 +250,17 @@ Alloy - diagram-builder-base Demo
// console.log('addField', event);
}
},
- availableFields: [
+ availableFields: availableFields,
+
+ fields: [
{
- type: 'node',
- id: 'myLabel',
- label: 'My label',
- iconClass: 'my-icon'
+ bodyContent: 'Node1',
+ xy: [200, 200]
},
{
- id: 'mySecondLabel',
- label: 'My second label',
- iconClass: 'my-second-icon'
+ bodyContent: 'Node2'
}
],
-
- fields: [
- { bodyContent: 'Node1', xy: [200, 200] },
- { bodyContent: 'Node2' }
- ],
// fields: new A.ArrayList([{a:1}, {a:2}]),
// propertyList: {
@@ -157,7 +282,7 @@ Alloy - diagram-builder-base Demo
}
).render('#diagramBuilder2');
- diagramBuilder2.set('height', 1000);
+ // diagramBuilder2.set('height', 1000);
// diagramBuilder2.set('fields', [1,2]);
// diagramBuilder2.addField({bodyContent:'ABC'});
diff --git a/src/aui-diagram-builder/assets/aui-diagram-builder-base-core.css b/src/aui-diagram-builder/assets/aui-diagram-builder-base-core.css
index b2bddeb1a4a..d07f3fe1cd3 100644
--- a/src/aui-diagram-builder/assets/aui-diagram-builder-base-core.css
+++ b/src/aui-diagram-builder/assets/aui-diagram-builder-base-core.css
@@ -41,7 +41,7 @@
width: 260px;
}
-.aui-diagram-builder-base-content-container {
+.aui-diagram-builder-base-viewport {
background-color: #f5f5f5;
}
@@ -55,7 +55,7 @@
.aui-diagram-node {
position: absolute;
- background-color: #D96666;
+/* background-color: #D96666;*/
}
.aui-diagram-builder-base-drop-container {
diff --git a/src/aui-diagram-builder/build.aui-diagram-builder-connector.properties b/src/aui-diagram-builder/build.aui-diagram-builder-connector.properties
new file mode 100755
index 00000000000..33d9fe2f5ca
--- /dev/null
+++ b/src/aui-diagram-builder/build.aui-diagram-builder-connector.properties
@@ -0,0 +1,10 @@
+# Build Properties
+
+srcdir=../..
+global.src.component=${srcdir}/src/aui-diagram-builder
+global.build.component=${srcdir}/build/aui-diagram-builder
+
+component=aui-diagram-builder-connector
+component.jsfiles=aui-diagram-builder-connector.js
+component.requires=aui-base,arraylist-add,arraylist-filter,json,graphics,dd
+component.skinnable=true
\ No newline at end of file
diff --git a/src/aui-diagram-builder/build.aui-diagram-builder-connector.xml b/src/aui-diagram-builder/build.aui-diagram-builder-connector.xml
new file mode 100755
index 00000000000..054aa366b0e
--- /dev/null
+++ b/src/aui-diagram-builder/build.aui-diagram-builder-connector.xml
@@ -0,0 +1,7 @@
+
+
+',
+ VIEWPORT_TEMPLATE: '',
fieldsNode: null,
propertyList: null,
@@ -421,7 +431,7 @@ var DiagramBuilderBase = A.Component.create(
instance.after(instance._afterUiSetHeight, instance, '_uiSetHeight');
- instance.contentContainer = instance.get(CONTENT_CONTAINER);
+ instance.viewport = instance.get(VIEWPORT);
instance.dropContainer = instance.get(DROP_CONTAINER);
instance.fieldsContainer = instance.get(FIELDS_CONTAINER);
instance.toolbarContainer = instance.get(TOOLBAR_CONTAINER);
@@ -434,14 +444,20 @@ var DiagramBuilderBase = A.Component.create(
return (drag === availableFieldsDrag.dd);
},
- plotFields: function(fields) {
+ plotFields: function() {
+ var instance = this;
+ var fields = instance.get(FIELDS);
+
+ fields.each(function(field) {
+ instance.plotField(field);
+ });
},
renderUI: function() {
var instance = this;
instance._renderTabs();
- instance._renderContentContainer();
+ instance._renderViewport();
instance._uiSetAvailableFields(
instance.get(AVAILABLE_FIELDS)
@@ -497,13 +513,13 @@ var DiagramBuilderBase = A.Component.create(
instance.fire(SAVE);
},
- _renderContentContainer: function() {
+ _renderViewport: function() {
var instance = this;
var contentBox = instance.get(CONTENT_BOX);
- var contentContainer = instance.contentContainer;
+ var viewport = instance.viewport;
- contentContainer.appendChild(instance.dropContainer);
- contentBox.appendChild(contentContainer);
+ viewport.appendChild(instance.dropContainer);
+ contentBox.appendChild(viewport);
},
_renderPropertyList: function() {
@@ -587,6 +603,7 @@ var DiagramBuilderBase = A.Component.create(
return A.merge(
{
bubbleTargets: instance,
+ groups: [AVAILABLE_FIELDS],
node: instance.dropContainer
},
val || {}
@@ -601,6 +618,7 @@ var DiagramBuilderBase = A.Component.create(
bubbleTargets: instance,
container: instance.get(BOUNDING_BOX),
dragConfig: {
+ groups: [AVAILABLE_FIELDS],
plugins: [
{
cfg: {
diff --git a/src/aui-diagram-builder/js/aui-diagram-builder-connector.js b/src/aui-diagram-builder/js/aui-diagram-builder-connector.js
new file mode 100644
index 00000000000..a21ab0e71b4
--- /dev/null
+++ b/src/aui-diagram-builder/js/aui-diagram-builder-connector.js
@@ -0,0 +1,562 @@
+var Lang = A.Lang,
+ isArray = Lang.isArray,
+ isBoolean = Lang.isBoolean,
+ isNumber = Lang.isNumber,
+ isObject = Lang.isObject,
+ isString = Lang.isString,
+
+ YArray = A.Array,
+
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
+ isArrayList = function(val) {
+ return (val instanceof A.ArrayList);
+ },
+
+ ANCHOR = 'anchor',
+ ARROW_POINTS = 'arrowPoints',
+ BODY = 'body',
+ BOUNDING_BOX = 'boundingBox',
+ BUILDER = 'builder',
+ COLOR = 'color',
+ CONNECTOR = 'connector',
+ DATA_ANCHOR = 'dataAnchor',
+ DIAGRAM = 'diagram',
+ DIAGRAM_NODE = 'diagramNode',
+ HEIGHT = 'height',
+ ID = 'id',
+ LAZY_DRAW = 'lazyDraw',
+ MAX_SOURCES = 'maxSources',
+ MAX_TARGETS = 'maxTargets',
+ NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
+ PATH = 'path',
+ SHAPE = 'shape',
+ SOURCES = 'sources',
+ TARGETS = 'targets',
+ VIEWPORT = 'viewport',
+ WIDTH = 'width',
+ WRAPPER = 'wrapper',
+
+ _DOT = '.',
+
+ AgetClassName = A.getClassName,
+
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
+
+A.PolygonUtil = {
+ ARROW_POINTS: [
+ [ -12, -6 ],
+ [ -8, 0 ],
+ [ -12, 6 ],
+ [ 6, 0 ]
+ ],
+
+ drawLineArrow: function(shape, x1, y1, x2, y2, arrowPoints) {
+ var instance = this;
+
+ shape.moveTo(x1, y1);
+ shape.lineTo(x2, y2);
+
+ var angle = Math.atan2(y2-y1, x2-x1), centerX = (x2+x1)/2, centerY = (y2+y1)/2;
+
+ instance.drawPolygon(
+ shape,
+ instance.translatePoints(instance.rotatePoints(arrowPoints || instance.ARROW_POINTS, angle), centerX, centerY)
+ );
+ },
+
+ drawPolygon: function(shape, points) {
+ var instance = this;
+
+ shape.moveTo(points[0][0], points[0][1]);
+
+ YArray.each(points, function(p, i) {
+ if (i > 0) {
+ shape.lineTo(points[i][0], points[i][1]);
+ }
+ });
+
+ shape.lineTo(points[0][0], points[0][1]);
+ shape.end();
+ },
+
+ translatePoints: function(points, x, y) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push([ points[i][0] + x, points[i][1] + y ]);
+ });
+
+ return xy;
+ },
+
+ rotatePoints: function(points, angle) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push(instance.rotatePoint(angle, points[i][0], points[i][1]));
+ });
+
+ return xy;
+ },
+
+ rotatePoint: function(angle, x, y) {
+ return [
+ (x * Math.cos(angle)) - (y * Math.sin(angle)),
+ (x * Math.sin(angle)) + (y * Math.cos(angle))
+ ];
+ }
+};
+
+A.Connector = A.Base.create('line', A.Base, [], {
+ graphics: null,
+ shape: null,
+
+ initializer: function(config) {
+ var instance = this;
+
+ instance.after({
+ p1Change: instance.draw,
+ p2Change: instance.draw
+ });
+
+ instance._initGraphics();
+ instance._initShapes();
+
+ if (!instance.get(LAZY_DRAW)) {
+ instance.draw();
+ }
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.graphics.destroy();
+ },
+
+ draw: function() {
+ var instance = this;
+ var shape = instance.shape;
+
+ var c1 = instance.getCoordinate(instance.get(P1));
+ var c2 = instance.getCoordinate(instance.get(P2));
+
+ shape.clear();
+
+ A.PolygonUtil.drawLineArrow(shape, c1[0], c1[1], c2[0], c2[1], instance.get(ARROW_POINTS));
+ },
+
+ getCoordinate: function(p) {
+ var instance = this;
+ var xy = instance.get(VIEWPORT).getXY();
+
+ return [ p[0] - xy[0], p[1] - xy[1] ];
+ },
+
+ _initGraphics: function() {
+ var instance = this;
+
+ var graphics = new A.Graphic({
+ width: instance.get(WIDTH),
+ height: instance.get(HEIGHT),
+ render: instance.get(VIEWPORT)
+ });
+
+ instance.graphics = graphics;
+ },
+
+ _initShapes: function() {
+ var instance = this;
+
+ instance.shape = instance.graphics.getShape(
+ instance.get(SHAPE)
+ );
+ },
+
+ _setShape: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ type: PATH,
+ stroke: {
+ color: instance.get(COLOR),
+ weight: 2
+ },
+ fill: {
+ color: instance.get(COLOR)
+ }
+ },
+ val
+ );
+ }
+},{
+ ATTRS: {
+ color: {
+ value: '#666',
+ validator: isString
+ },
+
+ lazyDraw: {
+ value: false,
+ validator: isBoolean
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ },
+
+ shape: {
+ value: null,
+ setter: '_setShape'
+ },
+
+ arrowPoints: {
+ value: A.PolygonUtil.ARROW_POINTS
+ },
+
+ p1: {
+ value: [0, 0],
+ validator: isArray
+ },
+
+ p2: {
+ value: [0, 0],
+ validator: isArray
+ }
+ }
+});
+
+A.Anchor = A.Base.create('anchor', A.Base, [], {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ NODE_TEMPLATE: '',
+
+ connectors: null,
+
+ initializer: function() {
+ var instance = this;
+
+ instance.connectors = {};
+
+ instance._renderNode();
+
+ instance.connectTargets();
+
+ instance.after({
+ targetsChange: instance._afterTargetsChange
+ });
+ },
+
+ addSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ },
+
+ addTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ },
+
+ alignConnectors: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ var tConnector = instance.getConnector(target);
+
+ if (tConnector) {
+ tConnector.set(P1, instance.getCenterXY());
+ tConnector.set(P2, target.getCenterXY());
+ }
+ });
+
+ instance.get(SOURCES).each(function(source) {
+ var sConnector = source.getConnector(instance);
+
+ if (sConnector) {
+ sConnector.set(P1, source.getCenterXY());
+ sConnector.set(P2, instance.getCenterXY());
+ }
+ });
+
+ return instance;
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.disconnectTargets();
+ instance.disconnectSources();
+
+ instance.get(NODE).remove();
+ },
+
+ connect: function(target) {
+ var instance = this;
+
+ instance.addTarget(target);
+
+ if (!instance.isConnected(target)) {
+ var tConnector = target.get(CONNECTOR);
+
+ tConnector.p1 = instance.getCenterXY();
+ tConnector.p2 = target.getCenterXY();
+
+ instance.connectors[target.get(ID)] = new A.Connector(tConnector);
+ }
+
+ return instance;
+ },
+
+ connectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(A.bind(instance.connect, instance));
+
+ return instance;
+ },
+
+ disconnect: function(target) {
+ var instance = this;
+
+ instance.getConnector(target).destroy();
+
+ instance.removeTarget(target);
+ target.removeSource(instance);
+ },
+
+ disconnectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ instance.disconnect(target);
+ });
+
+ return instance;
+ },
+
+ disconnectSources: function() {
+ var instance = this;
+
+ instance.get(SOURCES).each(function(source) {
+ source.disconnect(instance);
+ });
+
+ return instance;
+ },
+
+ getCenterXY: function() {
+ var instance = this;
+
+ return instance.get(NODE).getCenterXY();
+ },
+
+ getConnector: function(target) {
+ var instance = this;
+
+ return instance.connectors[target.get(ID)];
+ },
+
+ isConnected: function(target) {
+ var instance = this;
+
+ return instance.connectors.hasOwnProperty(target.get(ID));
+ },
+
+ updateSources: function(sources) {
+ var instance = this;
+
+ instance.set(SOURCES, sources);
+
+ return instance;
+ },
+
+ updateTargets: function(targets) {
+ var instance = this;
+
+ instance.set(TARGETS, targets);
+
+ return instance;
+ },
+
+ removeSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source)
+ );
+ },
+
+ removeTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target)
+ );
+ },
+
+ _afterActiveChange: function(event) {
+ var instance = this;
+
+ instance._uiSetActive(event.newVal);
+ },
+
+ _afterTargetsChange: function(event) {
+ var instance = this;
+
+ // TODO - event.prevVal is always equal to event.newVal, review this
+ // logic below, references between anchors needs to be cleaned up otherwise
+ // will store the wrong relationship between nodes.
+
+ event.prevVal.each(function(anchor) {
+ anchor.removeSource(instance);
+ });
+
+ event.newVal.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+ },
+
+ _renderNode: function() {
+ var instance = this;
+ var diagramNode = instance.get(DIAGRAM_NODE);
+ var container = diagramNode.get(BOUNDING_BOX);
+
+ instance.wrapper = container.one(_DOT+CSS_DB_ANCHOR_NODE_WRAPPER) ||
+ A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE);
+
+ instance.wrapper.appendTo(container).appendChild(instance.get(NODE));
+ },
+
+ _setConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ viewport: instance.get(VIEWPORT)
+ },
+ val
+ );
+ },
+
+ _setSources: function(val) {
+ var instance = this;
+
+ return instance._setAnchors(val);
+ },
+
+ _setTargets: function(val) {
+ var instance = this;
+
+ val = instance._setAnchors(val);
+
+ val.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+
+ return val;
+ },
+
+ _setAnchors: function(val) {
+ var instance = this;
+
+ if (!isArrayList(val)) {
+ var targets = [];
+
+ A.Array.each(val, function(target) {
+ if (isString(target)) {
+ // TODO - need this?
+ target = A.Anchor.getAnchorByNode(target);
+ }
+
+ targets.push( isAnchor(target) ? target : new A.Anchor(target) );
+ });
+
+ val = new A.ArrayList(targets);
+ }
+
+ return val;
+ },
+
+ _setNode: function(val) {
+ var instance = this;
+ var id = instance.get(ID);
+
+ return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ }
+},{
+ ATTRS: {
+ diagramNode: {
+ },
+
+ connector: {
+ setter: '_setConnector',
+ value: {},
+ validator: isObject
+ },
+
+ id: {
+ readOnly: true,
+ valueFn: function() {
+ return A.guid();
+ }
+ },
+
+ maxSources: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ maxTargets: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ node: {
+ setter: '_setNode',
+ valueFn: function() {
+ var instance = this;
+
+ return A.Node.create(instance.NODE_TEMPLATE);
+ }
+ },
+
+ sources: {
+ value: [],
+ setter: '_setSources',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ targets: {
+ value: [],
+ setter: '_setTargets',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ }
+ },
+
+ getAnchorByNode: function(node) {
+ return A.one(node).getData(DATA_ANCHOR);
+ }
+});
\ No newline at end of file
diff --git a/src/aui-diagram-builder/js/aui-diagram-builder-impl.js b/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
index 881b74ba39f..0a02c774f34 100644
--- a/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
+++ b/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
@@ -2,6 +2,7 @@ var Lang = A.Lang,
isArray = Lang.isArray,
isObject = Lang.isObject,
isString = Lang.isString,
+ isBoolean = Lang.isBoolean,
AArray = A.Array,
@@ -13,6 +14,10 @@ var Lang = A.Lang,
return (val instanceof A.DiagramNode);
},
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
getLeftTop = function(container, node) {
var nodeXY = isArray(node) ? node : node.getXY();
var containerXY = isArray(container) ? container : container.getXY();
@@ -22,44 +27,123 @@ var Lang = A.Lang,
});
},
+ ANCHOR = 'anchor',
+ ANCHORS = 'anchors',
+ ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
+ CANCEL = 'cancel',
+ CLICK = 'click',
+ CONTENT = 'content',
+ CONTROLS = 'controls',
+ CONTROLS_TOOLBAR = 'controlsToolbar',
DATA = 'data',
DBLCLICK = 'dblclick',
+ DELETE = 'delete',
+ DELETE_MESSAGE = 'deleteMessage',
DESCRIPTION = 'description',
DIAGRAM = 'diagram',
- DIAGRAM_BUILDER = 'diagram-builder',
- DIAGRAM_NODE = 'diagram-node',
+ DIAGRAM_BUILDER_NAME = 'diagram-builder',
+ DIAGRAM_NODE = 'diagramNode',
+ DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ ESC = 'esc',
+ FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
+ HOVER = 'hover',
+ KEYDOWN = 'keydown',
+ LINK = 'link',
+ MOUSEENTER = 'mouseenter',
+ MOUSELEAVE = 'mouseleave',
NAME = 'name',
NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
PARENT_NODE = 'parentNode',
RECORDS = 'records',
RECORDSET = 'recordset',
+ REGION = 'region',
RENDERED = 'rendered',
+ SELECTED = 'selected',
+ SHUFFLE = 'shuffle',
+ TASK = 'task',
+ TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
+ VIEWPORT = 'viewport',
+ WRAPPER = 'wrapper',
XY = 'xy',
_DOT = '.',
+ _DOLLAR = '$',
_EMPTY_STR = '',
+ _DASH = '-',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_CONTROLS = AgetClassName(DIAGRAM, BUILDER, CONTROLS),
+ CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE),
+ CSS_DIAGRAM_NODE_CONTENT = AgetClassName(DIAGRAM, NODE, CONTENT),
CSS_DIAGRAM_NODE_EDITING = AgetClassName(DIAGRAM, NODE, EDITING),
- CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE);
+ CSS_DIAGRAM_NODE_SELECTED = AgetClassName(DIAGRAM, NODE, SELECTED);
+
+// REMOVE THIS!
+var __dump = function() {
+ var PAD = ' ', BR = '
';
+
+ A.all('.aui-diagram-node').each(function(n) {
+ var b = _EMPTY_STR,
+ dn = A.Widget.getByNode(n),
+ dnName = dn.get('name'),
+ dnBB = dn.get('boundingBox'),
+ log = dnBB.one('.log') || A.Node.create('').appendTo(dnBB);
+
+ b += dnName + BR;
+
+ dn.get(FIELDS).each(function(a) {
+ b += PAD + 'a: ' + a.get('id') + BR;
+
+ a.get('targets').each(function(t) {
+ var tdn = t.get(DIAGRAM_NODE);
+
+ t.get('node').setContent(t.get('id'));
+
+ b += PAD + PAD + 't: ' + tdn.get('name') + ' (s: ' + t.get('id') + ')' + BR;
+ });
+
+ a.get('sources').each(function(s) {
+ var sdn = s.get(DIAGRAM_NODE);
+
+ s.get('node').setContent(s.get('id'));
+
+ b += PAD + PAD + 's: ' + sdn.get('name') + ' (t: ' + s.get('id') + ')' + BR;
+ });
+ });
+
+ log.setContent(b);
+ });
+};
+// END.
var DiagramBuilder = A.Component.create({
- NAME: DIAGRAM_BUILDER,
+ NAME: DIAGRAM_BUILDER_NAME,
ATTRS: {
fieldsDragConfig: {
value: null,
setter: '_setFieldsDragConfig',
validator: isObject
+ },
+
+ tmpConnector: {
+ setter: '_setTmpConnector',
+ value: {},
+ validator: isObject
}
},
@@ -76,12 +160,18 @@ var DiagramBuilder = A.Component.create({
instance.on({
cancel: instance._onCancel,
+ 'drag:drag': instance._onDrag,
'drag:end': instance._onDragEnd,
'drop:hit': instance._onDropHit,
save: instance._onSave
});
+ instance.handlerKeyDown = A.getDoc().on(KEYDOWN, A.bind(instance._afterKeyEvent, instance));
+
+ instance.dropContainer.delegate(CLICK, A.bind(instance._onNodeClick, instance), _DOT+CSS_DIAGRAM_NODE);
instance.dropContainer.delegate(DBLCLICK, A.bind(instance._onNodeEdit, instance), _DOT+CSS_DIAGRAM_NODE);
+ instance.dropContainer.delegate(MOUSEENTER, A.bind(instance._onMouseenterAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
+ instance.dropContainer.delegate(MOUSELEAVE, A.bind(instance._onMouseleaveAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
},
syncUI: function() {
@@ -90,12 +180,17 @@ var DiagramBuilder = A.Component.create({
A.DiagramBuilder.superclass.syncUI.apply(this, arguments);
instance._setupFieldsDrag();
+
+ instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
createField: function(val) {
var instance = this;
if (!isDiagramNode(val)) {
+ // val.bubbleTargets = instance;
+ val.builder = instance;
+ val.viewport = instance.get(VIEWPORT);
val = new (instance.getFieldClass(val.type || NODE))(val);
}
@@ -125,21 +220,31 @@ var DiagramBuilder = A.Component.create({
return (drag === fieldsDrag.dd);
},
- plotFields: function() {
+ plotField: function(field) {
var instance = this;
- var fields = instance.get(FIELDS);
- fields.each(function(field) {
- instance.plotField(field);
- });
+ if (!field.get(RENDERED)) {
+ field.render(instance.dropContainer);
+ }
},
- plotField: function(field) {
+ unselectAll: function() {
var instance = this;
+ var selectedNode = instance.selectedNode;
- if (!field.get(RENDERED)) {
- field.render(instance.dropContainer);
+ if (selectedNode) {
+ selectedNode.set(SELECTED, false);
}
+
+ instance.selectedNode = null;
+ },
+
+ select: function(diagramNode) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
startEditingNode: function(diagramNode) {
@@ -150,8 +255,6 @@ var DiagramBuilder = A.Component.create({
instance.tabView.selectTab(A.DiagramBuilder.SETTINGS_TAB);
- // instance._renderPropertyList();
-
instance.propertyList.set(RECORDSET, diagramNode.getProperties());
diagramNode.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE_EDITING);
@@ -173,12 +276,42 @@ var DiagramBuilder = A.Component.create({
}
},
+ _afterKeyEvent: function(event) {
+ var instance = this;
+
+ if (!instance.selectedNode || event.hasModifier() || !event.isKeyInSet(ESC, DELETE)) {
+ return;
+ }
+
+ if (event.isKey(ESC)) {
+ instance._onEscKey(event);
+ }
+ else if (event.isKey(DELETE)) {
+ instance._onDeleteKey(event);
+ }
+
+ event.halt();
+ },
+
_onCancel: function(event) {
var instance = this;
instance.stopEditingNode();
},
+ _onDrag: function(event) {
+ var instance = this;
+ var drag = event.target;
+
+ if (instance.isFieldsDrag(drag)) {
+ var diagramNode = A.Widget.getByNode(drag.get(DRAG_NODE));
+
+ diagramNode.get(FIELDS).each(function(anchor) {
+ anchor.alignConnectors();
+ });
+ }
+ },
+
_onDragEnd: function(event) {
var instance = this;
var drag = event.target;
@@ -197,15 +330,56 @@ var DiagramBuilder = A.Component.create({
if (instance.isAvailableFieldsDrag(drag)) {
var availableField = drag.get(NODE).getData(AVAILABLE_FIELD);
- instance.addField({
+ var newField = instance.addField({
xy: getLeftTop(drag.lastXY, instance.dropContainer),
- type: availableField.get(TYPE)
+ type: availableField.get(TYPE),
+ fields: [{}]
});
+
+ instance.select(newField);
}
},
+ _onDeleteKey: function(event) {
+ var instance = this;
+
+ instance.selectedNode.close();
+ },
+
+ _onEscKey: function(event) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ },
+
+ _onMouseenterAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.addClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onMouseleaveAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.removeClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onNodeClick: function(event) {
+ var instance = this;
+ var diagramNode = A.Widget.getByNode(event.currentTarget);
+
+ instance.select(diagramNode);
+ },
+
_onNodeEdit: function(event) {
var instance = this;
+
+ // Only enable editing if the double clicked node is inside the node contentBox.
+ if (!event.target.ancestor(_DOT+CSS_DIAGRAM_NODE_CONTENT, true)) {
+ return;
+ }
+
var diagramNode = A.Widget.getByNode(event.currentTarget);
if (diagramNode) {
@@ -229,6 +403,18 @@ var DiagramBuilder = A.Component.create({
}
},
+ _setTmpConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ lazyDraw: true,
+ viewport: instance.viewport
+ },
+ val
+ );
+ },
+
_setFieldsDragConfig: function(val) {
var instance = this;
var dropContainer = instance.dropContainer;
@@ -273,11 +459,32 @@ A.DiagramBuilder = DiagramBuilder;
A.DiagramBuilder.types = {};
+var DiagramNodeOverlay = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ EXTENDS: A.Overlay,
+
+ // A.FieldSupport augment the class with "fields" attribute and util methods
+ // such as: addField, removeField. Although the attribute is called "fields" due to
+ // the augmentation, those fields are the anchors. TODO: Allow A.FieldSupport to
+ // customize the name of the attribute and method sufixes.
+ AUGMENTS: [A.FieldSupport]
+});
+
var DiagramNode = A.Component.create({
- NAME: DIAGRAM_NODE,
+ NAME: DIAGRAM_NODE_NAME,
+
+ UI_ATTRS: [FIELDS, NAME, SELECTED],
ATTRS: {
+ anchorsDragConfig: {
+ value: null,
+ setter: '_setAnchorsDragConfig',
+ validator: isObject
+ },
+
builder: {
+ setter: '_setBuilder',
validator: isDiagramBuilder
},
@@ -287,7 +494,7 @@ var DiagramNode = A.Component.create({
},
height: {
- value: 100
+ value: 90
},
name: {
@@ -299,8 +506,14 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ selected: {
+ value: false,
+ validator: isBoolean
+ },
+
strings: {
value: {
+ deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
name: 'Name',
type: 'Type'
@@ -312,14 +525,114 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ controlsToolbar: {
+ setter: '_setControlsToolbar',
+ validator: isObject,
+ value: null
+ },
+
width: {
- value: 200
+ value: 90
+ },
+
+ zIndex: {
+ value: 100
+ },
+
+ tabIndex: {
+ value: 1
}
},
- EXTENDS: A.Overlay,
+ EXTENDS: DiagramNodeOverlay,
+
+ buildNodeId: function(id) {
+ return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ },
prototype: {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ CONTROLS_TEMPLATE: '',
+
+ initializer: function() {
+ var instance = this;
+
+ instance._renderNodes();
+ instance._setupAnchorsDrag();
+
+ instance.after({
+ render: instance._afterRender
+ });
+
+ instance.on({
+ 'drag:drag': instance._onAnchorDrag,
+ 'drag:end': instance._onAnchorDragEnd,
+ 'drag:start': instance._onAnchorDragStart,
+ 'drop:hit': instance._onAnchorDropHit
+ });
+
+ instance.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE+_DASH+instance.get(TYPE));
+
+ // REMOVE THIS!
+ instance.set('bodyContent', instance.get(NAME));
+ },
+
+ alignAnchors: function() {
+ var instance = this;
+ var anchors = instance.get(FIELDS);
+
+ var cRegion = instance.get(BOUNDING_BOX).get(REGION),
+ dAngle = Math.floor(360/anchors.size()),
+ a = cRegion.width/2,
+ b = cRegion.height/2,
+ centerX = cRegion.left + cRegion.width/2,
+ centerY = cRegion.top + cRegion.height/2;
+
+ anchors.each(function(anchor, index) {
+ var anchorNode = anchor.get(NODE);
+ var aRegion = anchorNode.get(REGION);
+ var exy = instance._getEllipseXY(a, b, centerX, centerY, index*dAngle);
+
+ anchorNode.setXY([ exy[0] - aRegion.width/2, exy[1] - aRegion.height/2 ]);
+
+ anchor.alignConnectors();
+ });
+
+ return instance;
+ },
+
+ close: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ if (confirm(strings[DELETE_MESSAGE])) {
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.destroy();
+ });
+
+ instance.destroy();
+ }
+
+ __dump();
+
+ return instance;
+ },
+
+ createField: function(val) {
+ var instance = this;
+
+ if (!isAnchor(val)) {
+ var builder = instance.get(BUILDER);
+
+ val.diagramNode = instance;
+ val.viewport = (builder ? builder.get(VIEWPORT) : null);
+
+ val = new A.Anchor(val);
+ }
+
+ return val;
+ },
+
getLeftTop: function() {
var instance = this;
@@ -368,12 +681,225 @@ var DiagramNode = A.Component.create({
];
},
+ _afterRender: function(event) {
+ var instance = this;
+
+ instance.alignAnchors();
+
+ instance._renderControls();
+ },
+
_getContainer: function() {
var instance = this;
return (instance.get(BUILDER).dropContainer || instance.get(BOUNDING_BOX).get(PARENT_NODE));
},
+ _getEllipseXY: function(a, b, centerX, centerY, angle) {
+ var t = angle*Math.PI/180;
+
+ return [ centerX + a*Math.cos(t), centerY - b*Math.sin(t) ];
+ },
+
+ _handleAddAnchorEvent: function(event) {
+ var instance = this;
+
+ instance.addField({});
+
+ // event.halt();
+ },
+
+ _handleAddTaskEvent: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ var diagramNode = new A.DiagramNode({
+ type: NODE,
+ xy: [100, 100] // TODO - find best position?
+ });
+
+ builder.addField(diagramNode);
+
+ var source = instance.addField({});
+ var target = diagramNode.addField({});
+ source.connect(target);
+ },
+
+ _handleCloseEvent: function(event) {
+ var instance = this;
+
+ instance.close();
+ },
+
+ _onAnchorDrag: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P2, event.target.get(DRAG_NODE).getCenterXY());
+ },
+
+ _onAnchorDragEnd: function(event) {
+ var instance = this;
+ var shape = instance.get(BUILDER).tmpConnector.shape;
+
+ shape.clear();
+ shape.end();
+ },
+
+ _onAnchorDragStart: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P1, event.target.get(NODE).getCenterXY());
+ },
+
+ _onAnchorDropHit: function(event) {
+ var instance = this;
+ var source = A.Anchor.getAnchorByNode(event.drag.get(NODE));
+ var target = A.Anchor.getAnchorByNode(event.drop.get(NODE));
+
+ source.connect(target);
+
+ __dump();
+ },
+
+ _renderControls: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.controlsNode = A.Node.create(instance.CONTROLS_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderNodes: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.anchorWrapper = A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderControlsToolbar: function(event) {
+ var instance = this;
+
+ instance.controlsToolbar = new A.Toolbar(
+ instance.get(CONTROLS_TOOLBAR)
+ )
+ .render(instance.controlsNode);
+ },
+
+ _setBuilder: function(val) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.set(VIEWPORT, val.get(VIEWPORT));
+ });
+
+ return val;
+ },
+
+ _setAnchorsDragConfig: function(val) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ return A.merge(
+ {
+ bubbleTargets: instance,
+ container: instance.anchorWrapper,
+ dragConfig: {
+ groups: [ANCHORS],
+ plugins: [
+ {
+ cfg: {
+ constrain: (builder ? builder.get(VIEWPORT) : null)
+ },
+ fn: A.Plugin.DDConstrained
+ },
+ {
+ cfg: {
+ scrollDelay: 150
+ },
+ fn: A.Plugin.DDWinScroll
+ },
+ {
+ cfg: {
+ moveOnEnd: false
+ },
+ fn: A.Plugin.DDProxy
+ }
+ ]
+ },
+ nodes: _DOT+CSS_DB_ANCHOR_NODE,
+ target: true
+ },
+ val || {}
+ );
+ },
+
+ _setupAnchorsDrag: function() {
+ var instance = this;
+
+ instance.anchorsDrag = new A.DD.Delegate(
+ instance.get(ANCHORS_DRAG_CONFIG)
+ );
+ },
+
+ _setControlsToolbar: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK
+ },
+ {
+ handler: A.bind(instance._handleAddTaskEvent, instance),
+ icon: SHUFFLE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL
+ }
+ ]
+ },
+ val
+ );
+ },
+
+ _uiSetFields: function(val) {
+ var instance = this;
+
+ if (instance.get(RENDERED)) {
+ instance.alignAnchors();
+
+ setTimeout(function() {
+ instance.anchorsDrag.syncTargets();
+ }, 50);
+ }
+ },
+
+ _uiSetName: function(val) {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetSelected: function(val) {
+ var instance = this;
+
+ instance.get(BOUNDING_BOX).toggleClass(CSS_DIAGRAM_NODE_SELECTED, val);
+
+ if (val && !instance.controlsToolbar) {
+ instance._renderControlsToolbar();
+ }
+
+ // if (instance.get(RENDERED)) {
+ // instance.alignAnchors();
+ // }
+ },
+
_uiSetXY : function(val) {
var instance = this;
var containerXY = instance._getContainer().getXY();
@@ -383,10 +909,30 @@ var DiagramNode = A.Component.create({
}
});
-DiagramNode.buildNodeId = function(id) {
- return DIAGRAM_NODE + _DOLLAR + FIELD + _DOLLAR + id;
-};
-
A.DiagramNode = DiagramNode;
-A.DiagramBuilder.types['node'] = A.DiagramNode;
\ No newline at end of file
+A.DiagramBuilder.types[NODE] = A.DiagramNode;
+
+A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNode
+});
+
+A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+// TODO deletar anchors OK
+// TODO deletar connections (delete) OK
+// TODO Adicionar overlay de controles OK
+// TODO syncTargets dd delegate
+
+
+// TODO gerar XML
+// TODO reposicionar setas?
+// TODO Adicionar groups/validation for connection
\ No newline at end of file
From 680a489bddec733f005debdda2a88166248ffc77 Mon Sep 17 00:00:00 2001
From: Eduardo Lundgren ',
+ VIEWPORT_TEMPLATE: '',
fieldsNode: null,
propertyList: null,
@@ -422,7 +432,7 @@ var DiagramBuilderBase = A.Component.create(
instance.after(instance._afterUiSetHeight, instance, '_uiSetHeight');
- instance.contentContainer = instance.get(CONTENT_CONTAINER);
+ instance.viewport = instance.get(VIEWPORT);
instance.dropContainer = instance.get(DROP_CONTAINER);
instance.fieldsContainer = instance.get(FIELDS_CONTAINER);
instance.toolbarContainer = instance.get(TOOLBAR_CONTAINER);
@@ -435,14 +445,20 @@ var DiagramBuilderBase = A.Component.create(
return (drag === availableFieldsDrag.dd);
},
- plotFields: function(fields) {
+ plotFields: function() {
+ var instance = this;
+ var fields = instance.get(FIELDS);
+
+ fields.each(function(field) {
+ instance.plotField(field);
+ });
},
renderUI: function() {
var instance = this;
instance._renderTabs();
- instance._renderContentContainer();
+ instance._renderViewport();
instance._uiSetAvailableFields(
instance.get(AVAILABLE_FIELDS)
@@ -498,13 +514,13 @@ var DiagramBuilderBase = A.Component.create(
instance.fire(SAVE);
},
- _renderContentContainer: function() {
+ _renderViewport: function() {
var instance = this;
var contentBox = instance.get(CONTENT_BOX);
- var contentContainer = instance.contentContainer;
+ var viewport = instance.viewport;
- contentContainer.appendChild(instance.dropContainer);
- contentBox.appendChild(contentContainer);
+ viewport.appendChild(instance.dropContainer);
+ contentBox.appendChild(viewport);
},
_renderPropertyList: function() {
@@ -588,6 +604,7 @@ var DiagramBuilderBase = A.Component.create(
return A.merge(
{
bubbleTargets: instance,
+ groups: [AVAILABLE_FIELDS],
node: instance.dropContainer
},
val || {}
@@ -602,6 +619,7 @@ var DiagramBuilderBase = A.Component.create(
bubbleTargets: instance,
container: instance.get(BOUNDING_BOX),
dragConfig: {
+ groups: [AVAILABLE_FIELDS],
plugins: [
{
cfg: {
diff --git a/build/aui-diagram-builder/aui-diagram-builder-base-min.js b/build/aui-diagram-builder/aui-diagram-builder-base-min.js
index 460835bde8b..03a1704328a 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-base-min.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-base-min.js
@@ -1,2 +1,2 @@
-AUI.add("aui-diagram-builder-base",function(ab){var R=ab.Lang,d=R.isArray,ao=R.isBoolean,K=R.isNumber,z=R.isObject,ar=R.isString,H=function(A){return(A instanceof ab.ArrayList);},P=function(A){return(A instanceof ab.Node);},C=function(A){return(A instanceof ab.AvailableField);},aC=ab.Array,T="add",k="addNode",aB="auto",L="availableField",O="availableFields",ay="availableFieldsDragConfig",al="base",s="boundingBox",av="builder",X="cancel",ap="clearfix",a="container",Z="content",u="contentBox",e="contentContainer",N="contentNode",D="createDocumentFragment",y="diagram",E="diagram-builder-base",Y="disk",o="draggable",ax="drop",ai="dropConfig",V="dropContainer",an="field",t="fields",n="fieldsContainer",am="height",p="helper",U="icon",v="iconClass",ah="id",ad="label",af="list",r="node",x="nodeSettings",aa="propertyList",aw="rendered",aj="save",q="settings",M="tab",b="tabs",f="tabview",F="tabView",J="toolbar",j="toolbarContainer",w=ab.getClassName,aA=" ",h=".",G="$",aD=w(y,av,al,ax,a),ak=w(y,av,al,Z,a),B=w(y,av,al,an),g=w(y,av,al,t,a),ae=w(y,av,al,an,o),c=w(y,av,al,an,U),S=w(y,av,al,an,ad),m=w(y,av,al,b,a),W=w(y,av,al,b,a,Z),ag=w(y,av,al,M,T),I=w(y,av,al,M,q),at=w(y,av,al,J,a),ac=w(p,ap),l=w(U),au=w(f,Z),az=w(f,af);var i=ab.Component.create({NAME:L,ATTRS:{draggable:{value:true,validator:ao},label:{validator:ar},iconClass:{validator:ar},id:{value:ab.guid(),setter:"_setId",validator:ar},node:{valueFn:function(aE){var A=this;if(!P(aE)){aE=ab.Node.create(ab.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aE.setData(L,A);}return aE;},validator:P,writeOnce:true},type:{value:r,validator:ar}},EXTENDS:ab.Base,prototype:{FIELD_ITEM_TEMPLATE:'
',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.contentContainer=A.get(e);A.dropContainer=A.get(V);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aF){var A=this;var aE=A.availableFieldsDrag;return(aF===aE.dd);},plotFields:function(A){},renderUI:function(){var A=this;A._renderTabs();A._renderContentContainer();A._uiSetAvailableFields(A.get(O));},syncUI:function(){var A=this;var aE=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aE.addClass(ac);},_afterActiveTabChange:function(aF){var A=this;var aE=aF.newVal.get(N);if(A.get(aw)&&(aE===A.settingsNode)){A._renderSettings();}},_afterRender:function(aE){var A=this;A.plotFields();},_afterUiSetHeight:function(aE){var A=this;A.dropContainer.setStyle(am,K(aE)?aE+A.DEF_UNIT:aE);},_defCancelFn:function(aE){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(X);},_handleSaveEvent:function(){var A=this;A.fire(aj);},_renderContentContainer:function(){var A=this;var aE=A.get(u);var aF=A.contentContainer;aF.appendChild(A.dropContainer);aE.appendChild(aF);},_renderPropertyList:function(){var A=this;if(!A.propertyList){A.propertyList=new ab.PropertyList(A.get(aa)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();
-},_renderTabs:function(){var A=this;if(!A.tabView){var aE=new ab.TabView(A.get(F));A.tabView=aE;A.fieldsNode=aE.getTab(0).get(N);A.settingsNode=aE.getTab(1).get(N);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ab.Toolbar(A.get(J)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ab.DD.Drop(A.get(ai));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ab.DD.Delegate(A.get(ay));},_setAvailableFields:function(aF){var aE=this;var A=[];aC.each(aF,function(aH,aG){A.push(C(aH)?aH:new ab.AvailableField(aH));});return A;},_setDropConfig:function(aE){var A=this;return ab.merge({bubbleTargets:A,node:A.dropContainer},aE||{});},_setAvailableFieldsDragConfig:function(aE){var A=this;return ab.merge({bubbleTargets:A,container:A.get(s),dragConfig:{plugins:[{cfg:{moveOnEnd:false},fn:ab.Plugin.DDProxy}]},nodes:h+ae},aE||{});},_setPropertyList:function(aE){var A=this;return ab.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aB}},aE);},_setTabView:function(aH){var aE=this;var aG=aE.get(s);var aI=aG.one(h+az);var aF={after:{activeTabChange:ab.bind(aE._afterActiveTabChange,aE)},boundingBox:aG.one(h+m),contentBox:aG.one(h+W),bubbleTargets:aE,contentNode:aG.one(h+au),cssClass:m,listNode:aI,render:aE.get(u)};if(!aI){var A=aE.getStrings();aF.items=[{cssClass:ag,label:A[k]},{cssClass:I,label:A[x]}];}return ab.merge(aF,aH);},_setToolbar:function(aF){var aE=this;var A=aE.getStrings();return ab.merge({activeState:false,bubbleTargets:aE,children:[{handler:ab.bind(aE._handleSaveEvent,aE),label:A[aj],icon:Y},{handler:ab.bind(aE._handleCancelEvent,aE),label:A[X]}]},aF);},_uiSetAvailableFields:function(aG){var A=this;var aF=A.fieldsNode;if(aF){var aE=ab.getDoc().invoke(D);aC.each(aG,function(aH){aE.appendChild(aH.get(r));});aF.setContent(A.fieldsContainer.setContent(aE));}},_uiSetFields:function(aE){var A=this;if(A.get(aw)){A.plotFields();}}}});ab.DiagramBuilderBase=aq;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-base",function(ad){var T=ad.Lang,d=T.isArray,ap=T.isBoolean,M=T.isNumber,B=T.isObject,at=T.isString,I=function(A){return(A instanceof ad.ArrayList);},R=function(A){return(A instanceof ad.Node);},D=function(A){return(A instanceof ad.AvailableField);},aD=ad.Array,V="add",k="addNode",aC="auto",N="availableField",Q="availableFields",az="availableFieldsDragConfig",am="base",s="boundingBox",aw="builder",Z="cancel",aq="clearfix",a="container",ab="content",u="contentBox",J="viewport",P="contentNode",E="createDocumentFragment",z="diagram",F="diagram-builder-base",aa="disk",o="draggable",ay="drop",ak="dropConfig",X="dropContainer",ao="field",t="fields",n="fieldsContainer",an="height",p="helper",W="icon",v="iconClass",aj="id",af="label",ai="list",r="node",y="nodeSettings",ac="propertyList",ax="rendered",al="save",q="settings",O="tab",b="tabs",e="tabview",G="tabView",L="toolbar",j="toolbarContainer",w=ad.getClassName,aB=" ",g=".",H="$",h="#",aE=w(z,aw,am,ay,a),x=w(z,aw,am,J),C=w(z,aw,am,ao),f=w(z,aw,am,t,a),ag=w(z,aw,am,ao,o),c=w(z,aw,am,ao,W),U=w(z,aw,am,ao,af),m=w(z,aw,am,b,a),Y=w(z,aw,am,b,a,ab),ah=w(z,aw,am,O,V),K=w(z,aw,am,O,q),au=w(z,aw,am,L,a),ae=w(p,aq),l=w(W),av=w(e,ab),aA=w(e,ai);var i=ad.Component.create({NAME:N,ATTRS:{draggable:{value:true,validator:ap},label:{validator:at},iconClass:{validator:at},id:{value:ad.guid(),setter:"_setId",validator:at},node:{valueFn:function(aF){var A=this;if(!R(aF)){aF=ad.Node.create(ad.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aF.setData(N,A);}return aF;},validator:R,writeOnce:true},type:{value:r,validator:at}},EXTENDS:ad.Base,buildNodeId:function(A){return Q+H+ao+H+A;},getAvailableFieldByNode:function(A){return ad.one(A).getData(N);},getAvailableFieldById:function(A){return ad.AvailableField.getAvailableFieldByNode(h+ad.AvailableField.buildNodeId(A));},prototype:{FIELD_ITEM_TEMPLATE:'
',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(X);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aG){var A=this;var aF=A.availableFieldsDrag;return(aG===aF.dd);},plotFields:function(){var aF=this;var A=aF.get(t);A.each(function(aG){aF.plotField(aG);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(Q));},syncUI:function(){var A=this;var aF=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aF.addClass(ae);},_afterActiveTabChange:function(aG){var A=this;var aF=aG.newVal.get(P);if(A.get(ax)&&(aF===A.settingsNode)){A._renderSettings();}},_afterRender:function(aF){var A=this;A.plotFields();},_afterUiSetHeight:function(aF){var A=this;A.dropContainer.setStyle(an,M(aF)?aF+A.DEF_UNIT:aF);},_defCancelFn:function(aF){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(Z);},_handleSaveEvent:function(){var A=this;A.fire(al);},_renderViewport:function(){var aF=this;var aG=aF.get(u);var A=aF.viewport;A.appendChild(aF.dropContainer);aG.appendChild(A);},_renderPropertyList:function(){var A=this;
+if(!A.propertyList){A.propertyList=new ad.PropertyList(A.get(ac)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aF=new ad.TabView(A.get(G));A.tabView=aF;A.fieldsNode=aF.getTab(0).get(P);A.settingsNode=aF.getTab(1).get(P);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ad.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ad.DD.Drop(A.get(ak));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ad.DD.Delegate(A.get(az));},_setAvailableFields:function(aG){var aF=this;var A=[];aD.each(aG,function(aI,aH){A.push(D(aI)?aI:new ad.AvailableField(aI));});return A;},_setDropConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,groups:[Q],node:A.dropContainer},aF||{});},_setAvailableFieldsDragConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[Q],plugins:[{cfg:{moveOnEnd:false},fn:ad.Plugin.DDProxy}]},nodes:g+ag},aF||{});},_setPropertyList:function(aF){var A=this;return ad.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aC}},aF);},_setTabView:function(aI){var aF=this;var aH=aF.get(s);var aJ=aH.one(g+aA);var aG={after:{activeTabChange:ad.bind(aF._afterActiveTabChange,aF)},boundingBox:aH.one(g+m),contentBox:aH.one(g+Y),bubbleTargets:aF,contentNode:aH.one(g+av),cssClass:m,listNode:aJ,render:aF.get(u)};if(!aJ){var A=aF.getStrings();aG.items=[{cssClass:ah,label:A[k]},{cssClass:K,label:A[y]}];}return ad.merge(aG,aI);},_setToolbar:function(aG){var aF=this;var A=aF.getStrings();return ad.merge({activeState:false,bubbleTargets:aF,children:[{handler:ad.bind(aF._handleSaveEvent,aF),label:A[al],icon:aa},{handler:ad.bind(aF._handleCancelEvent,aF),label:A[Z]}]},aG);},_uiSetAvailableFields:function(aH){var A=this;var aG=A.fieldsNode;if(aG){var aF=ad.getDoc().invoke(E);aD.each(aH,function(aI){aF.appendChild(aI.get(r));});aG.setContent(A.fieldsContainer.setContent(aF));}},_uiSetFields:function(aF){var A=this;if(A.get(ax)){A.plotFields();}}}});ad.DiagramBuilderBase=ar;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder-base.js b/build/aui-diagram-builder/aui-diagram-builder-base.js
index fb9c9d865a3..be9cb6f4650 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-base.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-base.js
@@ -34,7 +34,7 @@ var Lang = A.Lang,
CONTAINER = 'container',
CONTENT = 'content',
CONTENT_BOX = 'contentBox',
- CONTENT_CONTAINER = 'contentContainer',
+ VIEWPORT = 'viewport',
CONTENT_NODE = 'contentNode',
CREATE_DOCUMENT_FRAGMENT = 'createDocumentFragment',
DIAGRAM = 'diagram',
@@ -72,9 +72,10 @@ var Lang = A.Lang,
_SPACE = ' ',
_DOT = '.',
_DOLLAR = '$',
+ _HASH = '#',
CSS_DIAGRAM_BUILDER_BASE_DROP_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, DROP, CONTAINER),
- CSS_DIAGRAM_BUILDER_BASE_CONTENT_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, CONTENT, CONTAINER),
+ CSS_DIAGRAM_BUILDER_BASE_VIEWPORT = AgetClassName(DIAGRAM, BUILDER, BASE, VIEWPORT),
CSS_DIAGRAM_BUILDER_BASE_FIELD = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD),
CSS_DIAGRAM_BUILDER_BASE_FIELDS_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, FIELDS, CONTAINER),
CSS_DIAGRAM_BUILDER_BASE_FIELD_DRAGGABLE = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD, DRAGGABLE),
@@ -144,6 +145,18 @@ var AvailableField = A.Component.create({
EXTENDS: A.Base,
+ buildNodeId: function(id) {
+ return AVAILABLE_FIELDS + _DOLLAR + FIELD + _DOLLAR + id;
+ },
+
+ getAvailableFieldByNode: function(node) {
+ return A.one(node).getData(AVAILABLE_FIELD);
+ },
+
+ getAvailableFieldById: function(id) {
+ return A.AvailableField.getAvailableFieldByNode(_HASH+A.AvailableField.buildNodeId(id));
+ },
+
prototype: {
FIELD_ITEM_TEMPLATE: '
',
+ VIEWPORT_TEMPLATE: '',
fieldsNode: null,
propertyList: null,
@@ -422,7 +432,7 @@ var DiagramBuilderBase = A.Component.create(
instance.after(instance._afterUiSetHeight, instance, '_uiSetHeight');
- instance.contentContainer = instance.get(CONTENT_CONTAINER);
+ instance.viewport = instance.get(VIEWPORT);
instance.dropContainer = instance.get(DROP_CONTAINER);
instance.fieldsContainer = instance.get(FIELDS_CONTAINER);
instance.toolbarContainer = instance.get(TOOLBAR_CONTAINER);
@@ -435,14 +445,20 @@ var DiagramBuilderBase = A.Component.create(
return (drag === availableFieldsDrag.dd);
},
- plotFields: function(fields) {
+ plotFields: function() {
+ var instance = this;
+ var fields = instance.get(FIELDS);
+
+ fields.each(function(field) {
+ instance.plotField(field);
+ });
},
renderUI: function() {
var instance = this;
instance._renderTabs();
- instance._renderContentContainer();
+ instance._renderViewport();
instance._uiSetAvailableFields(
instance.get(AVAILABLE_FIELDS)
@@ -498,13 +514,13 @@ var DiagramBuilderBase = A.Component.create(
instance.fire(SAVE);
},
- _renderContentContainer: function() {
+ _renderViewport: function() {
var instance = this;
var contentBox = instance.get(CONTENT_BOX);
- var contentContainer = instance.contentContainer;
+ var viewport = instance.viewport;
- contentContainer.appendChild(instance.dropContainer);
- contentBox.appendChild(contentContainer);
+ viewport.appendChild(instance.dropContainer);
+ contentBox.appendChild(viewport);
},
_renderPropertyList: function() {
@@ -588,6 +604,7 @@ var DiagramBuilderBase = A.Component.create(
return A.merge(
{
bubbleTargets: instance,
+ groups: [AVAILABLE_FIELDS],
node: instance.dropContainer
},
val || {}
@@ -602,6 +619,7 @@ var DiagramBuilderBase = A.Component.create(
bubbleTargets: instance,
container: instance.get(BOUNDING_BOX),
dragConfig: {
+ groups: [AVAILABLE_FIELDS],
plugins: [
{
cfg: {
diff --git a/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js b/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js
new file mode 100644
index 00000000000..e48234c19d4
--- /dev/null
+++ b/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js
@@ -0,0 +1,565 @@
+AUI.add('aui-diagram-builder-connector', function(A) {
+var Lang = A.Lang,
+ isArray = Lang.isArray,
+ isBoolean = Lang.isBoolean,
+ isNumber = Lang.isNumber,
+ isObject = Lang.isObject,
+ isString = Lang.isString,
+
+ YArray = A.Array,
+
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
+ isArrayList = function(val) {
+ return (val instanceof A.ArrayList);
+ },
+
+ ANCHOR = 'anchor',
+ ARROW_POINTS = 'arrowPoints',
+ BODY = 'body',
+ BOUNDING_BOX = 'boundingBox',
+ BUILDER = 'builder',
+ COLOR = 'color',
+ CONNECTOR = 'connector',
+ DATA_ANCHOR = 'dataAnchor',
+ DIAGRAM = 'diagram',
+ DIAGRAM_NODE = 'diagramNode',
+ HEIGHT = 'height',
+ ID = 'id',
+ LAZY_DRAW = 'lazyDraw',
+ MAX_SOURCES = 'maxSources',
+ MAX_TARGETS = 'maxTargets',
+ NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
+ PATH = 'path',
+ SHAPE = 'shape',
+ SOURCES = 'sources',
+ TARGETS = 'targets',
+ VIEWPORT = 'viewport',
+ WIDTH = 'width',
+ WRAPPER = 'wrapper',
+
+ _DOT = '.',
+
+ AgetClassName = A.getClassName,
+
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
+
+A.PolygonUtil = {
+ ARROW_POINTS: [
+ [ -12, -6 ],
+ [ -8, 0 ],
+ [ -12, 6 ],
+ [ 6, 0 ]
+ ],
+
+ drawLineArrow: function(shape, x1, y1, x2, y2, arrowPoints) {
+ var instance = this;
+
+ shape.moveTo(x1, y1);
+ shape.lineTo(x2, y2);
+
+ var angle = Math.atan2(y2-y1, x2-x1), centerX = (x2+x1)/2, centerY = (y2+y1)/2;
+
+ instance.drawPolygon(
+ shape,
+ instance.translatePoints(instance.rotatePoints(arrowPoints || instance.ARROW_POINTS, angle), centerX, centerY)
+ );
+ },
+
+ drawPolygon: function(shape, points) {
+ var instance = this;
+
+ shape.moveTo(points[0][0], points[0][1]);
+
+ YArray.each(points, function(p, i) {
+ if (i > 0) {
+ shape.lineTo(points[i][0], points[i][1]);
+ }
+ });
+
+ shape.lineTo(points[0][0], points[0][1]);
+ shape.end();
+ },
+
+ translatePoints: function(points, x, y) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push([ points[i][0] + x, points[i][1] + y ]);
+ });
+
+ return xy;
+ },
+
+ rotatePoints: function(points, angle) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push(instance.rotatePoint(angle, points[i][0], points[i][1]));
+ });
+
+ return xy;
+ },
+
+ rotatePoint: function(angle, x, y) {
+ return [
+ (x * Math.cos(angle)) - (y * Math.sin(angle)),
+ (x * Math.sin(angle)) + (y * Math.cos(angle))
+ ];
+ }
+};
+
+A.Connector = A.Base.create('line', A.Base, [], {
+ graphics: null,
+ shape: null,
+
+ initializer: function(config) {
+ var instance = this;
+
+ instance.after({
+ p1Change: instance.draw,
+ p2Change: instance.draw
+ });
+
+ instance._initGraphics();
+ instance._initShapes();
+
+ if (!instance.get(LAZY_DRAW)) {
+ instance.draw();
+ }
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.graphics.destroy();
+ },
+
+ draw: function() {
+ var instance = this;
+ var shape = instance.shape;
+
+ var c1 = instance.getCoordinate(instance.get(P1));
+ var c2 = instance.getCoordinate(instance.get(P2));
+
+ shape.clear();
+
+ A.PolygonUtil.drawLineArrow(shape, c1[0], c1[1], c2[0], c2[1], instance.get(ARROW_POINTS));
+ },
+
+ getCoordinate: function(p) {
+ var instance = this;
+ var xy = instance.get(VIEWPORT).getXY();
+
+ return [ p[0] - xy[0], p[1] - xy[1] ];
+ },
+
+ _initGraphics: function() {
+ var instance = this;
+
+ var graphics = new A.Graphic({
+ width: instance.get(WIDTH),
+ height: instance.get(HEIGHT),
+ render: instance.get(VIEWPORT)
+ });
+
+ instance.graphics = graphics;
+ },
+
+ _initShapes: function() {
+ var instance = this;
+
+ instance.shape = instance.graphics.getShape(
+ instance.get(SHAPE)
+ );
+ },
+
+ _setShape: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ type: PATH,
+ stroke: {
+ color: instance.get(COLOR),
+ weight: 2
+ },
+ fill: {
+ color: instance.get(COLOR)
+ }
+ },
+ val
+ );
+ }
+},{
+ ATTRS: {
+ color: {
+ value: '#666',
+ validator: isString
+ },
+
+ lazyDraw: {
+ value: false,
+ validator: isBoolean
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ },
+
+ shape: {
+ value: null,
+ setter: '_setShape'
+ },
+
+ arrowPoints: {
+ value: A.PolygonUtil.ARROW_POINTS
+ },
+
+ p1: {
+ value: [0, 0],
+ validator: isArray
+ },
+
+ p2: {
+ value: [0, 0],
+ validator: isArray
+ }
+ }
+});
+
+A.Anchor = A.Base.create('anchor', A.Base, [], {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ NODE_TEMPLATE: '',
+
+ connectors: null,
+
+ initializer: function() {
+ var instance = this;
+
+ instance.connectors = {};
+
+ instance._renderNode();
+
+ instance.connectTargets();
+
+ instance.after({
+ targetsChange: instance._afterTargetsChange
+ });
+ },
+
+ addSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ },
+
+ addTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ },
+
+ alignConnectors: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ var tConnector = instance.getConnector(target);
+
+ if (tConnector) {
+ tConnector.set(P1, instance.getCenterXY());
+ tConnector.set(P2, target.getCenterXY());
+ }
+ });
+
+ instance.get(SOURCES).each(function(source) {
+ var sConnector = source.getConnector(instance);
+
+ if (sConnector) {
+ sConnector.set(P1, source.getCenterXY());
+ sConnector.set(P2, instance.getCenterXY());
+ }
+ });
+
+ return instance;
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.disconnectTargets();
+ instance.disconnectSources();
+
+ instance.get(NODE).remove();
+ },
+
+ connect: function(target) {
+ var instance = this;
+
+ instance.addTarget(target);
+
+ if (!instance.isConnected(target)) {
+ var tConnector = target.get(CONNECTOR);
+
+ tConnector.p1 = instance.getCenterXY();
+ tConnector.p2 = target.getCenterXY();
+
+ instance.connectors[target.get(ID)] = new A.Connector(tConnector);
+ }
+
+ return instance;
+ },
+
+ connectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(A.bind(instance.connect, instance));
+
+ return instance;
+ },
+
+ disconnect: function(target) {
+ var instance = this;
+
+ instance.getConnector(target).destroy();
+
+ instance.removeTarget(target);
+ target.removeSource(instance);
+ },
+
+ disconnectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ instance.disconnect(target);
+ });
+
+ return instance;
+ },
+
+ disconnectSources: function() {
+ var instance = this;
+
+ instance.get(SOURCES).each(function(source) {
+ source.disconnect(instance);
+ });
+
+ return instance;
+ },
+
+ getCenterXY: function() {
+ var instance = this;
+
+ return instance.get(NODE).getCenterXY();
+ },
+
+ getConnector: function(target) {
+ var instance = this;
+
+ return instance.connectors[target.get(ID)];
+ },
+
+ isConnected: function(target) {
+ var instance = this;
+
+ return instance.connectors.hasOwnProperty(target.get(ID));
+ },
+
+ updateSources: function(sources) {
+ var instance = this;
+
+ instance.set(SOURCES, sources);
+
+ return instance;
+ },
+
+ updateTargets: function(targets) {
+ var instance = this;
+
+ instance.set(TARGETS, targets);
+
+ return instance;
+ },
+
+ removeSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source)
+ );
+ },
+
+ removeTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target)
+ );
+ },
+
+ _afterActiveChange: function(event) {
+ var instance = this;
+
+ instance._uiSetActive(event.newVal);
+ },
+
+ _afterTargetsChange: function(event) {
+ var instance = this;
+
+ // TODO - event.prevVal is always equal to event.newVal, review this
+ // logic below, references between anchors needs to be cleaned up otherwise
+ // will store the wrong relationship between nodes.
+
+ event.prevVal.each(function(anchor) {
+ anchor.removeSource(instance);
+ });
+
+ event.newVal.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+ },
+
+ _renderNode: function() {
+ var instance = this;
+ var diagramNode = instance.get(DIAGRAM_NODE);
+ var container = diagramNode.get(BOUNDING_BOX);
+
+ instance.wrapper = container.one(_DOT+CSS_DB_ANCHOR_NODE_WRAPPER) ||
+ A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE);
+
+ instance.wrapper.appendTo(container).appendChild(instance.get(NODE));
+ },
+
+ _setConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ viewport: instance.get(VIEWPORT)
+ },
+ val
+ );
+ },
+
+ _setSources: function(val) {
+ var instance = this;
+
+ return instance._setAnchors(val);
+ },
+
+ _setTargets: function(val) {
+ var instance = this;
+
+ val = instance._setAnchors(val);
+
+ val.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+
+ return val;
+ },
+
+ _setAnchors: function(val) {
+ var instance = this;
+
+ if (!isArrayList(val)) {
+ var targets = [];
+
+ A.Array.each(val, function(target) {
+ if (isString(target)) {
+ // TODO - need this?
+ target = A.Anchor.getAnchorByNode(target);
+ }
+
+ targets.push( isAnchor(target) ? target : new A.Anchor(target) );
+ });
+
+ val = new A.ArrayList(targets);
+ }
+
+ return val;
+ },
+
+ _setNode: function(val) {
+ var instance = this;
+ var id = instance.get(ID);
+
+ return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ }
+},{
+ ATTRS: {
+ diagramNode: {
+ },
+
+ connector: {
+ setter: '_setConnector',
+ value: {},
+ validator: isObject
+ },
+
+ id: {
+ readOnly: true,
+ valueFn: function() {
+ return A.guid();
+ }
+ },
+
+ maxSources: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ maxTargets: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ node: {
+ setter: '_setNode',
+ valueFn: function() {
+ var instance = this;
+
+ return A.Node.create(instance.NODE_TEMPLATE);
+ }
+ },
+
+ sources: {
+ value: [],
+ setter: '_setSources',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ targets: {
+ value: [],
+ setter: '_setTargets',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ }
+ },
+
+ getAnchorByNode: function(node) {
+ return A.one(node).getData(DATA_ANCHOR);
+ }
+});
+
+}, '@VERSION@' ,{requires:['aui-base','arraylist-add','arraylist-filter','json','graphics','dd'], skinnable:true});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-connector-min.js b/build/aui-diagram-builder/aui-diagram-builder-connector-min.js
new file mode 100644
index 00000000000..35a0ebce02c
--- /dev/null
+++ b/build/aui-diagram-builder/aui-diagram-builder-connector-min.js
@@ -0,0 +1 @@
+AUI.add("aui-diagram-builder-connector",function(k){var M=k.Lang,r=M.isArray,v=M.isBoolean,L=M.isNumber,z=M.isObject,h=M.isString,D=k.Array,b=function(A){return(A instanceof k.Anchor);},E=function(A){return(A instanceof k.ArrayList);},y="anchor",F="arrowPoints",C="body",G="boundingBox",N="builder",w="color",n="connector",a="dataAnchor",x="diagram",s="diagramNode",u="height",p="id",I="lazyDraw",j="maxSources",i="maxTargets",J="node",m="p1",l="p2",e="path",q="shape",g="sources",f="targets",B="viewport",c="width",K="wrapper",o=".",t=k.getClassName,d=t(x,N,y,J,K),H=t(x,N,y,J);k.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(T,O,V,A,U,R){var W=this;T.moveTo(O,V);T.lineTo(A,U);var P=Math.atan2(U-V,A-O),S=(A+O)/2,Q=(U+V)/2;W.drawPolygon(T,W.translatePoints(W.rotatePoints(R||W.ARROW_POINTS,P),S,Q));},drawPolygon:function(O,P){var A=this;O.moveTo(P[0][0],P[0][1]);D.each(P,function(R,Q){if(Q>0){O.lineTo(P[Q][0],P[Q][1]);}});O.lineTo(P[0][0],P[0][1]);O.end();},translatePoints:function(P,O,R){var A=this;var Q=[];D.each(P,function(T,S){Q.push([P[S][0]+O,P[S][1]+R]);});return Q;},rotatePoints:function(O,Q){var A=this;var P=[];D.each(O,function(S,R){P.push(A.rotatePoint(Q,O[R][0],O[R][1]));});return P;},rotatePoint:function(O,A,P){return[(A*Math.cos(O))-(P*Math.sin(O)),(A*Math.sin(O))+(P*Math.cos(O))];}};k.Connector=k.Base.create("line",k.Base,[],{graphics:null,shape:null,initializer:function(O){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(I)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var O=A.shape;var Q=A.getCoordinate(A.get(m));var P=A.getCoordinate(A.get(l));O.clear();k.PolygonUtil.drawLineArrow(O,Q[0],Q[1],P[0],P[1],A.get(F));},getCoordinate:function(P){var A=this;var O=A.get(B).getXY();return[P[0]-O[0],P[1]-O[1]];},_initGraphics:function(){var A=this;var O=new k.Graphic({width:A.get(c),height:A.get(u),render:A.get(B)});A.graphics=O;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(q));},_setShape:function(O){var A=this;return k.merge({type:e,stroke:{color:A.get(w),weight:2},fill:{color:A.get(w)}},O);}},{ATTRS:{color:{value:"#666",validator:h},lazyDraw:{value:false,validator:v},viewport:{setter:k.one,value:C},shape:{value:null,setter:"_setShape"},arrowPoints:{value:k.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:r},p2:{value:[0,0],validator:r}}});k.Anchor=k.Base.create("anchor",k.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({targetsChange:A._afterTargetsChange});},addSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O).add(O));},addTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O).add(O));},alignConnectors:function(){var A=this;A.get(f).each(function(O){var P=A.getConnector(O);if(P){P.set(m,A.getCenterXY());P.set(l,O.getCenterXY());}});A.get(g).each(function(O){var P=O.getConnector(A);if(P){P.set(m,O.getCenterXY());P.set(l,A.getCenterXY());}});return A;},destroy:function(){var A=this;A.disconnectTargets();A.disconnectSources();A.get(J).remove();},connect:function(O){var A=this;A.addTarget(O);if(!A.isConnected(O)){var P=O.get(n);P.p1=A.getCenterXY();P.p2=O.getCenterXY();A.connectors[O.get(p)]=new k.Connector(P);}return A;},connectTargets:function(){var A=this;A.get(f).each(k.bind(A.connect,A));return A;},disconnect:function(O){var A=this;A.getConnector(O).destroy();A.removeTarget(O);O.removeSource(A);},disconnectTargets:function(){var A=this;A.get(f).each(function(O){A.disconnect(O);});return A;},disconnectSources:function(){var A=this;A.get(g).each(function(O){O.disconnect(A);});return A;},getCenterXY:function(){var A=this;return A.get(J).getCenterXY();},getConnector:function(O){var A=this;return A.connectors[O.get(p)];},isConnected:function(O){var A=this;return A.connectors.hasOwnProperty(O.get(p));},updateSources:function(O){var A=this;A.set(g,O);return A;},updateTargets:function(O){var A=this;A.set(f,O);return A;},removeSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O));},removeTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O));},_afterActiveChange:function(O){var A=this;A._uiSetActive(O.newVal);},_afterTargetsChange:function(O){var A=this;O.prevVal.each(function(P){P.removeSource(A);});O.newVal.each(function(P){P.addSource(A);});},_renderNode:function(){var A=this;var P=A.get(s);var O=P.get(G);A.wrapper=O.one(o+d)||k.Node.create(A.ANCHOR_WRAPPER_TEMPLATE);A.wrapper.appendTo(O).appendChild(A.get(J));},_setConnector:function(O){var A=this;return k.merge({viewport:A.get(B)},O);},_setSources:function(O){var A=this;return A._setAnchors(O);},_setTargets:function(O){var A=this;O=A._setAnchors(O);O.each(function(P){P.addSource(A);});return O;},_setAnchors:function(P){var A=this;if(!E(P)){var O=[];k.Array.each(P,function(Q){if(h(Q)){Q=k.Anchor.getAnchorByNode(Q);}O.push(b(Q)?Q:new k.Anchor(Q));});P=new k.ArrayList(O);}return P;},_setNode:function(O){var A=this;var P=A.get(p);return k.one(O).set(p,P).setData(a,A);}},{ATTRS:{diagramNode:{},connector:{setter:"_setConnector",value:{},validator:z},id:{readOnly:true,valueFn:function(){return k.guid();}},maxSources:{value:Infinity,validator:L},maxTargets:{value:Infinity,validator:L},node:{setter:"_setNode",valueFn:function(){var A=this;return k.Node.create(A.NODE_TEMPLATE);}},sources:{value:[],setter:"_setSources",validator:function(A){return r(A)||E(A);}},targets:{value:[],setter:"_setTargets",validator:function(A){return r(A)||E(A);}},viewport:{setter:k.one,value:C}},getAnchorByNode:function(A){return k.one(A).getData(a);}});},"@VERSION@",{requires:["aui-base","arraylist-add","arraylist-filter","json","graphics","dd"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder-connector.js b/build/aui-diagram-builder/aui-diagram-builder-connector.js
new file mode 100644
index 00000000000..e48234c19d4
--- /dev/null
+++ b/build/aui-diagram-builder/aui-diagram-builder-connector.js
@@ -0,0 +1,565 @@
+AUI.add('aui-diagram-builder-connector', function(A) {
+var Lang = A.Lang,
+ isArray = Lang.isArray,
+ isBoolean = Lang.isBoolean,
+ isNumber = Lang.isNumber,
+ isObject = Lang.isObject,
+ isString = Lang.isString,
+
+ YArray = A.Array,
+
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
+ isArrayList = function(val) {
+ return (val instanceof A.ArrayList);
+ },
+
+ ANCHOR = 'anchor',
+ ARROW_POINTS = 'arrowPoints',
+ BODY = 'body',
+ BOUNDING_BOX = 'boundingBox',
+ BUILDER = 'builder',
+ COLOR = 'color',
+ CONNECTOR = 'connector',
+ DATA_ANCHOR = 'dataAnchor',
+ DIAGRAM = 'diagram',
+ DIAGRAM_NODE = 'diagramNode',
+ HEIGHT = 'height',
+ ID = 'id',
+ LAZY_DRAW = 'lazyDraw',
+ MAX_SOURCES = 'maxSources',
+ MAX_TARGETS = 'maxTargets',
+ NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
+ PATH = 'path',
+ SHAPE = 'shape',
+ SOURCES = 'sources',
+ TARGETS = 'targets',
+ VIEWPORT = 'viewport',
+ WIDTH = 'width',
+ WRAPPER = 'wrapper',
+
+ _DOT = '.',
+
+ AgetClassName = A.getClassName,
+
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
+
+A.PolygonUtil = {
+ ARROW_POINTS: [
+ [ -12, -6 ],
+ [ -8, 0 ],
+ [ -12, 6 ],
+ [ 6, 0 ]
+ ],
+
+ drawLineArrow: function(shape, x1, y1, x2, y2, arrowPoints) {
+ var instance = this;
+
+ shape.moveTo(x1, y1);
+ shape.lineTo(x2, y2);
+
+ var angle = Math.atan2(y2-y1, x2-x1), centerX = (x2+x1)/2, centerY = (y2+y1)/2;
+
+ instance.drawPolygon(
+ shape,
+ instance.translatePoints(instance.rotatePoints(arrowPoints || instance.ARROW_POINTS, angle), centerX, centerY)
+ );
+ },
+
+ drawPolygon: function(shape, points) {
+ var instance = this;
+
+ shape.moveTo(points[0][0], points[0][1]);
+
+ YArray.each(points, function(p, i) {
+ if (i > 0) {
+ shape.lineTo(points[i][0], points[i][1]);
+ }
+ });
+
+ shape.lineTo(points[0][0], points[0][1]);
+ shape.end();
+ },
+
+ translatePoints: function(points, x, y) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push([ points[i][0] + x, points[i][1] + y ]);
+ });
+
+ return xy;
+ },
+
+ rotatePoints: function(points, angle) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push(instance.rotatePoint(angle, points[i][0], points[i][1]));
+ });
+
+ return xy;
+ },
+
+ rotatePoint: function(angle, x, y) {
+ return [
+ (x * Math.cos(angle)) - (y * Math.sin(angle)),
+ (x * Math.sin(angle)) + (y * Math.cos(angle))
+ ];
+ }
+};
+
+A.Connector = A.Base.create('line', A.Base, [], {
+ graphics: null,
+ shape: null,
+
+ initializer: function(config) {
+ var instance = this;
+
+ instance.after({
+ p1Change: instance.draw,
+ p2Change: instance.draw
+ });
+
+ instance._initGraphics();
+ instance._initShapes();
+
+ if (!instance.get(LAZY_DRAW)) {
+ instance.draw();
+ }
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.graphics.destroy();
+ },
+
+ draw: function() {
+ var instance = this;
+ var shape = instance.shape;
+
+ var c1 = instance.getCoordinate(instance.get(P1));
+ var c2 = instance.getCoordinate(instance.get(P2));
+
+ shape.clear();
+
+ A.PolygonUtil.drawLineArrow(shape, c1[0], c1[1], c2[0], c2[1], instance.get(ARROW_POINTS));
+ },
+
+ getCoordinate: function(p) {
+ var instance = this;
+ var xy = instance.get(VIEWPORT).getXY();
+
+ return [ p[0] - xy[0], p[1] - xy[1] ];
+ },
+
+ _initGraphics: function() {
+ var instance = this;
+
+ var graphics = new A.Graphic({
+ width: instance.get(WIDTH),
+ height: instance.get(HEIGHT),
+ render: instance.get(VIEWPORT)
+ });
+
+ instance.graphics = graphics;
+ },
+
+ _initShapes: function() {
+ var instance = this;
+
+ instance.shape = instance.graphics.getShape(
+ instance.get(SHAPE)
+ );
+ },
+
+ _setShape: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ type: PATH,
+ stroke: {
+ color: instance.get(COLOR),
+ weight: 2
+ },
+ fill: {
+ color: instance.get(COLOR)
+ }
+ },
+ val
+ );
+ }
+},{
+ ATTRS: {
+ color: {
+ value: '#666',
+ validator: isString
+ },
+
+ lazyDraw: {
+ value: false,
+ validator: isBoolean
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ },
+
+ shape: {
+ value: null,
+ setter: '_setShape'
+ },
+
+ arrowPoints: {
+ value: A.PolygonUtil.ARROW_POINTS
+ },
+
+ p1: {
+ value: [0, 0],
+ validator: isArray
+ },
+
+ p2: {
+ value: [0, 0],
+ validator: isArray
+ }
+ }
+});
+
+A.Anchor = A.Base.create('anchor', A.Base, [], {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ NODE_TEMPLATE: '',
+
+ connectors: null,
+
+ initializer: function() {
+ var instance = this;
+
+ instance.connectors = {};
+
+ instance._renderNode();
+
+ instance.connectTargets();
+
+ instance.after({
+ targetsChange: instance._afterTargetsChange
+ });
+ },
+
+ addSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ },
+
+ addTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ },
+
+ alignConnectors: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ var tConnector = instance.getConnector(target);
+
+ if (tConnector) {
+ tConnector.set(P1, instance.getCenterXY());
+ tConnector.set(P2, target.getCenterXY());
+ }
+ });
+
+ instance.get(SOURCES).each(function(source) {
+ var sConnector = source.getConnector(instance);
+
+ if (sConnector) {
+ sConnector.set(P1, source.getCenterXY());
+ sConnector.set(P2, instance.getCenterXY());
+ }
+ });
+
+ return instance;
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.disconnectTargets();
+ instance.disconnectSources();
+
+ instance.get(NODE).remove();
+ },
+
+ connect: function(target) {
+ var instance = this;
+
+ instance.addTarget(target);
+
+ if (!instance.isConnected(target)) {
+ var tConnector = target.get(CONNECTOR);
+
+ tConnector.p1 = instance.getCenterXY();
+ tConnector.p2 = target.getCenterXY();
+
+ instance.connectors[target.get(ID)] = new A.Connector(tConnector);
+ }
+
+ return instance;
+ },
+
+ connectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(A.bind(instance.connect, instance));
+
+ return instance;
+ },
+
+ disconnect: function(target) {
+ var instance = this;
+
+ instance.getConnector(target).destroy();
+
+ instance.removeTarget(target);
+ target.removeSource(instance);
+ },
+
+ disconnectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ instance.disconnect(target);
+ });
+
+ return instance;
+ },
+
+ disconnectSources: function() {
+ var instance = this;
+
+ instance.get(SOURCES).each(function(source) {
+ source.disconnect(instance);
+ });
+
+ return instance;
+ },
+
+ getCenterXY: function() {
+ var instance = this;
+
+ return instance.get(NODE).getCenterXY();
+ },
+
+ getConnector: function(target) {
+ var instance = this;
+
+ return instance.connectors[target.get(ID)];
+ },
+
+ isConnected: function(target) {
+ var instance = this;
+
+ return instance.connectors.hasOwnProperty(target.get(ID));
+ },
+
+ updateSources: function(sources) {
+ var instance = this;
+
+ instance.set(SOURCES, sources);
+
+ return instance;
+ },
+
+ updateTargets: function(targets) {
+ var instance = this;
+
+ instance.set(TARGETS, targets);
+
+ return instance;
+ },
+
+ removeSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source)
+ );
+ },
+
+ removeTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target)
+ );
+ },
+
+ _afterActiveChange: function(event) {
+ var instance = this;
+
+ instance._uiSetActive(event.newVal);
+ },
+
+ _afterTargetsChange: function(event) {
+ var instance = this;
+
+ // TODO - event.prevVal is always equal to event.newVal, review this
+ // logic below, references between anchors needs to be cleaned up otherwise
+ // will store the wrong relationship between nodes.
+
+ event.prevVal.each(function(anchor) {
+ anchor.removeSource(instance);
+ });
+
+ event.newVal.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+ },
+
+ _renderNode: function() {
+ var instance = this;
+ var diagramNode = instance.get(DIAGRAM_NODE);
+ var container = diagramNode.get(BOUNDING_BOX);
+
+ instance.wrapper = container.one(_DOT+CSS_DB_ANCHOR_NODE_WRAPPER) ||
+ A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE);
+
+ instance.wrapper.appendTo(container).appendChild(instance.get(NODE));
+ },
+
+ _setConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ viewport: instance.get(VIEWPORT)
+ },
+ val
+ );
+ },
+
+ _setSources: function(val) {
+ var instance = this;
+
+ return instance._setAnchors(val);
+ },
+
+ _setTargets: function(val) {
+ var instance = this;
+
+ val = instance._setAnchors(val);
+
+ val.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+
+ return val;
+ },
+
+ _setAnchors: function(val) {
+ var instance = this;
+
+ if (!isArrayList(val)) {
+ var targets = [];
+
+ A.Array.each(val, function(target) {
+ if (isString(target)) {
+ // TODO - need this?
+ target = A.Anchor.getAnchorByNode(target);
+ }
+
+ targets.push( isAnchor(target) ? target : new A.Anchor(target) );
+ });
+
+ val = new A.ArrayList(targets);
+ }
+
+ return val;
+ },
+
+ _setNode: function(val) {
+ var instance = this;
+ var id = instance.get(ID);
+
+ return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ }
+},{
+ ATTRS: {
+ diagramNode: {
+ },
+
+ connector: {
+ setter: '_setConnector',
+ value: {},
+ validator: isObject
+ },
+
+ id: {
+ readOnly: true,
+ valueFn: function() {
+ return A.guid();
+ }
+ },
+
+ maxSources: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ maxTargets: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ node: {
+ setter: '_setNode',
+ valueFn: function() {
+ var instance = this;
+
+ return A.Node.create(instance.NODE_TEMPLATE);
+ }
+ },
+
+ sources: {
+ value: [],
+ setter: '_setSources',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ targets: {
+ value: [],
+ setter: '_setTargets',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ }
+ },
+
+ getAnchorByNode: function(node) {
+ return A.one(node).getData(DATA_ANCHOR);
+ }
+});
+
+}, '@VERSION@' ,{requires:['aui-base','arraylist-add','arraylist-filter','json','graphics','dd'], skinnable:true});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-debug.js b/build/aui-diagram-builder/aui-diagram-builder-debug.js
index fbc2659c074..42d2ebf15d9 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-debug.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-debug.js
@@ -34,7 +34,7 @@ var Lang = A.Lang,
CONTAINER = 'container',
CONTENT = 'content',
CONTENT_BOX = 'contentBox',
- CONTENT_CONTAINER = 'contentContainer',
+ VIEWPORT = 'viewport',
CONTENT_NODE = 'contentNode',
CREATE_DOCUMENT_FRAGMENT = 'createDocumentFragment',
DIAGRAM = 'diagram',
@@ -72,9 +72,10 @@ var Lang = A.Lang,
_SPACE = ' ',
_DOT = '.',
_DOLLAR = '$',
+ _HASH = '#',
CSS_DIAGRAM_BUILDER_BASE_DROP_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, DROP, CONTAINER),
- CSS_DIAGRAM_BUILDER_BASE_CONTENT_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, CONTENT, CONTAINER),
+ CSS_DIAGRAM_BUILDER_BASE_VIEWPORT = AgetClassName(DIAGRAM, BUILDER, BASE, VIEWPORT),
CSS_DIAGRAM_BUILDER_BASE_FIELD = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD),
CSS_DIAGRAM_BUILDER_BASE_FIELDS_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, FIELDS, CONTAINER),
CSS_DIAGRAM_BUILDER_BASE_FIELD_DRAGGABLE = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD, DRAGGABLE),
@@ -144,6 +145,18 @@ var AvailableField = A.Component.create({
EXTENDS: A.Base,
+ buildNodeId: function(id) {
+ return AVAILABLE_FIELDS + _DOLLAR + FIELD + _DOLLAR + id;
+ },
+
+ getAvailableFieldByNode: function(node) {
+ return A.one(node).getData(AVAILABLE_FIELD);
+ },
+
+ getAvailableFieldById: function(id) {
+ return A.AvailableField.getAvailableFieldByNode(_HASH+A.AvailableField.buildNodeId(id));
+ },
+
prototype: {
FIELD_ITEM_TEMPLATE: '
',
+ VIEWPORT_TEMPLATE: '',
fieldsNode: null,
propertyList: null,
@@ -422,7 +432,7 @@ var DiagramBuilderBase = A.Component.create(
instance.after(instance._afterUiSetHeight, instance, '_uiSetHeight');
- instance.contentContainer = instance.get(CONTENT_CONTAINER);
+ instance.viewport = instance.get(VIEWPORT);
instance.dropContainer = instance.get(DROP_CONTAINER);
instance.fieldsContainer = instance.get(FIELDS_CONTAINER);
instance.toolbarContainer = instance.get(TOOLBAR_CONTAINER);
@@ -435,14 +445,20 @@ var DiagramBuilderBase = A.Component.create(
return (drag === availableFieldsDrag.dd);
},
- plotFields: function(fields) {
+ plotFields: function() {
+ var instance = this;
+ var fields = instance.get(FIELDS);
+
+ fields.each(function(field) {
+ instance.plotField(field);
+ });
},
renderUI: function() {
var instance = this;
instance._renderTabs();
- instance._renderContentContainer();
+ instance._renderViewport();
instance._uiSetAvailableFields(
instance.get(AVAILABLE_FIELDS)
@@ -498,13 +514,13 @@ var DiagramBuilderBase = A.Component.create(
instance.fire(SAVE);
},
- _renderContentContainer: function() {
+ _renderViewport: function() {
var instance = this;
var contentBox = instance.get(CONTENT_BOX);
- var contentContainer = instance.contentContainer;
+ var viewport = instance.viewport;
- contentContainer.appendChild(instance.dropContainer);
- contentBox.appendChild(contentContainer);
+ viewport.appendChild(instance.dropContainer);
+ contentBox.appendChild(viewport);
},
_renderPropertyList: function() {
@@ -588,6 +604,7 @@ var DiagramBuilderBase = A.Component.create(
return A.merge(
{
bubbleTargets: instance,
+ groups: [AVAILABLE_FIELDS],
node: instance.dropContainer
},
val || {}
@@ -602,6 +619,7 @@ var DiagramBuilderBase = A.Component.create(
bubbleTargets: instance,
container: instance.get(BOUNDING_BOX),
dragConfig: {
+ groups: [AVAILABLE_FIELDS],
plugins: [
{
cfg: {
@@ -723,6 +741,7 @@ var Lang = A.Lang,
isArray = Lang.isArray,
isObject = Lang.isObject,
isString = Lang.isString,
+ isBoolean = Lang.isBoolean,
AArray = A.Array,
@@ -734,6 +753,10 @@ var Lang = A.Lang,
return (val instanceof A.DiagramNode);
},
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
getLeftTop = function(container, node) {
var nodeXY = isArray(node) ? node : node.getXY();
var containerXY = isArray(container) ? container : container.getXY();
@@ -743,44 +766,123 @@ var Lang = A.Lang,
});
},
+ ANCHOR = 'anchor',
+ ANCHORS = 'anchors',
+ ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
+ CANCEL = 'cancel',
+ CLICK = 'click',
+ CONTENT = 'content',
+ CONTROLS = 'controls',
+ CONTROLS_TOOLBAR = 'controlsToolbar',
DATA = 'data',
DBLCLICK = 'dblclick',
+ DELETE = 'delete',
+ DELETE_MESSAGE = 'deleteMessage',
DESCRIPTION = 'description',
DIAGRAM = 'diagram',
- DIAGRAM_BUILDER = 'diagram-builder',
- DIAGRAM_NODE = 'diagram-node',
+ DIAGRAM_BUILDER_NAME = 'diagram-builder',
+ DIAGRAM_NODE = 'diagramNode',
+ DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ ESC = 'esc',
+ FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
+ HOVER = 'hover',
+ KEYDOWN = 'keydown',
+ LINK = 'link',
+ MOUSEENTER = 'mouseenter',
+ MOUSELEAVE = 'mouseleave',
NAME = 'name',
NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
PARENT_NODE = 'parentNode',
RECORDS = 'records',
RECORDSET = 'recordset',
+ REGION = 'region',
RENDERED = 'rendered',
+ SELECTED = 'selected',
+ SHUFFLE = 'shuffle',
+ TASK = 'task',
+ TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
+ VIEWPORT = 'viewport',
+ WRAPPER = 'wrapper',
XY = 'xy',
_DOT = '.',
+ _DOLLAR = '$',
_EMPTY_STR = '',
+ _DASH = '-',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_CONTROLS = AgetClassName(DIAGRAM, BUILDER, CONTROLS),
+ CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE),
+ CSS_DIAGRAM_NODE_CONTENT = AgetClassName(DIAGRAM, NODE, CONTENT),
CSS_DIAGRAM_NODE_EDITING = AgetClassName(DIAGRAM, NODE, EDITING),
- CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE);
+ CSS_DIAGRAM_NODE_SELECTED = AgetClassName(DIAGRAM, NODE, SELECTED);
+
+// REMOVE THIS!
+var __dump = function() {
+ var PAD = ' ', BR = '
';
+
+ A.all('.aui-diagram-node').each(function(n) {
+ var b = _EMPTY_STR,
+ dn = A.Widget.getByNode(n),
+ dnName = dn.get('name'),
+ dnBB = dn.get('boundingBox'),
+ log = dnBB.one('.log') || A.Node.create('').appendTo(dnBB);
+
+ b += dnName + BR;
+
+ dn.get(FIELDS).each(function(a) {
+ b += PAD + 'a: ' + a.get('id') + BR;
+
+ a.get('targets').each(function(t) {
+ var tdn = t.get(DIAGRAM_NODE);
+
+ t.get('node').setContent(t.get('id'));
+
+ b += PAD + PAD + 't: ' + tdn.get('name') + ' (s: ' + t.get('id') + ')' + BR;
+ });
+
+ a.get('sources').each(function(s) {
+ var sdn = s.get(DIAGRAM_NODE);
+
+ s.get('node').setContent(s.get('id'));
+
+ b += PAD + PAD + 's: ' + sdn.get('name') + ' (t: ' + s.get('id') + ')' + BR;
+ });
+ });
+
+ log.setContent(b);
+ });
+};
+// END.
var DiagramBuilder = A.Component.create({
- NAME: DIAGRAM_BUILDER,
+ NAME: DIAGRAM_BUILDER_NAME,
ATTRS: {
fieldsDragConfig: {
value: null,
setter: '_setFieldsDragConfig',
validator: isObject
+ },
+
+ tmpConnector: {
+ setter: '_setTmpConnector',
+ value: {},
+ validator: isObject
}
},
@@ -797,12 +899,18 @@ var DiagramBuilder = A.Component.create({
instance.on({
cancel: instance._onCancel,
+ 'drag:drag': instance._onDrag,
'drag:end': instance._onDragEnd,
'drop:hit': instance._onDropHit,
save: instance._onSave
});
+ instance.handlerKeyDown = A.getDoc().on(KEYDOWN, A.bind(instance._afterKeyEvent, instance));
+
+ instance.dropContainer.delegate(CLICK, A.bind(instance._onNodeClick, instance), _DOT+CSS_DIAGRAM_NODE);
instance.dropContainer.delegate(DBLCLICK, A.bind(instance._onNodeEdit, instance), _DOT+CSS_DIAGRAM_NODE);
+ instance.dropContainer.delegate(MOUSEENTER, A.bind(instance._onMouseenterAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
+ instance.dropContainer.delegate(MOUSELEAVE, A.bind(instance._onMouseleaveAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
},
syncUI: function() {
@@ -811,12 +919,17 @@ var DiagramBuilder = A.Component.create({
A.DiagramBuilder.superclass.syncUI.apply(this, arguments);
instance._setupFieldsDrag();
+
+ instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
createField: function(val) {
var instance = this;
if (!isDiagramNode(val)) {
+ // val.bubbleTargets = instance;
+ val.builder = instance;
+ val.viewport = instance.get(VIEWPORT);
val = new (instance.getFieldClass(val.type || NODE))(val);
}
@@ -846,21 +959,31 @@ var DiagramBuilder = A.Component.create({
return (drag === fieldsDrag.dd);
},
- plotFields: function() {
+ plotField: function(field) {
var instance = this;
- var fields = instance.get(FIELDS);
- fields.each(function(field) {
- instance.plotField(field);
- });
+ if (!field.get(RENDERED)) {
+ field.render(instance.dropContainer);
+ }
},
- plotField: function(field) {
+ unselectAll: function() {
var instance = this;
+ var selectedNode = instance.selectedNode;
- if (!field.get(RENDERED)) {
- field.render(instance.dropContainer);
+ if (selectedNode) {
+ selectedNode.set(SELECTED, false);
}
+
+ instance.selectedNode = null;
+ },
+
+ select: function(diagramNode) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
startEditingNode: function(diagramNode) {
@@ -871,8 +994,6 @@ var DiagramBuilder = A.Component.create({
instance.tabView.selectTab(A.DiagramBuilder.SETTINGS_TAB);
- // instance._renderPropertyList();
-
instance.propertyList.set(RECORDSET, diagramNode.getProperties());
diagramNode.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE_EDITING);
@@ -894,12 +1015,42 @@ var DiagramBuilder = A.Component.create({
}
},
+ _afterKeyEvent: function(event) {
+ var instance = this;
+
+ if (!instance.selectedNode || event.hasModifier() || !event.isKeyInSet(ESC, DELETE)) {
+ return;
+ }
+
+ if (event.isKey(ESC)) {
+ instance._onEscKey(event);
+ }
+ else if (event.isKey(DELETE)) {
+ instance._onDeleteKey(event);
+ }
+
+ event.halt();
+ },
+
_onCancel: function(event) {
var instance = this;
instance.stopEditingNode();
},
+ _onDrag: function(event) {
+ var instance = this;
+ var drag = event.target;
+
+ if (instance.isFieldsDrag(drag)) {
+ var diagramNode = A.Widget.getByNode(drag.get(DRAG_NODE));
+
+ diagramNode.get(FIELDS).each(function(anchor) {
+ anchor.alignConnectors();
+ });
+ }
+ },
+
_onDragEnd: function(event) {
var instance = this;
var drag = event.target;
@@ -918,15 +1069,56 @@ var DiagramBuilder = A.Component.create({
if (instance.isAvailableFieldsDrag(drag)) {
var availableField = drag.get(NODE).getData(AVAILABLE_FIELD);
- instance.addField({
+ var newField = instance.addField({
xy: getLeftTop(drag.lastXY, instance.dropContainer),
- type: availableField.get(TYPE)
+ type: availableField.get(TYPE),
+ fields: [{}]
});
+
+ instance.select(newField);
}
},
+ _onDeleteKey: function(event) {
+ var instance = this;
+
+ instance.selectedNode.close();
+ },
+
+ _onEscKey: function(event) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ },
+
+ _onMouseenterAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.addClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onMouseleaveAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.removeClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onNodeClick: function(event) {
+ var instance = this;
+ var diagramNode = A.Widget.getByNode(event.currentTarget);
+
+ instance.select(diagramNode);
+ },
+
_onNodeEdit: function(event) {
var instance = this;
+
+ // Only enable editing if the double clicked node is inside the node contentBox.
+ if (!event.target.ancestor(_DOT+CSS_DIAGRAM_NODE_CONTENT, true)) {
+ return;
+ }
+
var diagramNode = A.Widget.getByNode(event.currentTarget);
if (diagramNode) {
@@ -950,6 +1142,18 @@ var DiagramBuilder = A.Component.create({
}
},
+ _setTmpConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ lazyDraw: true,
+ viewport: instance.viewport
+ },
+ val
+ );
+ },
+
_setFieldsDragConfig: function(val) {
var instance = this;
var dropContainer = instance.dropContainer;
@@ -994,11 +1198,32 @@ A.DiagramBuilder = DiagramBuilder;
A.DiagramBuilder.types = {};
+var DiagramNodeOverlay = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ EXTENDS: A.Overlay,
+
+ // A.FieldSupport augment the class with "fields" attribute and util methods
+ // such as: addField, removeField. Although the attribute is called "fields" due to
+ // the augmentation, those fields are the anchors. TODO: Allow A.FieldSupport to
+ // customize the name of the attribute and method sufixes.
+ AUGMENTS: [A.FieldSupport]
+});
+
var DiagramNode = A.Component.create({
- NAME: DIAGRAM_NODE,
+ NAME: DIAGRAM_NODE_NAME,
+
+ UI_ATTRS: [FIELDS, NAME, SELECTED],
ATTRS: {
+ anchorsDragConfig: {
+ value: null,
+ setter: '_setAnchorsDragConfig',
+ validator: isObject
+ },
+
builder: {
+ setter: '_setBuilder',
validator: isDiagramBuilder
},
@@ -1008,7 +1233,7 @@ var DiagramNode = A.Component.create({
},
height: {
- value: 100
+ value: 90
},
name: {
@@ -1020,8 +1245,14 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ selected: {
+ value: false,
+ validator: isBoolean
+ },
+
strings: {
value: {
+ deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
name: 'Name',
type: 'Type'
@@ -1033,14 +1264,114 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ controlsToolbar: {
+ setter: '_setControlsToolbar',
+ validator: isObject,
+ value: null
+ },
+
width: {
- value: 200
+ value: 90
+ },
+
+ zIndex: {
+ value: 100
+ },
+
+ tabIndex: {
+ value: 1
}
},
- EXTENDS: A.Overlay,
+ EXTENDS: DiagramNodeOverlay,
+
+ buildNodeId: function(id) {
+ return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ },
prototype: {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ CONTROLS_TEMPLATE: '',
+
+ initializer: function() {
+ var instance = this;
+
+ instance._renderNodes();
+ instance._setupAnchorsDrag();
+
+ instance.after({
+ render: instance._afterRender
+ });
+
+ instance.on({
+ 'drag:drag': instance._onAnchorDrag,
+ 'drag:end': instance._onAnchorDragEnd,
+ 'drag:start': instance._onAnchorDragStart,
+ 'drop:hit': instance._onAnchorDropHit
+ });
+
+ instance.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE+_DASH+instance.get(TYPE));
+
+ // REMOVE THIS!
+ instance.set('bodyContent', instance.get(NAME));
+ },
+
+ alignAnchors: function() {
+ var instance = this;
+ var anchors = instance.get(FIELDS);
+
+ var cRegion = instance.get(BOUNDING_BOX).get(REGION),
+ dAngle = Math.floor(360/anchors.size()),
+ a = cRegion.width/2,
+ b = cRegion.height/2,
+ centerX = cRegion.left + cRegion.width/2,
+ centerY = cRegion.top + cRegion.height/2;
+
+ anchors.each(function(anchor, index) {
+ var anchorNode = anchor.get(NODE);
+ var aRegion = anchorNode.get(REGION);
+ var exy = instance._getEllipseXY(a, b, centerX, centerY, index*dAngle);
+
+ anchorNode.setXY([ exy[0] - aRegion.width/2, exy[1] - aRegion.height/2 ]);
+
+ anchor.alignConnectors();
+ });
+
+ return instance;
+ },
+
+ close: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ if (confirm(strings[DELETE_MESSAGE])) {
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.destroy();
+ });
+
+ instance.destroy();
+ }
+
+ __dump();
+
+ return instance;
+ },
+
+ createField: function(val) {
+ var instance = this;
+
+ if (!isAnchor(val)) {
+ var builder = instance.get(BUILDER);
+
+ val.diagramNode = instance;
+ val.viewport = (builder ? builder.get(VIEWPORT) : null);
+
+ val = new A.Anchor(val);
+ }
+
+ return val;
+ },
+
getLeftTop: function() {
var instance = this;
@@ -1089,31 +1420,829 @@ var DiagramNode = A.Component.create({
];
},
+ _afterRender: function(event) {
+ var instance = this;
+
+ instance.alignAnchors();
+
+ instance._renderControls();
+ },
+
_getContainer: function() {
var instance = this;
return (instance.get(BUILDER).dropContainer || instance.get(BOUNDING_BOX).get(PARENT_NODE));
},
- _uiSetXY : function(val) {
+ _getEllipseXY: function(a, b, centerX, centerY, angle) {
+ var t = angle*Math.PI/180;
+
+ return [ centerX + a*Math.cos(t), centerY - b*Math.sin(t) ];
+ },
+
+ _handleAddAnchorEvent: function(event) {
var instance = this;
- var containerXY = instance._getContainer().getXY();
- this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
- }
- }
-});
+ instance.addField({});
-DiagramNode.buildNodeId = function(id) {
- return DIAGRAM_NODE + _DOLLAR + FIELD + _DOLLAR + id;
-};
+ // event.halt();
+ },
-A.DiagramNode = DiagramNode;
+ _handleAddTaskEvent: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
-A.DiagramBuilder.types['node'] = A.DiagramNode;
+ var diagramNode = new A.DiagramNode({
+ type: NODE,
+ xy: [100, 100] // TODO - find best position?
+ });
-}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
+ builder.addField(diagramNode);
+
+ var source = instance.addField({});
+ var target = diagramNode.addField({});
+ source.connect(target);
+ },
+
+ _handleCloseEvent: function(event) {
+ var instance = this;
+
+ instance.close();
+ },
+
+ _onAnchorDrag: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P2, event.target.get(DRAG_NODE).getCenterXY());
+ },
+
+ _onAnchorDragEnd: function(event) {
+ var instance = this;
+ var shape = instance.get(BUILDER).tmpConnector.shape;
+
+ shape.clear();
+ shape.end();
+ },
+
+ _onAnchorDragStart: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P1, event.target.get(NODE).getCenterXY());
+ },
+
+ _onAnchorDropHit: function(event) {
+ var instance = this;
+ var source = A.Anchor.getAnchorByNode(event.drag.get(NODE));
+ var target = A.Anchor.getAnchorByNode(event.drop.get(NODE));
+
+ source.connect(target);
+
+ __dump();
+ },
+
+ _renderControls: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.controlsNode = A.Node.create(instance.CONTROLS_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderNodes: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.anchorWrapper = A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderControlsToolbar: function(event) {
+ var instance = this;
+
+ instance.controlsToolbar = new A.Toolbar(
+ instance.get(CONTROLS_TOOLBAR)
+ )
+ .render(instance.controlsNode);
+ },
+
+ _setBuilder: function(val) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.set(VIEWPORT, val.get(VIEWPORT));
+ });
+
+ return val;
+ },
+
+ _setAnchorsDragConfig: function(val) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ return A.merge(
+ {
+ bubbleTargets: instance,
+ container: instance.anchorWrapper,
+ dragConfig: {
+ groups: [ANCHORS],
+ plugins: [
+ {
+ cfg: {
+ constrain: (builder ? builder.get(VIEWPORT) : null)
+ },
+ fn: A.Plugin.DDConstrained
+ },
+ {
+ cfg: {
+ scrollDelay: 150
+ },
+ fn: A.Plugin.DDWinScroll
+ },
+ {
+ cfg: {
+ moveOnEnd: false
+ },
+ fn: A.Plugin.DDProxy
+ }
+ ]
+ },
+ nodes: _DOT+CSS_DB_ANCHOR_NODE,
+ target: true
+ },
+ val || {}
+ );
+ },
+
+ _setupAnchorsDrag: function() {
+ var instance = this;
+
+ instance.anchorsDrag = new A.DD.Delegate(
+ instance.get(ANCHORS_DRAG_CONFIG)
+ );
+ },
+
+ _setControlsToolbar: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK
+ },
+ {
+ handler: A.bind(instance._handleAddTaskEvent, instance),
+ icon: SHUFFLE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL
+ }
+ ]
+ },
+ val
+ );
+ },
+
+ _uiSetFields: function(val) {
+ var instance = this;
+
+ if (instance.get(RENDERED)) {
+ instance.alignAnchors();
+
+ setTimeout(function() {
+ instance.anchorsDrag.syncTargets();
+ }, 50);
+ }
+ },
+
+ _uiSetName: function(val) {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetSelected: function(val) {
+ var instance = this;
+
+ instance.get(BOUNDING_BOX).toggleClass(CSS_DIAGRAM_NODE_SELECTED, val);
+
+ if (val && !instance.controlsToolbar) {
+ instance._renderControlsToolbar();
+ }
+
+ // if (instance.get(RENDERED)) {
+ // instance.alignAnchors();
+ // }
+ },
+
+ _uiSetXY : function(val) {
+ var instance = this;
+ var containerXY = instance._getContainer().getXY();
+
+ this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
+ }
+ }
+});
+
+A.DiagramNode = DiagramNode;
+
+A.DiagramBuilder.types[NODE] = A.DiagramNode;
+
+A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNode
+});
+
+A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+// TODO deletar anchors OK
+// TODO deletar connections (delete) OK
+// TODO Adicionar overlay de controles OK
+// TODO syncTargets dd delegate
+
+
+// TODO gerar XML
+// TODO reposicionar setas?
+// TODO Adicionar groups/validation for connection
+
+}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
+AUI.add('aui-diagram-builder-connector', function(A) {
+var Lang = A.Lang,
+ isArray = Lang.isArray,
+ isBoolean = Lang.isBoolean,
+ isNumber = Lang.isNumber,
+ isObject = Lang.isObject,
+ isString = Lang.isString,
+
+ YArray = A.Array,
+
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
+ isArrayList = function(val) {
+ return (val instanceof A.ArrayList);
+ },
+
+ ANCHOR = 'anchor',
+ ARROW_POINTS = 'arrowPoints',
+ BODY = 'body',
+ BOUNDING_BOX = 'boundingBox',
+ BUILDER = 'builder',
+ COLOR = 'color',
+ CONNECTOR = 'connector',
+ DATA_ANCHOR = 'dataAnchor',
+ DIAGRAM = 'diagram',
+ DIAGRAM_NODE = 'diagramNode',
+ HEIGHT = 'height',
+ ID = 'id',
+ LAZY_DRAW = 'lazyDraw',
+ MAX_SOURCES = 'maxSources',
+ MAX_TARGETS = 'maxTargets',
+ NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
+ PATH = 'path',
+ SHAPE = 'shape',
+ SOURCES = 'sources',
+ TARGETS = 'targets',
+ VIEWPORT = 'viewport',
+ WIDTH = 'width',
+ WRAPPER = 'wrapper',
+
+ _DOT = '.',
+
+ AgetClassName = A.getClassName,
+
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
+
+A.PolygonUtil = {
+ ARROW_POINTS: [
+ [ -12, -6 ],
+ [ -8, 0 ],
+ [ -12, 6 ],
+ [ 6, 0 ]
+ ],
+
+ drawLineArrow: function(shape, x1, y1, x2, y2, arrowPoints) {
+ var instance = this;
+
+ shape.moveTo(x1, y1);
+ shape.lineTo(x2, y2);
+
+ var angle = Math.atan2(y2-y1, x2-x1), centerX = (x2+x1)/2, centerY = (y2+y1)/2;
+
+ instance.drawPolygon(
+ shape,
+ instance.translatePoints(instance.rotatePoints(arrowPoints || instance.ARROW_POINTS, angle), centerX, centerY)
+ );
+ },
+
+ drawPolygon: function(shape, points) {
+ var instance = this;
+
+ shape.moveTo(points[0][0], points[0][1]);
+
+ YArray.each(points, function(p, i) {
+ if (i > 0) {
+ shape.lineTo(points[i][0], points[i][1]);
+ }
+ });
+
+ shape.lineTo(points[0][0], points[0][1]);
+ shape.end();
+ },
+
+ translatePoints: function(points, x, y) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push([ points[i][0] + x, points[i][1] + y ]);
+ });
+
+ return xy;
+ },
+
+ rotatePoints: function(points, angle) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push(instance.rotatePoint(angle, points[i][0], points[i][1]));
+ });
+
+ return xy;
+ },
+
+ rotatePoint: function(angle, x, y) {
+ return [
+ (x * Math.cos(angle)) - (y * Math.sin(angle)),
+ (x * Math.sin(angle)) + (y * Math.cos(angle))
+ ];
+ }
+};
+
+A.Connector = A.Base.create('line', A.Base, [], {
+ graphics: null,
+ shape: null,
+
+ initializer: function(config) {
+ var instance = this;
+
+ instance.after({
+ p1Change: instance.draw,
+ p2Change: instance.draw
+ });
+
+ instance._initGraphics();
+ instance._initShapes();
+
+ if (!instance.get(LAZY_DRAW)) {
+ instance.draw();
+ }
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.graphics.destroy();
+ },
+
+ draw: function() {
+ var instance = this;
+ var shape = instance.shape;
+
+ var c1 = instance.getCoordinate(instance.get(P1));
+ var c2 = instance.getCoordinate(instance.get(P2));
+
+ shape.clear();
+
+ A.PolygonUtil.drawLineArrow(shape, c1[0], c1[1], c2[0], c2[1], instance.get(ARROW_POINTS));
+ },
+
+ getCoordinate: function(p) {
+ var instance = this;
+ var xy = instance.get(VIEWPORT).getXY();
+
+ return [ p[0] - xy[0], p[1] - xy[1] ];
+ },
+
+ _initGraphics: function() {
+ var instance = this;
+
+ var graphics = new A.Graphic({
+ width: instance.get(WIDTH),
+ height: instance.get(HEIGHT),
+ render: instance.get(VIEWPORT)
+ });
+
+ instance.graphics = graphics;
+ },
+
+ _initShapes: function() {
+ var instance = this;
+
+ instance.shape = instance.graphics.getShape(
+ instance.get(SHAPE)
+ );
+ },
+
+ _setShape: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ type: PATH,
+ stroke: {
+ color: instance.get(COLOR),
+ weight: 2
+ },
+ fill: {
+ color: instance.get(COLOR)
+ }
+ },
+ val
+ );
+ }
+},{
+ ATTRS: {
+ color: {
+ value: '#666',
+ validator: isString
+ },
+
+ lazyDraw: {
+ value: false,
+ validator: isBoolean
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ },
+
+ shape: {
+ value: null,
+ setter: '_setShape'
+ },
+
+ arrowPoints: {
+ value: A.PolygonUtil.ARROW_POINTS
+ },
+
+ p1: {
+ value: [0, 0],
+ validator: isArray
+ },
+
+ p2: {
+ value: [0, 0],
+ validator: isArray
+ }
+ }
+});
+
+A.Anchor = A.Base.create('anchor', A.Base, [], {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ NODE_TEMPLATE: '',
+
+ connectors: null,
+
+ initializer: function() {
+ var instance = this;
+
+ instance.connectors = {};
+
+ instance._renderNode();
+
+ instance.connectTargets();
+
+ instance.after({
+ targetsChange: instance._afterTargetsChange
+ });
+ },
+
+ addSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ },
+
+ addTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ },
+
+ alignConnectors: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ var tConnector = instance.getConnector(target);
+
+ if (tConnector) {
+ tConnector.set(P1, instance.getCenterXY());
+ tConnector.set(P2, target.getCenterXY());
+ }
+ });
+
+ instance.get(SOURCES).each(function(source) {
+ var sConnector = source.getConnector(instance);
+
+ if (sConnector) {
+ sConnector.set(P1, source.getCenterXY());
+ sConnector.set(P2, instance.getCenterXY());
+ }
+ });
+
+ return instance;
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.disconnectTargets();
+ instance.disconnectSources();
+
+ instance.get(NODE).remove();
+ },
+
+ connect: function(target) {
+ var instance = this;
+
+ instance.addTarget(target);
+
+ if (!instance.isConnected(target)) {
+ var tConnector = target.get(CONNECTOR);
+
+ tConnector.p1 = instance.getCenterXY();
+ tConnector.p2 = target.getCenterXY();
+
+ instance.connectors[target.get(ID)] = new A.Connector(tConnector);
+ }
+
+ return instance;
+ },
+
+ connectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(A.bind(instance.connect, instance));
+
+ return instance;
+ },
+
+ disconnect: function(target) {
+ var instance = this;
+
+ instance.getConnector(target).destroy();
+
+ instance.removeTarget(target);
+ target.removeSource(instance);
+ },
+
+ disconnectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ instance.disconnect(target);
+ });
+
+ return instance;
+ },
+
+ disconnectSources: function() {
+ var instance = this;
+
+ instance.get(SOURCES).each(function(source) {
+ source.disconnect(instance);
+ });
+
+ return instance;
+ },
+
+ getCenterXY: function() {
+ var instance = this;
+
+ return instance.get(NODE).getCenterXY();
+ },
+
+ getConnector: function(target) {
+ var instance = this;
+
+ return instance.connectors[target.get(ID)];
+ },
+
+ isConnected: function(target) {
+ var instance = this;
+
+ return instance.connectors.hasOwnProperty(target.get(ID));
+ },
+
+ updateSources: function(sources) {
+ var instance = this;
+
+ instance.set(SOURCES, sources);
+
+ return instance;
+ },
+
+ updateTargets: function(targets) {
+ var instance = this;
+
+ instance.set(TARGETS, targets);
+
+ return instance;
+ },
+
+ removeSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source)
+ );
+ },
+
+ removeTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target)
+ );
+ },
+
+ _afterActiveChange: function(event) {
+ var instance = this;
+
+ instance._uiSetActive(event.newVal);
+ },
+
+ _afterTargetsChange: function(event) {
+ var instance = this;
+
+ // TODO - event.prevVal is always equal to event.newVal, review this
+ // logic below, references between anchors needs to be cleaned up otherwise
+ // will store the wrong relationship between nodes.
+
+ event.prevVal.each(function(anchor) {
+ anchor.removeSource(instance);
+ });
+
+ event.newVal.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+ },
+
+ _renderNode: function() {
+ var instance = this;
+ var diagramNode = instance.get(DIAGRAM_NODE);
+ var container = diagramNode.get(BOUNDING_BOX);
+
+ instance.wrapper = container.one(_DOT+CSS_DB_ANCHOR_NODE_WRAPPER) ||
+ A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE);
+
+ instance.wrapper.appendTo(container).appendChild(instance.get(NODE));
+ },
+
+ _setConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ viewport: instance.get(VIEWPORT)
+ },
+ val
+ );
+ },
+
+ _setSources: function(val) {
+ var instance = this;
+
+ return instance._setAnchors(val);
+ },
+
+ _setTargets: function(val) {
+ var instance = this;
+
+ val = instance._setAnchors(val);
+
+ val.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+
+ return val;
+ },
+
+ _setAnchors: function(val) {
+ var instance = this;
+
+ if (!isArrayList(val)) {
+ var targets = [];
+
+ A.Array.each(val, function(target) {
+ if (isString(target)) {
+ // TODO - need this?
+ target = A.Anchor.getAnchorByNode(target);
+ }
+
+ targets.push( isAnchor(target) ? target : new A.Anchor(target) );
+ });
+
+ val = new A.ArrayList(targets);
+ }
+
+ return val;
+ },
+
+ _setNode: function(val) {
+ var instance = this;
+ var id = instance.get(ID);
+
+ return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ }
+},{
+ ATTRS: {
+ diagramNode: {
+ },
+
+ connector: {
+ setter: '_setConnector',
+ value: {},
+ validator: isObject
+ },
+
+ id: {
+ readOnly: true,
+ valueFn: function() {
+ return A.guid();
+ }
+ },
+
+ maxSources: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ maxTargets: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ node: {
+ setter: '_setNode',
+ valueFn: function() {
+ var instance = this;
+
+ return A.Node.create(instance.NODE_TEMPLATE);
+ }
+ },
+
+ sources: {
+ value: [],
+ setter: '_setSources',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ targets: {
+ value: [],
+ setter: '_setTargets',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ }
+ },
+
+ getAnchorByNode: function(node) {
+ return A.one(node).getData(DATA_ANCHOR);
+ }
+});
+
+}, '@VERSION@' ,{requires:['aui-base','arraylist-add','arraylist-filter','json','graphics','dd'], skinnable:true});
-AUI.add('aui-diagram-builder', function(A){}, '@VERSION@' ,{use:['aui-diagram-builder-base','aui-diagram-builder-impl'], skinnable:true});
+AUI.add('aui-diagram-builder', function(A){}, '@VERSION@' ,{use:['aui-diagram-builder-base','aui-diagram-builder-impl','aui-diagram-builder-connector'], skinnable:true});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-impl-debug.js b/build/aui-diagram-builder/aui-diagram-builder-impl-debug.js
index d7ee02a9cb2..47d1c299466 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-impl-debug.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-impl-debug.js
@@ -3,6 +3,7 @@ var Lang = A.Lang,
isArray = Lang.isArray,
isObject = Lang.isObject,
isString = Lang.isString,
+ isBoolean = Lang.isBoolean,
AArray = A.Array,
@@ -14,6 +15,10 @@ var Lang = A.Lang,
return (val instanceof A.DiagramNode);
},
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
getLeftTop = function(container, node) {
var nodeXY = isArray(node) ? node : node.getXY();
var containerXY = isArray(container) ? container : container.getXY();
@@ -23,44 +28,123 @@ var Lang = A.Lang,
});
},
+ ANCHOR = 'anchor',
+ ANCHORS = 'anchors',
+ ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
+ CANCEL = 'cancel',
+ CLICK = 'click',
+ CONTENT = 'content',
+ CONTROLS = 'controls',
+ CONTROLS_TOOLBAR = 'controlsToolbar',
DATA = 'data',
DBLCLICK = 'dblclick',
+ DELETE = 'delete',
+ DELETE_MESSAGE = 'deleteMessage',
DESCRIPTION = 'description',
DIAGRAM = 'diagram',
- DIAGRAM_BUILDER = 'diagram-builder',
- DIAGRAM_NODE = 'diagram-node',
+ DIAGRAM_BUILDER_NAME = 'diagram-builder',
+ DIAGRAM_NODE = 'diagramNode',
+ DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ ESC = 'esc',
+ FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
+ HOVER = 'hover',
+ KEYDOWN = 'keydown',
+ LINK = 'link',
+ MOUSEENTER = 'mouseenter',
+ MOUSELEAVE = 'mouseleave',
NAME = 'name',
NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
PARENT_NODE = 'parentNode',
RECORDS = 'records',
RECORDSET = 'recordset',
+ REGION = 'region',
RENDERED = 'rendered',
+ SELECTED = 'selected',
+ SHUFFLE = 'shuffle',
+ TASK = 'task',
+ TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
+ VIEWPORT = 'viewport',
+ WRAPPER = 'wrapper',
XY = 'xy',
_DOT = '.',
+ _DOLLAR = '$',
_EMPTY_STR = '',
+ _DASH = '-',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_CONTROLS = AgetClassName(DIAGRAM, BUILDER, CONTROLS),
+ CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE),
+ CSS_DIAGRAM_NODE_CONTENT = AgetClassName(DIAGRAM, NODE, CONTENT),
CSS_DIAGRAM_NODE_EDITING = AgetClassName(DIAGRAM, NODE, EDITING),
- CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE);
+ CSS_DIAGRAM_NODE_SELECTED = AgetClassName(DIAGRAM, NODE, SELECTED);
+
+// REMOVE THIS!
+var __dump = function() {
+ var PAD = ' ', BR = '
';
+
+ A.all('.aui-diagram-node').each(function(n) {
+ var b = _EMPTY_STR,
+ dn = A.Widget.getByNode(n),
+ dnName = dn.get('name'),
+ dnBB = dn.get('boundingBox'),
+ log = dnBB.one('.log') || A.Node.create('').appendTo(dnBB);
+
+ b += dnName + BR;
+
+ dn.get(FIELDS).each(function(a) {
+ b += PAD + 'a: ' + a.get('id') + BR;
+
+ a.get('targets').each(function(t) {
+ var tdn = t.get(DIAGRAM_NODE);
+
+ t.get('node').setContent(t.get('id'));
+
+ b += PAD + PAD + 't: ' + tdn.get('name') + ' (s: ' + t.get('id') + ')' + BR;
+ });
+
+ a.get('sources').each(function(s) {
+ var sdn = s.get(DIAGRAM_NODE);
+
+ s.get('node').setContent(s.get('id'));
+
+ b += PAD + PAD + 's: ' + sdn.get('name') + ' (t: ' + s.get('id') + ')' + BR;
+ });
+ });
+
+ log.setContent(b);
+ });
+};
+// END.
var DiagramBuilder = A.Component.create({
- NAME: DIAGRAM_BUILDER,
+ NAME: DIAGRAM_BUILDER_NAME,
ATTRS: {
fieldsDragConfig: {
value: null,
setter: '_setFieldsDragConfig',
validator: isObject
+ },
+
+ tmpConnector: {
+ setter: '_setTmpConnector',
+ value: {},
+ validator: isObject
}
},
@@ -77,12 +161,18 @@ var DiagramBuilder = A.Component.create({
instance.on({
cancel: instance._onCancel,
+ 'drag:drag': instance._onDrag,
'drag:end': instance._onDragEnd,
'drop:hit': instance._onDropHit,
save: instance._onSave
});
+ instance.handlerKeyDown = A.getDoc().on(KEYDOWN, A.bind(instance._afterKeyEvent, instance));
+
+ instance.dropContainer.delegate(CLICK, A.bind(instance._onNodeClick, instance), _DOT+CSS_DIAGRAM_NODE);
instance.dropContainer.delegate(DBLCLICK, A.bind(instance._onNodeEdit, instance), _DOT+CSS_DIAGRAM_NODE);
+ instance.dropContainer.delegate(MOUSEENTER, A.bind(instance._onMouseenterAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
+ instance.dropContainer.delegate(MOUSELEAVE, A.bind(instance._onMouseleaveAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
},
syncUI: function() {
@@ -91,12 +181,17 @@ var DiagramBuilder = A.Component.create({
A.DiagramBuilder.superclass.syncUI.apply(this, arguments);
instance._setupFieldsDrag();
+
+ instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
createField: function(val) {
var instance = this;
if (!isDiagramNode(val)) {
+ // val.bubbleTargets = instance;
+ val.builder = instance;
+ val.viewport = instance.get(VIEWPORT);
val = new (instance.getFieldClass(val.type || NODE))(val);
}
@@ -126,21 +221,31 @@ var DiagramBuilder = A.Component.create({
return (drag === fieldsDrag.dd);
},
- plotFields: function() {
+ plotField: function(field) {
var instance = this;
- var fields = instance.get(FIELDS);
- fields.each(function(field) {
- instance.plotField(field);
- });
+ if (!field.get(RENDERED)) {
+ field.render(instance.dropContainer);
+ }
},
- plotField: function(field) {
+ unselectAll: function() {
var instance = this;
+ var selectedNode = instance.selectedNode;
- if (!field.get(RENDERED)) {
- field.render(instance.dropContainer);
+ if (selectedNode) {
+ selectedNode.set(SELECTED, false);
}
+
+ instance.selectedNode = null;
+ },
+
+ select: function(diagramNode) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
startEditingNode: function(diagramNode) {
@@ -151,8 +256,6 @@ var DiagramBuilder = A.Component.create({
instance.tabView.selectTab(A.DiagramBuilder.SETTINGS_TAB);
- // instance._renderPropertyList();
-
instance.propertyList.set(RECORDSET, diagramNode.getProperties());
diagramNode.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE_EDITING);
@@ -174,12 +277,42 @@ var DiagramBuilder = A.Component.create({
}
},
+ _afterKeyEvent: function(event) {
+ var instance = this;
+
+ if (!instance.selectedNode || event.hasModifier() || !event.isKeyInSet(ESC, DELETE)) {
+ return;
+ }
+
+ if (event.isKey(ESC)) {
+ instance._onEscKey(event);
+ }
+ else if (event.isKey(DELETE)) {
+ instance._onDeleteKey(event);
+ }
+
+ event.halt();
+ },
+
_onCancel: function(event) {
var instance = this;
instance.stopEditingNode();
},
+ _onDrag: function(event) {
+ var instance = this;
+ var drag = event.target;
+
+ if (instance.isFieldsDrag(drag)) {
+ var diagramNode = A.Widget.getByNode(drag.get(DRAG_NODE));
+
+ diagramNode.get(FIELDS).each(function(anchor) {
+ anchor.alignConnectors();
+ });
+ }
+ },
+
_onDragEnd: function(event) {
var instance = this;
var drag = event.target;
@@ -198,15 +331,56 @@ var DiagramBuilder = A.Component.create({
if (instance.isAvailableFieldsDrag(drag)) {
var availableField = drag.get(NODE).getData(AVAILABLE_FIELD);
- instance.addField({
+ var newField = instance.addField({
xy: getLeftTop(drag.lastXY, instance.dropContainer),
- type: availableField.get(TYPE)
+ type: availableField.get(TYPE),
+ fields: [{}]
});
+
+ instance.select(newField);
}
},
+ _onDeleteKey: function(event) {
+ var instance = this;
+
+ instance.selectedNode.close();
+ },
+
+ _onEscKey: function(event) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ },
+
+ _onMouseenterAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.addClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onMouseleaveAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.removeClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onNodeClick: function(event) {
+ var instance = this;
+ var diagramNode = A.Widget.getByNode(event.currentTarget);
+
+ instance.select(diagramNode);
+ },
+
_onNodeEdit: function(event) {
var instance = this;
+
+ // Only enable editing if the double clicked node is inside the node contentBox.
+ if (!event.target.ancestor(_DOT+CSS_DIAGRAM_NODE_CONTENT, true)) {
+ return;
+ }
+
var diagramNode = A.Widget.getByNode(event.currentTarget);
if (diagramNode) {
@@ -230,6 +404,18 @@ var DiagramBuilder = A.Component.create({
}
},
+ _setTmpConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ lazyDraw: true,
+ viewport: instance.viewport
+ },
+ val
+ );
+ },
+
_setFieldsDragConfig: function(val) {
var instance = this;
var dropContainer = instance.dropContainer;
@@ -274,11 +460,32 @@ A.DiagramBuilder = DiagramBuilder;
A.DiagramBuilder.types = {};
+var DiagramNodeOverlay = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ EXTENDS: A.Overlay,
+
+ // A.FieldSupport augment the class with "fields" attribute and util methods
+ // such as: addField, removeField. Although the attribute is called "fields" due to
+ // the augmentation, those fields are the anchors. TODO: Allow A.FieldSupport to
+ // customize the name of the attribute and method sufixes.
+ AUGMENTS: [A.FieldSupport]
+});
+
var DiagramNode = A.Component.create({
- NAME: DIAGRAM_NODE,
+ NAME: DIAGRAM_NODE_NAME,
+
+ UI_ATTRS: [FIELDS, NAME, SELECTED],
ATTRS: {
+ anchorsDragConfig: {
+ value: null,
+ setter: '_setAnchorsDragConfig',
+ validator: isObject
+ },
+
builder: {
+ setter: '_setBuilder',
validator: isDiagramBuilder
},
@@ -288,7 +495,7 @@ var DiagramNode = A.Component.create({
},
height: {
- value: 100
+ value: 90
},
name: {
@@ -300,8 +507,14 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ selected: {
+ value: false,
+ validator: isBoolean
+ },
+
strings: {
value: {
+ deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
name: 'Name',
type: 'Type'
@@ -313,14 +526,114 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ controlsToolbar: {
+ setter: '_setControlsToolbar',
+ validator: isObject,
+ value: null
+ },
+
width: {
- value: 200
+ value: 90
+ },
+
+ zIndex: {
+ value: 100
+ },
+
+ tabIndex: {
+ value: 1
}
},
- EXTENDS: A.Overlay,
+ EXTENDS: DiagramNodeOverlay,
+
+ buildNodeId: function(id) {
+ return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ },
prototype: {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ CONTROLS_TEMPLATE: '',
+
+ initializer: function() {
+ var instance = this;
+
+ instance._renderNodes();
+ instance._setupAnchorsDrag();
+
+ instance.after({
+ render: instance._afterRender
+ });
+
+ instance.on({
+ 'drag:drag': instance._onAnchorDrag,
+ 'drag:end': instance._onAnchorDragEnd,
+ 'drag:start': instance._onAnchorDragStart,
+ 'drop:hit': instance._onAnchorDropHit
+ });
+
+ instance.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE+_DASH+instance.get(TYPE));
+
+ // REMOVE THIS!
+ instance.set('bodyContent', instance.get(NAME));
+ },
+
+ alignAnchors: function() {
+ var instance = this;
+ var anchors = instance.get(FIELDS);
+
+ var cRegion = instance.get(BOUNDING_BOX).get(REGION),
+ dAngle = Math.floor(360/anchors.size()),
+ a = cRegion.width/2,
+ b = cRegion.height/2,
+ centerX = cRegion.left + cRegion.width/2,
+ centerY = cRegion.top + cRegion.height/2;
+
+ anchors.each(function(anchor, index) {
+ var anchorNode = anchor.get(NODE);
+ var aRegion = anchorNode.get(REGION);
+ var exy = instance._getEllipseXY(a, b, centerX, centerY, index*dAngle);
+
+ anchorNode.setXY([ exy[0] - aRegion.width/2, exy[1] - aRegion.height/2 ]);
+
+ anchor.alignConnectors();
+ });
+
+ return instance;
+ },
+
+ close: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ if (confirm(strings[DELETE_MESSAGE])) {
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.destroy();
+ });
+
+ instance.destroy();
+ }
+
+ __dump();
+
+ return instance;
+ },
+
+ createField: function(val) {
+ var instance = this;
+
+ if (!isAnchor(val)) {
+ var builder = instance.get(BUILDER);
+
+ val.diagramNode = instance;
+ val.viewport = (builder ? builder.get(VIEWPORT) : null);
+
+ val = new A.Anchor(val);
+ }
+
+ return val;
+ },
+
getLeftTop: function() {
var instance = this;
@@ -369,12 +682,225 @@ var DiagramNode = A.Component.create({
];
},
+ _afterRender: function(event) {
+ var instance = this;
+
+ instance.alignAnchors();
+
+ instance._renderControls();
+ },
+
_getContainer: function() {
var instance = this;
return (instance.get(BUILDER).dropContainer || instance.get(BOUNDING_BOX).get(PARENT_NODE));
},
+ _getEllipseXY: function(a, b, centerX, centerY, angle) {
+ var t = angle*Math.PI/180;
+
+ return [ centerX + a*Math.cos(t), centerY - b*Math.sin(t) ];
+ },
+
+ _handleAddAnchorEvent: function(event) {
+ var instance = this;
+
+ instance.addField({});
+
+ // event.halt();
+ },
+
+ _handleAddTaskEvent: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ var diagramNode = new A.DiagramNode({
+ type: NODE,
+ xy: [100, 100] // TODO - find best position?
+ });
+
+ builder.addField(diagramNode);
+
+ var source = instance.addField({});
+ var target = diagramNode.addField({});
+ source.connect(target);
+ },
+
+ _handleCloseEvent: function(event) {
+ var instance = this;
+
+ instance.close();
+ },
+
+ _onAnchorDrag: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P2, event.target.get(DRAG_NODE).getCenterXY());
+ },
+
+ _onAnchorDragEnd: function(event) {
+ var instance = this;
+ var shape = instance.get(BUILDER).tmpConnector.shape;
+
+ shape.clear();
+ shape.end();
+ },
+
+ _onAnchorDragStart: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P1, event.target.get(NODE).getCenterXY());
+ },
+
+ _onAnchorDropHit: function(event) {
+ var instance = this;
+ var source = A.Anchor.getAnchorByNode(event.drag.get(NODE));
+ var target = A.Anchor.getAnchorByNode(event.drop.get(NODE));
+
+ source.connect(target);
+
+ __dump();
+ },
+
+ _renderControls: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.controlsNode = A.Node.create(instance.CONTROLS_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderNodes: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.anchorWrapper = A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderControlsToolbar: function(event) {
+ var instance = this;
+
+ instance.controlsToolbar = new A.Toolbar(
+ instance.get(CONTROLS_TOOLBAR)
+ )
+ .render(instance.controlsNode);
+ },
+
+ _setBuilder: function(val) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.set(VIEWPORT, val.get(VIEWPORT));
+ });
+
+ return val;
+ },
+
+ _setAnchorsDragConfig: function(val) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ return A.merge(
+ {
+ bubbleTargets: instance,
+ container: instance.anchorWrapper,
+ dragConfig: {
+ groups: [ANCHORS],
+ plugins: [
+ {
+ cfg: {
+ constrain: (builder ? builder.get(VIEWPORT) : null)
+ },
+ fn: A.Plugin.DDConstrained
+ },
+ {
+ cfg: {
+ scrollDelay: 150
+ },
+ fn: A.Plugin.DDWinScroll
+ },
+ {
+ cfg: {
+ moveOnEnd: false
+ },
+ fn: A.Plugin.DDProxy
+ }
+ ]
+ },
+ nodes: _DOT+CSS_DB_ANCHOR_NODE,
+ target: true
+ },
+ val || {}
+ );
+ },
+
+ _setupAnchorsDrag: function() {
+ var instance = this;
+
+ instance.anchorsDrag = new A.DD.Delegate(
+ instance.get(ANCHORS_DRAG_CONFIG)
+ );
+ },
+
+ _setControlsToolbar: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK
+ },
+ {
+ handler: A.bind(instance._handleAddTaskEvent, instance),
+ icon: SHUFFLE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL
+ }
+ ]
+ },
+ val
+ );
+ },
+
+ _uiSetFields: function(val) {
+ var instance = this;
+
+ if (instance.get(RENDERED)) {
+ instance.alignAnchors();
+
+ setTimeout(function() {
+ instance.anchorsDrag.syncTargets();
+ }, 50);
+ }
+ },
+
+ _uiSetName: function(val) {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetSelected: function(val) {
+ var instance = this;
+
+ instance.get(BOUNDING_BOX).toggleClass(CSS_DIAGRAM_NODE_SELECTED, val);
+
+ if (val && !instance.controlsToolbar) {
+ instance._renderControlsToolbar();
+ }
+
+ // if (instance.get(RENDERED)) {
+ // instance.alignAnchors();
+ // }
+ },
+
_uiSetXY : function(val) {
var instance = this;
var containerXY = instance._getContainer().getXY();
@@ -384,12 +910,32 @@ var DiagramNode = A.Component.create({
}
});
-DiagramNode.buildNodeId = function(id) {
- return DIAGRAM_NODE + _DOLLAR + FIELD + _DOLLAR + id;
-};
-
A.DiagramNode = DiagramNode;
-A.DiagramBuilder.types['node'] = A.DiagramNode;
+A.DiagramBuilder.types[NODE] = A.DiagramNode;
+
+A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNode
+});
+
+A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+// TODO deletar anchors OK
+// TODO deletar connections (delete) OK
+// TODO Adicionar overlay de controles OK
+// TODO syncTargets dd delegate
+
+
+// TODO gerar XML
+// TODO reposicionar setas?
+// TODO Adicionar groups/validation for connection
}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-impl-min.js b/build/aui-diagram-builder/aui-diagram-builder-impl-min.js
index 79fe9e45d4e..89a00c8e6a2 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-impl-min.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-impl-min.js
@@ -1 +1,2 @@
-AUI.add("aui-diagram-builder-impl",function(h){var K=h.Lang,u=K.isArray,E=K.isObject,e=K.isString,o=h.Array,y=function(A){return(A instanceof h.DiagramBuilderBase);},p=function(A){return(A instanceof h.DiagramNode);},n=function(A,N){var M=u(N)?N:N.getXY();var O=u(A)?A:A.getXY();return o.map(O,function(Q,P){return Math.max(0,Q-M[P]);});},B="availableField",I="boundingBox",L="builder",g="data",t="dblclick",a="description",C="diagram",d="diagram-builder",v="diagram-node",c="dragNode",b="editing",x="fields",s="fieldsDragConfig",z="name",J="node",f="parentNode",i="records",D="recordset",G="rendered",j="type",k="xy",r=".",q="",w=h.getClassName,l=w(C,J,b),F=w(C,J);var m=h.Component.create({NAME:d,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:E}},EXTENDS:h.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.dropContainer.delegate(t,h.bind(A._onNodeEdit,A),r+F);},syncUI:function(){var A=this;h.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();},createField:function(M){var A=this;if(!p(M)){M=new (A.getFieldClass(M.type||J))(M);}M.set(L,A);return M;},getFieldClass:function(N){var A=this;var M=h.DiagramBuilder.types[N];if(M){return M;}else{h.log("The field type: ["+N+"] couldn't be found.");return null;}},isFieldsDrag:function(N){var A=this;var M=A.fieldsDrag;return(N===M.dd);},plotFields:function(){var M=this;var A=M.get(x);A.each(function(N){M.plotField(N);});},plotField:function(M){var A=this;if(!M.get(G)){M.render(A.dropContainer);}},startEditingNode:function(M){var A=this;if(M){A.stopEditingNode();A.tabView.selectTab(h.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(D,M.getProperties());M.get(I).addClass(l);A.editNode=M;}},stopEditingNode:function(N){var A=this;var M=N||A.editNode;if(M){A.tabView.selectTab(h.DiagramBuilder.FIELDS_TAB);M.get(I).removeClass(l);A.editNode=null;}},_onCancel:function(M){var A=this;A.stopEditingNode();},_onDragEnd:function(N){var A=this;var M=N.target;if(A.isFieldsDrag(M)){var O=h.Widget.getByNode(M.get(c));O.set(k,O.getLeftTop());}},_onDropHit:function(N){var A=this;var M=N.drag;if(A.isAvailableFieldsDrag(M)){var O=M.get(J).getData(B);A.addField({xy:n(M.lastXY,A.dropContainer),type:O.get(j)});}},_onNodeEdit:function(M){var A=this;var N=h.Widget.getByNode(M.currentTarget);if(N){A.startEditingNode(N);}},_onSave:function(N){var A=this;var M=A.editNode;var O=A.propertyList.get(D);if(M){o.each(O.get(i),function(P){var Q=P.get(g);M.set(Q.attributeName,Q.value);});A.stopEditingNode(M);}},_setFieldsDragConfig:function(N){var A=this;var M=A.dropContainer;return h.merge({bubbleTargets:A,container:M,dragConfig:{plugins:[{cfg:{constrain:M},fn:h.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:h.Plugin.DDWinScroll}]},nodes:r+F},N||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new h.DD.Delegate(A.get(s));}}});h.DiagramBuilder=m;h.DiagramBuilder.types={};var H=h.Component.create({NAME:v,ATTRS:{builder:{validator:y},description:{value:q,validator:e},height:{value:100},name:{valueFn:function(){var A=this;return A.get(j)+(++h.Env._uidx);},validator:e},strings:{value:{description:"Description",name:"Name",type:"Type"}},type:{value:J,validator:e},width:{value:200}},EXTENDS:h.Overlay,prototype:{getLeftTop:function(){var A=this;return n(A.get(I),A._getContainer());},getProperties:function(){var A=this;var M=A.getPropertyModel();o.each(M,function(N){N.value=A.get(N.attributeName);});return M;},getPropertyModel:function(){var M=this;var A=M.getStrings();return[{attributeName:a,editor:new h.TextAreaCellEditor(),name:A[a]},{attributeName:z,editor:new h.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[z]},{attributeName:j,editor:false,name:A[j]}];},_getContainer:function(){var A=this;return(A.get(L).dropContainer||A.get(I).get(f));},_uiSetXY:function(N){var A=this;var M=A._getContainer().getXY();this._posNode.setXY([N[0]+M[0],N[1]+M[1]]);}}});H.buildNodeId=function(A){return v+_DOLLAR+FIELD+_DOLLAR+A;};h.DiagramNode=H;h.DiagramBuilder.types["node"]=h.DiagramNode;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-impl",function(W){var L=W.Lang,c=L.isArray,w=L.isObject,ar=L.isString,ao=L.isBoolean,ay=W.Array,J=function(A){return(A instanceof W.DiagramBuilderBase);},ap=function(A){return(A instanceof W.DiagramNode);},S=function(A){return(A instanceof W.Anchor);},ab=function(A,aB){var aA=c(aB)?aB:aB.getXY();var aC=c(A)?A:A.getXY();return ay.map(aC,function(aE,aD){return Math.max(0,aE-aA[aD]);});},ad="anchor",X="anchors",R="anchorsDragConfig",F="availableField",k="boundingBox",au="builder",P="cancel",ai="click",U="content",y="controls",ah="controlsToolbar",ag="data",Q="dblclick",I="delete",Y="deleteMessage",al="description",v="diagram",T="diagram-builder",ac="diagramNode",s="diagram-node",am="dragNode",D="editing",a="esc",an="field",m="fields",aa="fieldsDragConfig",l="hover",E="keydown",V="link",n="mouseenter",K="mouseleave",i="name",j="node",af="p1",ae="p2",d="parentNode",O="records",h="recordset",g="region",av="rendered",aq="selected",x="shuffle",t="task",B="tmpConnector",e="type",C="viewport",at="wrapper",q="xy",f=".",z="$",G="",ax="-",p=W.getClassName,Z=p(v,au,ad,l),aj=p(v,au,ad,j),u=p(v,au,ad,j,at),o=p(v,au,y),M=p(v,j),b=p(v,j,U),ak=p(v,j,D),aw=p(v,j,aq);var N=function(){var aA=" ",A="
";W.all(".aui-diagram-node").each(function(aG){var aB=G,aD=W.Widget.getByNode(aG),aC=aD.get("name"),aF=aD.get("boundingBox"),aE=aF.one(".log")||W.Node.create("").appendTo(aF);aB+=aC+A;aD.get(m).each(function(aH){aB+=aA+"a: "+aH.get("id")+A;aH.get("targets").each(function(aI){var aJ=aI.get(ac);aI.get("node").setContent(aI.get("id"));aB+=aA+aA+"t: "+aJ.get("name")+" (s: "+aI.get("id")+")"+A;});aH.get("sources").each(function(aJ){var aI=aJ.get(ac);aJ.get("node").setContent(aJ.get("id"));aB+=aA+aA+"s: "+aI.get("name")+" (t: "+aJ.get("id")+")"+A;});});aE.setContent(aB);});};var r=W.Component.create({NAME:T,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:w},tmpConnector:{setter:"_setTmpConnector",value:{},validator:w}},EXTENDS:W.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=W.getDoc().on(E,W.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ai,W.bind(A._onNodeClick,A),f+M);A.dropContainer.delegate(Q,W.bind(A._onNodeEdit,A),f+M);A.dropContainer.delegate(n,W.bind(A._onMouseenterAnchors,A),f+aj);A.dropContainer.delegate(K,W.bind(A._onMouseleaveAnchors,A),f+aj);},syncUI:function(){var A=this;W.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new W.Connector(A.get(B));},createField:function(aA){var A=this;if(!ap(aA)){aA.builder=A;aA.viewport=A.get(C);aA=new (A.getFieldClass(aA.type||j))(aA);}aA.set(au,A);return aA;},getFieldClass:function(aB){var A=this;var aA=W.DiagramBuilder.types[aB];if(aA){return aA;}else{W.log("The field type: ["+aB+"] couldn't be found.");return null;}},isFieldsDrag:function(aB){var A=this;var aA=A.fieldsDrag;return(aB===aA.dd);},plotField:function(aA){var A=this;if(!aA.get(av)){aA.render(A.dropContainer);}},unselectAll:function(){var A=this;var aA=A.selectedNode;if(aA){aA.set(aq,false);}A.selectedNode=null;},select:function(aA){var A=this;A.unselectAll();A.stopEditingNode();A.selectedNode=aA.set(aq,true).focus();},startEditingNode:function(aA){var A=this;if(aA){A.stopEditingNode();A.tabView.selectTab(W.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(h,aA.getProperties());aA.get(k).addClass(ak);A.editNode=aA;}},stopEditingNode:function(aB){var A=this;var aA=aB||A.editNode;if(aA){A.tabView.selectTab(W.DiagramBuilder.FIELDS_TAB);aA.get(k).removeClass(ak);A.editNode=null;}},_afterKeyEvent:function(aA){var A=this;if(!A.selectedNode||aA.hasModifier()||!aA.isKeyInSet(a,I)){return;}if(aA.isKey(a)){A._onEscKey(aA);}else{if(aA.isKey(I)){A._onDeleteKey(aA);}}aA.halt();},_onCancel:function(aA){var A=this;A.stopEditingNode();},_onDrag:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.get(m).each(function(aD){aD.alignConnectors();});}},_onDragEnd:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.set(q,aC.getLeftTop());}},_onDropHit:function(aB){var A=this;var aA=aB.drag;if(A.isAvailableFieldsDrag(aA)){var aD=aA.get(j).getData(F);var aC=A.addField({xy:ab(aA.lastXY,A.dropContainer),type:aD.get(e),fields:[{}]});A.select(aC);}},_onDeleteKey:function(aA){var A=this;A.selectedNode.close();},_onEscKey:function(aA){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aA){var A=this;aA.currentTarget.addClass(Z);},_onMouseleaveAnchors:function(aA){var A=this;aA.currentTarget.removeClass(Z);},_onNodeClick:function(aA){var A=this;var aB=W.Widget.getByNode(aA.currentTarget);A.select(aB);},_onNodeEdit:function(aA){var A=this;if(!aA.target.ancestor(f+b,true)){return;}var aB=W.Widget.getByNode(aA.currentTarget);if(aB){A.startEditingNode(aB);}},_onSave:function(aB){var A=this;var aA=A.editNode;var aC=A.propertyList.get(h);if(aA){ay.each(aC.get(O),function(aD){var aE=aD.get(ag);aA.set(aE.attributeName,aE.value);});A.stopEditingNode(aA);}},_setTmpConnector:function(aA){var A=this;return W.merge({lazyDraw:true,viewport:A.viewport},aA);},_setFieldsDragConfig:function(aB){var A=this;var aA=A.dropContainer;return W.merge({bubbleTargets:A,container:aA,dragConfig:{plugins:[{cfg:{constrain:aA},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll}]},nodes:f+M},aB||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new W.DD.Delegate(A.get(aa));}}});W.DiagramBuilder=r;W.DiagramBuilder.types={};var H=W.Component.create({NAME:s,EXTENDS:W.Overlay,AUGMENTS:[W.FieldSupport]});var az=W.Component.create({NAME:s,UI_ATTRS:[m,i,aq],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:w},builder:{setter:"_setBuilder",validator:J},description:{value:G,validator:ar},height:{value:90},name:{valueFn:function(){var A=this;
+return A.get(e)+(++W.Env._uidx);},validator:ar},selected:{value:false,validator:ao},strings:{value:{deleteMessage:"Are you sure you want to delete?",description:"Description",name:"Name",type:"Type"}},type:{value:j,validator:ar},controlsToolbar:{setter:"_setControlsToolbar",validator:w,value:null},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:H,buildNodeId:function(A){return s+z+an+z+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(k).addClass(M+ax+A.get(e));A.set("bodyContent",A.get(i));},alignAnchors:function(){var aA=this;var aE=aA.get(m);var aC=aA.get(k).get(g),aD=Math.floor(360/aE.size()),aB=aC.width/2,A=aC.height/2,aG=aC.left+aC.width/2,aF=aC.top+aC.height/2;aE.each(function(aK,aJ){var aI=aK.get(j);var aL=aI.get(g);var aH=aA._getEllipseXY(aB,A,aG,aF,aJ*aD);aI.setXY([aH[0]-aL.width/2,aH[1]-aL.height/2]);aK.alignConnectors();});return aA;},close:function(){var aA=this;var A=aA.getStrings();if(confirm(A[Y])){aA.get(m).each(function(aB){aB.destroy();});aA.destroy();}N();return aA;},createField:function(aB){var A=this;if(!S(aB)){var aA=A.get(au);aB.diagramNode=A;aB.viewport=(aA?aA.get(C):null);aB=new W.Anchor(aB);}return aB;},getLeftTop:function(){var A=this;return ab(A.get(k),A._getContainer());},getProperties:function(){var A=this;var aA=A.getPropertyModel();ay.each(aA,function(aB){aB.value=A.get(aB.attributeName);});return aA;},getPropertyModel:function(){var aA=this;var A=aA.getStrings();return[{attributeName:al,editor:new W.TextAreaCellEditor(),name:A[al]},{attributeName:i,editor:new W.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[i]},{attributeName:e,editor:false,name:A[e]}];},_afterRender:function(aA){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(au).dropContainer||A.get(k).get(d));},_getEllipseXY:function(aA,A,aD,aC,aE){var aB=aE*Math.PI/180;return[aD+aA*Math.cos(aB),aC-A*Math.sin(aB)];},_handleAddAnchorEvent:function(aA){var A=this;A.addField({});},_handleAddTaskEvent:function(aB){var A=this;var aA=A.get(au);var aE=new W.DiagramNode({type:j,xy:[100,100]});aA.addField(aE);var aC=A.addField({});var aD=aE.addField({});aC.connect(aD);},_handleCloseEvent:function(aA){var A=this;A.close();},_onAnchorDrag:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(ae,aB.target.get(am).getCenterXY());},_onAnchorDragEnd:function(aB){var A=this;var aA=A.get(au).tmpConnector.shape;aA.clear();aA.end();},_onAnchorDragStart:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(af,aB.target.get(j).getCenterXY());},_onAnchorDropHit:function(aA){var A=this;var aB=W.Anchor.getAnchorByNode(aA.drag.get(j));var aC=W.Anchor.getAnchorByNode(aA.drop.get(j));aB.connect(aC);N();},_renderControls:function(){var A=this;var aA=A.get(k);A.controlsNode=W.Node.create(A.CONTROLS_TEMPLATE).appendTo(aA);},_renderNodes:function(){var A=this;var aA=A.get(k);A.anchorWrapper=W.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aA);},_renderControlsToolbar:function(aA){var A=this;A.controlsToolbar=new W.Toolbar(A.get(ah)).render(A.controlsNode);},_setBuilder:function(aA){var A=this;A.get(m).each(function(aB){aB.set(C,aA.get(C));});return aA;},_setAnchorsDragConfig:function(aB){var A=this;var aA=A.get(au);return W.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[X],plugins:[{cfg:{constrain:(aA?aA.get(C):null)},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:W.Plugin.DDProxy}]},nodes:f+aj,target:true},aB||{});},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new W.DD.Delegate(A.get(R));},_setControlsToolbar:function(aA){var A=this;return W.merge({activeState:false,children:[{handler:W.bind(A._handleAddAnchorEvent,A),icon:V},{handler:W.bind(A._handleAddTaskEvent,A),icon:x},{handler:W.bind(A._handleCloseEvent,A),icon:P}]},aA);},_uiSetFields:function(aA){var A=this;if(A.get(av)){A.alignAnchors();setTimeout(function(){A.anchorsDrag.syncTargets();},50);}},_uiSetName:function(aB){var A=this;var aA=A.get(k);aA.setAttribute(i,W.DiagramNode.buildNodeId(aB));},_uiSetSelected:function(aA){var A=this;A.get(k).toggleClass(aw,aA);if(aA&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aB){var A=this;var aA=A._getContainer().getXY();this._posNode.setXY([aB[0]+aA[0],aB[1]+aA[1]]);}}});W.DiagramNode=az;W.DiagramBuilder.types[j]=W.DiagramNode;W.DiagramNodeTask=W.Component.create({NAME:s,ATTRS:{type:{value:t}},EXTENDS:W.DiagramNode});W.DiagramBuilder.types[t]=W.DiagramNodeTask;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder-impl.js b/build/aui-diagram-builder/aui-diagram-builder-impl.js
index d7ee02a9cb2..47d1c299466 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-impl.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-impl.js
@@ -3,6 +3,7 @@ var Lang = A.Lang,
isArray = Lang.isArray,
isObject = Lang.isObject,
isString = Lang.isString,
+ isBoolean = Lang.isBoolean,
AArray = A.Array,
@@ -14,6 +15,10 @@ var Lang = A.Lang,
return (val instanceof A.DiagramNode);
},
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
getLeftTop = function(container, node) {
var nodeXY = isArray(node) ? node : node.getXY();
var containerXY = isArray(container) ? container : container.getXY();
@@ -23,44 +28,123 @@ var Lang = A.Lang,
});
},
+ ANCHOR = 'anchor',
+ ANCHORS = 'anchors',
+ ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
+ CANCEL = 'cancel',
+ CLICK = 'click',
+ CONTENT = 'content',
+ CONTROLS = 'controls',
+ CONTROLS_TOOLBAR = 'controlsToolbar',
DATA = 'data',
DBLCLICK = 'dblclick',
+ DELETE = 'delete',
+ DELETE_MESSAGE = 'deleteMessage',
DESCRIPTION = 'description',
DIAGRAM = 'diagram',
- DIAGRAM_BUILDER = 'diagram-builder',
- DIAGRAM_NODE = 'diagram-node',
+ DIAGRAM_BUILDER_NAME = 'diagram-builder',
+ DIAGRAM_NODE = 'diagramNode',
+ DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ ESC = 'esc',
+ FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
+ HOVER = 'hover',
+ KEYDOWN = 'keydown',
+ LINK = 'link',
+ MOUSEENTER = 'mouseenter',
+ MOUSELEAVE = 'mouseleave',
NAME = 'name',
NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
PARENT_NODE = 'parentNode',
RECORDS = 'records',
RECORDSET = 'recordset',
+ REGION = 'region',
RENDERED = 'rendered',
+ SELECTED = 'selected',
+ SHUFFLE = 'shuffle',
+ TASK = 'task',
+ TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
+ VIEWPORT = 'viewport',
+ WRAPPER = 'wrapper',
XY = 'xy',
_DOT = '.',
+ _DOLLAR = '$',
_EMPTY_STR = '',
+ _DASH = '-',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_CONTROLS = AgetClassName(DIAGRAM, BUILDER, CONTROLS),
+ CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE),
+ CSS_DIAGRAM_NODE_CONTENT = AgetClassName(DIAGRAM, NODE, CONTENT),
CSS_DIAGRAM_NODE_EDITING = AgetClassName(DIAGRAM, NODE, EDITING),
- CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE);
+ CSS_DIAGRAM_NODE_SELECTED = AgetClassName(DIAGRAM, NODE, SELECTED);
+
+// REMOVE THIS!
+var __dump = function() {
+ var PAD = ' ', BR = '
';
+
+ A.all('.aui-diagram-node').each(function(n) {
+ var b = _EMPTY_STR,
+ dn = A.Widget.getByNode(n),
+ dnName = dn.get('name'),
+ dnBB = dn.get('boundingBox'),
+ log = dnBB.one('.log') || A.Node.create('').appendTo(dnBB);
+
+ b += dnName + BR;
+
+ dn.get(FIELDS).each(function(a) {
+ b += PAD + 'a: ' + a.get('id') + BR;
+
+ a.get('targets').each(function(t) {
+ var tdn = t.get(DIAGRAM_NODE);
+
+ t.get('node').setContent(t.get('id'));
+
+ b += PAD + PAD + 't: ' + tdn.get('name') + ' (s: ' + t.get('id') + ')' + BR;
+ });
+
+ a.get('sources').each(function(s) {
+ var sdn = s.get(DIAGRAM_NODE);
+
+ s.get('node').setContent(s.get('id'));
+
+ b += PAD + PAD + 's: ' + sdn.get('name') + ' (t: ' + s.get('id') + ')' + BR;
+ });
+ });
+
+ log.setContent(b);
+ });
+};
+// END.
var DiagramBuilder = A.Component.create({
- NAME: DIAGRAM_BUILDER,
+ NAME: DIAGRAM_BUILDER_NAME,
ATTRS: {
fieldsDragConfig: {
value: null,
setter: '_setFieldsDragConfig',
validator: isObject
+ },
+
+ tmpConnector: {
+ setter: '_setTmpConnector',
+ value: {},
+ validator: isObject
}
},
@@ -77,12 +161,18 @@ var DiagramBuilder = A.Component.create({
instance.on({
cancel: instance._onCancel,
+ 'drag:drag': instance._onDrag,
'drag:end': instance._onDragEnd,
'drop:hit': instance._onDropHit,
save: instance._onSave
});
+ instance.handlerKeyDown = A.getDoc().on(KEYDOWN, A.bind(instance._afterKeyEvent, instance));
+
+ instance.dropContainer.delegate(CLICK, A.bind(instance._onNodeClick, instance), _DOT+CSS_DIAGRAM_NODE);
instance.dropContainer.delegate(DBLCLICK, A.bind(instance._onNodeEdit, instance), _DOT+CSS_DIAGRAM_NODE);
+ instance.dropContainer.delegate(MOUSEENTER, A.bind(instance._onMouseenterAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
+ instance.dropContainer.delegate(MOUSELEAVE, A.bind(instance._onMouseleaveAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
},
syncUI: function() {
@@ -91,12 +181,17 @@ var DiagramBuilder = A.Component.create({
A.DiagramBuilder.superclass.syncUI.apply(this, arguments);
instance._setupFieldsDrag();
+
+ instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
createField: function(val) {
var instance = this;
if (!isDiagramNode(val)) {
+ // val.bubbleTargets = instance;
+ val.builder = instance;
+ val.viewport = instance.get(VIEWPORT);
val = new (instance.getFieldClass(val.type || NODE))(val);
}
@@ -126,21 +221,31 @@ var DiagramBuilder = A.Component.create({
return (drag === fieldsDrag.dd);
},
- plotFields: function() {
+ plotField: function(field) {
var instance = this;
- var fields = instance.get(FIELDS);
- fields.each(function(field) {
- instance.plotField(field);
- });
+ if (!field.get(RENDERED)) {
+ field.render(instance.dropContainer);
+ }
},
- plotField: function(field) {
+ unselectAll: function() {
var instance = this;
+ var selectedNode = instance.selectedNode;
- if (!field.get(RENDERED)) {
- field.render(instance.dropContainer);
+ if (selectedNode) {
+ selectedNode.set(SELECTED, false);
}
+
+ instance.selectedNode = null;
+ },
+
+ select: function(diagramNode) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
startEditingNode: function(diagramNode) {
@@ -151,8 +256,6 @@ var DiagramBuilder = A.Component.create({
instance.tabView.selectTab(A.DiagramBuilder.SETTINGS_TAB);
- // instance._renderPropertyList();
-
instance.propertyList.set(RECORDSET, diagramNode.getProperties());
diagramNode.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE_EDITING);
@@ -174,12 +277,42 @@ var DiagramBuilder = A.Component.create({
}
},
+ _afterKeyEvent: function(event) {
+ var instance = this;
+
+ if (!instance.selectedNode || event.hasModifier() || !event.isKeyInSet(ESC, DELETE)) {
+ return;
+ }
+
+ if (event.isKey(ESC)) {
+ instance._onEscKey(event);
+ }
+ else if (event.isKey(DELETE)) {
+ instance._onDeleteKey(event);
+ }
+
+ event.halt();
+ },
+
_onCancel: function(event) {
var instance = this;
instance.stopEditingNode();
},
+ _onDrag: function(event) {
+ var instance = this;
+ var drag = event.target;
+
+ if (instance.isFieldsDrag(drag)) {
+ var diagramNode = A.Widget.getByNode(drag.get(DRAG_NODE));
+
+ diagramNode.get(FIELDS).each(function(anchor) {
+ anchor.alignConnectors();
+ });
+ }
+ },
+
_onDragEnd: function(event) {
var instance = this;
var drag = event.target;
@@ -198,15 +331,56 @@ var DiagramBuilder = A.Component.create({
if (instance.isAvailableFieldsDrag(drag)) {
var availableField = drag.get(NODE).getData(AVAILABLE_FIELD);
- instance.addField({
+ var newField = instance.addField({
xy: getLeftTop(drag.lastXY, instance.dropContainer),
- type: availableField.get(TYPE)
+ type: availableField.get(TYPE),
+ fields: [{}]
});
+
+ instance.select(newField);
}
},
+ _onDeleteKey: function(event) {
+ var instance = this;
+
+ instance.selectedNode.close();
+ },
+
+ _onEscKey: function(event) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ },
+
+ _onMouseenterAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.addClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onMouseleaveAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.removeClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onNodeClick: function(event) {
+ var instance = this;
+ var diagramNode = A.Widget.getByNode(event.currentTarget);
+
+ instance.select(diagramNode);
+ },
+
_onNodeEdit: function(event) {
var instance = this;
+
+ // Only enable editing if the double clicked node is inside the node contentBox.
+ if (!event.target.ancestor(_DOT+CSS_DIAGRAM_NODE_CONTENT, true)) {
+ return;
+ }
+
var diagramNode = A.Widget.getByNode(event.currentTarget);
if (diagramNode) {
@@ -230,6 +404,18 @@ var DiagramBuilder = A.Component.create({
}
},
+ _setTmpConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ lazyDraw: true,
+ viewport: instance.viewport
+ },
+ val
+ );
+ },
+
_setFieldsDragConfig: function(val) {
var instance = this;
var dropContainer = instance.dropContainer;
@@ -274,11 +460,32 @@ A.DiagramBuilder = DiagramBuilder;
A.DiagramBuilder.types = {};
+var DiagramNodeOverlay = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ EXTENDS: A.Overlay,
+
+ // A.FieldSupport augment the class with "fields" attribute and util methods
+ // such as: addField, removeField. Although the attribute is called "fields" due to
+ // the augmentation, those fields are the anchors. TODO: Allow A.FieldSupport to
+ // customize the name of the attribute and method sufixes.
+ AUGMENTS: [A.FieldSupport]
+});
+
var DiagramNode = A.Component.create({
- NAME: DIAGRAM_NODE,
+ NAME: DIAGRAM_NODE_NAME,
+
+ UI_ATTRS: [FIELDS, NAME, SELECTED],
ATTRS: {
+ anchorsDragConfig: {
+ value: null,
+ setter: '_setAnchorsDragConfig',
+ validator: isObject
+ },
+
builder: {
+ setter: '_setBuilder',
validator: isDiagramBuilder
},
@@ -288,7 +495,7 @@ var DiagramNode = A.Component.create({
},
height: {
- value: 100
+ value: 90
},
name: {
@@ -300,8 +507,14 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ selected: {
+ value: false,
+ validator: isBoolean
+ },
+
strings: {
value: {
+ deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
name: 'Name',
type: 'Type'
@@ -313,14 +526,114 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ controlsToolbar: {
+ setter: '_setControlsToolbar',
+ validator: isObject,
+ value: null
+ },
+
width: {
- value: 200
+ value: 90
+ },
+
+ zIndex: {
+ value: 100
+ },
+
+ tabIndex: {
+ value: 1
}
},
- EXTENDS: A.Overlay,
+ EXTENDS: DiagramNodeOverlay,
+
+ buildNodeId: function(id) {
+ return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ },
prototype: {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ CONTROLS_TEMPLATE: '',
+
+ initializer: function() {
+ var instance = this;
+
+ instance._renderNodes();
+ instance._setupAnchorsDrag();
+
+ instance.after({
+ render: instance._afterRender
+ });
+
+ instance.on({
+ 'drag:drag': instance._onAnchorDrag,
+ 'drag:end': instance._onAnchorDragEnd,
+ 'drag:start': instance._onAnchorDragStart,
+ 'drop:hit': instance._onAnchorDropHit
+ });
+
+ instance.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE+_DASH+instance.get(TYPE));
+
+ // REMOVE THIS!
+ instance.set('bodyContent', instance.get(NAME));
+ },
+
+ alignAnchors: function() {
+ var instance = this;
+ var anchors = instance.get(FIELDS);
+
+ var cRegion = instance.get(BOUNDING_BOX).get(REGION),
+ dAngle = Math.floor(360/anchors.size()),
+ a = cRegion.width/2,
+ b = cRegion.height/2,
+ centerX = cRegion.left + cRegion.width/2,
+ centerY = cRegion.top + cRegion.height/2;
+
+ anchors.each(function(anchor, index) {
+ var anchorNode = anchor.get(NODE);
+ var aRegion = anchorNode.get(REGION);
+ var exy = instance._getEllipseXY(a, b, centerX, centerY, index*dAngle);
+
+ anchorNode.setXY([ exy[0] - aRegion.width/2, exy[1] - aRegion.height/2 ]);
+
+ anchor.alignConnectors();
+ });
+
+ return instance;
+ },
+
+ close: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ if (confirm(strings[DELETE_MESSAGE])) {
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.destroy();
+ });
+
+ instance.destroy();
+ }
+
+ __dump();
+
+ return instance;
+ },
+
+ createField: function(val) {
+ var instance = this;
+
+ if (!isAnchor(val)) {
+ var builder = instance.get(BUILDER);
+
+ val.diagramNode = instance;
+ val.viewport = (builder ? builder.get(VIEWPORT) : null);
+
+ val = new A.Anchor(val);
+ }
+
+ return val;
+ },
+
getLeftTop: function() {
var instance = this;
@@ -369,12 +682,225 @@ var DiagramNode = A.Component.create({
];
},
+ _afterRender: function(event) {
+ var instance = this;
+
+ instance.alignAnchors();
+
+ instance._renderControls();
+ },
+
_getContainer: function() {
var instance = this;
return (instance.get(BUILDER).dropContainer || instance.get(BOUNDING_BOX).get(PARENT_NODE));
},
+ _getEllipseXY: function(a, b, centerX, centerY, angle) {
+ var t = angle*Math.PI/180;
+
+ return [ centerX + a*Math.cos(t), centerY - b*Math.sin(t) ];
+ },
+
+ _handleAddAnchorEvent: function(event) {
+ var instance = this;
+
+ instance.addField({});
+
+ // event.halt();
+ },
+
+ _handleAddTaskEvent: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ var diagramNode = new A.DiagramNode({
+ type: NODE,
+ xy: [100, 100] // TODO - find best position?
+ });
+
+ builder.addField(diagramNode);
+
+ var source = instance.addField({});
+ var target = diagramNode.addField({});
+ source.connect(target);
+ },
+
+ _handleCloseEvent: function(event) {
+ var instance = this;
+
+ instance.close();
+ },
+
+ _onAnchorDrag: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P2, event.target.get(DRAG_NODE).getCenterXY());
+ },
+
+ _onAnchorDragEnd: function(event) {
+ var instance = this;
+ var shape = instance.get(BUILDER).tmpConnector.shape;
+
+ shape.clear();
+ shape.end();
+ },
+
+ _onAnchorDragStart: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P1, event.target.get(NODE).getCenterXY());
+ },
+
+ _onAnchorDropHit: function(event) {
+ var instance = this;
+ var source = A.Anchor.getAnchorByNode(event.drag.get(NODE));
+ var target = A.Anchor.getAnchorByNode(event.drop.get(NODE));
+
+ source.connect(target);
+
+ __dump();
+ },
+
+ _renderControls: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.controlsNode = A.Node.create(instance.CONTROLS_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderNodes: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.anchorWrapper = A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderControlsToolbar: function(event) {
+ var instance = this;
+
+ instance.controlsToolbar = new A.Toolbar(
+ instance.get(CONTROLS_TOOLBAR)
+ )
+ .render(instance.controlsNode);
+ },
+
+ _setBuilder: function(val) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.set(VIEWPORT, val.get(VIEWPORT));
+ });
+
+ return val;
+ },
+
+ _setAnchorsDragConfig: function(val) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ return A.merge(
+ {
+ bubbleTargets: instance,
+ container: instance.anchorWrapper,
+ dragConfig: {
+ groups: [ANCHORS],
+ plugins: [
+ {
+ cfg: {
+ constrain: (builder ? builder.get(VIEWPORT) : null)
+ },
+ fn: A.Plugin.DDConstrained
+ },
+ {
+ cfg: {
+ scrollDelay: 150
+ },
+ fn: A.Plugin.DDWinScroll
+ },
+ {
+ cfg: {
+ moveOnEnd: false
+ },
+ fn: A.Plugin.DDProxy
+ }
+ ]
+ },
+ nodes: _DOT+CSS_DB_ANCHOR_NODE,
+ target: true
+ },
+ val || {}
+ );
+ },
+
+ _setupAnchorsDrag: function() {
+ var instance = this;
+
+ instance.anchorsDrag = new A.DD.Delegate(
+ instance.get(ANCHORS_DRAG_CONFIG)
+ );
+ },
+
+ _setControlsToolbar: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK
+ },
+ {
+ handler: A.bind(instance._handleAddTaskEvent, instance),
+ icon: SHUFFLE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL
+ }
+ ]
+ },
+ val
+ );
+ },
+
+ _uiSetFields: function(val) {
+ var instance = this;
+
+ if (instance.get(RENDERED)) {
+ instance.alignAnchors();
+
+ setTimeout(function() {
+ instance.anchorsDrag.syncTargets();
+ }, 50);
+ }
+ },
+
+ _uiSetName: function(val) {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetSelected: function(val) {
+ var instance = this;
+
+ instance.get(BOUNDING_BOX).toggleClass(CSS_DIAGRAM_NODE_SELECTED, val);
+
+ if (val && !instance.controlsToolbar) {
+ instance._renderControlsToolbar();
+ }
+
+ // if (instance.get(RENDERED)) {
+ // instance.alignAnchors();
+ // }
+ },
+
_uiSetXY : function(val) {
var instance = this;
var containerXY = instance._getContainer().getXY();
@@ -384,12 +910,32 @@ var DiagramNode = A.Component.create({
}
});
-DiagramNode.buildNodeId = function(id) {
- return DIAGRAM_NODE + _DOLLAR + FIELD + _DOLLAR + id;
-};
-
A.DiagramNode = DiagramNode;
-A.DiagramBuilder.types['node'] = A.DiagramNode;
+A.DiagramBuilder.types[NODE] = A.DiagramNode;
+
+A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNode
+});
+
+A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+// TODO deletar anchors OK
+// TODO deletar connections (delete) OK
+// TODO Adicionar overlay de controles OK
+// TODO syncTargets dd delegate
+
+
+// TODO gerar XML
+// TODO reposicionar setas?
+// TODO Adicionar groups/validation for connection
}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-min.js b/build/aui-diagram-builder/aui-diagram-builder-min.js
index 2b0885e231d..b99565609b2 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-min.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-min.js
@@ -1,3 +1,5 @@
-AUI.add("aui-diagram-builder-base",function(ab){var R=ab.Lang,d=R.isArray,ao=R.isBoolean,K=R.isNumber,z=R.isObject,ar=R.isString,H=function(A){return(A instanceof ab.ArrayList);},P=function(A){return(A instanceof ab.Node);},C=function(A){return(A instanceof ab.AvailableField);},aC=ab.Array,T="add",k="addNode",aB="auto",L="availableField",O="availableFields",ay="availableFieldsDragConfig",al="base",s="boundingBox",av="builder",X="cancel",ap="clearfix",a="container",Z="content",u="contentBox",e="contentContainer",N="contentNode",D="createDocumentFragment",y="diagram",E="diagram-builder-base",Y="disk",o="draggable",ax="drop",ai="dropConfig",V="dropContainer",an="field",t="fields",n="fieldsContainer",am="height",p="helper",U="icon",v="iconClass",ah="id",ad="label",af="list",r="node",x="nodeSettings",aa="propertyList",aw="rendered",aj="save",q="settings",M="tab",b="tabs",f="tabview",F="tabView",J="toolbar",j="toolbarContainer",w=ab.getClassName,aA=" ",h=".",G="$",aD=w(y,av,al,ax,a),ak=w(y,av,al,Z,a),B=w(y,av,al,an),g=w(y,av,al,t,a),ae=w(y,av,al,an,o),c=w(y,av,al,an,U),S=w(y,av,al,an,ad),m=w(y,av,al,b,a),W=w(y,av,al,b,a,Z),ag=w(y,av,al,M,T),I=w(y,av,al,M,q),at=w(y,av,al,J,a),ac=w(p,ap),l=w(U),au=w(f,Z),az=w(f,af);var i=ab.Component.create({NAME:L,ATTRS:{draggable:{value:true,validator:ao},label:{validator:ar},iconClass:{validator:ar},id:{value:ab.guid(),setter:"_setId",validator:ar},node:{valueFn:function(aE){var A=this;if(!P(aE)){aE=ab.Node.create(ab.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aE.setData(L,A);}return aE;},validator:P,writeOnce:true},type:{value:r,validator:ar}},EXTENDS:ab.Base,prototype:{FIELD_ITEM_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.contentContainer=A.get(e);A.dropContainer=A.get(V);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aF){var A=this;var aE=A.availableFieldsDrag;return(aF===aE.dd);},plotFields:function(A){},renderUI:function(){var A=this;A._renderTabs();A._renderContentContainer();A._uiSetAvailableFields(A.get(O));},syncUI:function(){var A=this;var aE=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aE.addClass(ac);},_afterActiveTabChange:function(aF){var A=this;var aE=aF.newVal.get(N);if(A.get(aw)&&(aE===A.settingsNode)){A._renderSettings();}},_afterRender:function(aE){var A=this;A.plotFields();},_afterUiSetHeight:function(aE){var A=this;A.dropContainer.setStyle(am,K(aE)?aE+A.DEF_UNIT:aE);},_defCancelFn:function(aE){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(X);},_handleSaveEvent:function(){var A=this;A.fire(aj);},_renderContentContainer:function(){var A=this;var aE=A.get(u);var aF=A.contentContainer;aF.appendChild(A.dropContainer);aE.appendChild(aF);},_renderPropertyList:function(){var A=this;if(!A.propertyList){A.propertyList=new ab.PropertyList(A.get(aa)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();
-},_renderTabs:function(){var A=this;if(!A.tabView){var aE=new ab.TabView(A.get(F));A.tabView=aE;A.fieldsNode=aE.getTab(0).get(N);A.settingsNode=aE.getTab(1).get(N);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ab.Toolbar(A.get(J)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ab.DD.Drop(A.get(ai));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ab.DD.Delegate(A.get(ay));},_setAvailableFields:function(aF){var aE=this;var A=[];aC.each(aF,function(aH,aG){A.push(C(aH)?aH:new ab.AvailableField(aH));});return A;},_setDropConfig:function(aE){var A=this;return ab.merge({bubbleTargets:A,node:A.dropContainer},aE||{});},_setAvailableFieldsDragConfig:function(aE){var A=this;return ab.merge({bubbleTargets:A,container:A.get(s),dragConfig:{plugins:[{cfg:{moveOnEnd:false},fn:ab.Plugin.DDProxy}]},nodes:h+ae},aE||{});},_setPropertyList:function(aE){var A=this;return ab.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aB}},aE);},_setTabView:function(aH){var aE=this;var aG=aE.get(s);var aI=aG.one(h+az);var aF={after:{activeTabChange:ab.bind(aE._afterActiveTabChange,aE)},boundingBox:aG.one(h+m),contentBox:aG.one(h+W),bubbleTargets:aE,contentNode:aG.one(h+au),cssClass:m,listNode:aI,render:aE.get(u)};if(!aI){var A=aE.getStrings();aF.items=[{cssClass:ag,label:A[k]},{cssClass:I,label:A[x]}];}return ab.merge(aF,aH);},_setToolbar:function(aF){var aE=this;var A=aE.getStrings();return ab.merge({activeState:false,bubbleTargets:aE,children:[{handler:ab.bind(aE._handleSaveEvent,aE),label:A[aj],icon:Y},{handler:ab.bind(aE._handleCancelEvent,aE),label:A[X]}]},aF);},_uiSetAvailableFields:function(aG){var A=this;var aF=A.fieldsNode;if(aF){var aE=ab.getDoc().invoke(D);aC.each(aG,function(aH){aE.appendChild(aH.get(r));});aF.setContent(A.fieldsContainer.setContent(aE));}},_uiSetFields:function(aE){var A=this;if(A.get(aw)){A.plotFields();}}}});ab.DiagramBuilderBase=aq;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});AUI.add("aui-diagram-builder-impl",function(h){var K=h.Lang,u=K.isArray,E=K.isObject,e=K.isString,o=h.Array,y=function(A){return(A instanceof h.DiagramBuilderBase);},p=function(A){return(A instanceof h.DiagramNode);},n=function(A,N){var M=u(N)?N:N.getXY();var O=u(A)?A:A.getXY();return o.map(O,function(Q,P){return Math.max(0,Q-M[P]);});},B="availableField",I="boundingBox",L="builder",g="data",t="dblclick",a="description",C="diagram",d="diagram-builder",v="diagram-node",c="dragNode",b="editing",x="fields",s="fieldsDragConfig",z="name",J="node",f="parentNode",i="records",D="recordset",G="rendered",j="type",k="xy",r=".",q="",w=h.getClassName,l=w(C,J,b),F=w(C,J);var m=h.Component.create({NAME:d,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:E}},EXTENDS:h.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.dropContainer.delegate(t,h.bind(A._onNodeEdit,A),r+F);},syncUI:function(){var A=this;h.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();},createField:function(M){var A=this;if(!p(M)){M=new (A.getFieldClass(M.type||J))(M);}M.set(L,A);return M;},getFieldClass:function(N){var A=this;var M=h.DiagramBuilder.types[N];if(M){return M;}else{h.log("The field type: ["+N+"] couldn't be found.");return null;}},isFieldsDrag:function(N){var A=this;var M=A.fieldsDrag;return(N===M.dd);},plotFields:function(){var M=this;var A=M.get(x);A.each(function(N){M.plotField(N);});},plotField:function(M){var A=this;if(!M.get(G)){M.render(A.dropContainer);}},startEditingNode:function(M){var A=this;if(M){A.stopEditingNode();A.tabView.selectTab(h.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(D,M.getProperties());M.get(I).addClass(l);A.editNode=M;}},stopEditingNode:function(N){var A=this;var M=N||A.editNode;if(M){A.tabView.selectTab(h.DiagramBuilder.FIELDS_TAB);M.get(I).removeClass(l);A.editNode=null;}},_onCancel:function(M){var A=this;A.stopEditingNode();},_onDragEnd:function(N){var A=this;var M=N.target;if(A.isFieldsDrag(M)){var O=h.Widget.getByNode(M.get(c));O.set(k,O.getLeftTop());}},_onDropHit:function(N){var A=this;var M=N.drag;if(A.isAvailableFieldsDrag(M)){var O=M.get(J).getData(B);A.addField({xy:n(M.lastXY,A.dropContainer),type:O.get(j)});}},_onNodeEdit:function(M){var A=this;var N=h.Widget.getByNode(M.currentTarget);if(N){A.startEditingNode(N);}},_onSave:function(N){var A=this;var M=A.editNode;var O=A.propertyList.get(D);if(M){o.each(O.get(i),function(P){var Q=P.get(g);M.set(Q.attributeName,Q.value);});A.stopEditingNode(M);}},_setFieldsDragConfig:function(N){var A=this;var M=A.dropContainer;return h.merge({bubbleTargets:A,container:M,dragConfig:{plugins:[{cfg:{constrain:M},fn:h.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:h.Plugin.DDWinScroll}]},nodes:r+F},N||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new h.DD.Delegate(A.get(s));}}});h.DiagramBuilder=m;h.DiagramBuilder.types={};var H=h.Component.create({NAME:v,ATTRS:{builder:{validator:y},description:{value:q,validator:e},height:{value:100},name:{valueFn:function(){var A=this;return A.get(j)+(++h.Env._uidx);},validator:e},strings:{value:{description:"Description",name:"Name",type:"Type"}},type:{value:J,validator:e},width:{value:200}},EXTENDS:h.Overlay,prototype:{getLeftTop:function(){var A=this;return n(A.get(I),A._getContainer());},getProperties:function(){var A=this;var M=A.getPropertyModel();o.each(M,function(N){N.value=A.get(N.attributeName);});return M;},getPropertyModel:function(){var M=this;var A=M.getStrings();return[{attributeName:a,editor:new h.TextAreaCellEditor(),name:A[a]},{attributeName:z,editor:new h.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[z]},{attributeName:j,editor:false,name:A[j]}];},_getContainer:function(){var A=this;return(A.get(L).dropContainer||A.get(I).get(f));},_uiSetXY:function(N){var A=this;var M=A._getContainer().getXY();
-this._posNode.setXY([N[0]+M[0],N[1]+M[1]]);}}});H.buildNodeId=function(A){return v+_DOLLAR+FIELD+_DOLLAR+A;};h.DiagramNode=H;h.DiagramBuilder.types["node"]=h.DiagramNode;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});AUI.add("aui-diagram-builder",function(a){},"@VERSION@",{use:["aui-diagram-builder-base","aui-diagram-builder-impl"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-base",function(ad){var T=ad.Lang,d=T.isArray,ap=T.isBoolean,M=T.isNumber,B=T.isObject,at=T.isString,I=function(A){return(A instanceof ad.ArrayList);},R=function(A){return(A instanceof ad.Node);},D=function(A){return(A instanceof ad.AvailableField);},aD=ad.Array,V="add",k="addNode",aC="auto",N="availableField",Q="availableFields",az="availableFieldsDragConfig",am="base",s="boundingBox",aw="builder",Z="cancel",aq="clearfix",a="container",ab="content",u="contentBox",J="viewport",P="contentNode",E="createDocumentFragment",z="diagram",F="diagram-builder-base",aa="disk",o="draggable",ay="drop",ak="dropConfig",X="dropContainer",ao="field",t="fields",n="fieldsContainer",an="height",p="helper",W="icon",v="iconClass",aj="id",af="label",ai="list",r="node",y="nodeSettings",ac="propertyList",ax="rendered",al="save",q="settings",O="tab",b="tabs",e="tabview",G="tabView",L="toolbar",j="toolbarContainer",w=ad.getClassName,aB=" ",g=".",H="$",h="#",aE=w(z,aw,am,ay,a),x=w(z,aw,am,J),C=w(z,aw,am,ao),f=w(z,aw,am,t,a),ag=w(z,aw,am,ao,o),c=w(z,aw,am,ao,W),U=w(z,aw,am,ao,af),m=w(z,aw,am,b,a),Y=w(z,aw,am,b,a,ab),ah=w(z,aw,am,O,V),K=w(z,aw,am,O,q),au=w(z,aw,am,L,a),ae=w(p,aq),l=w(W),av=w(e,ab),aA=w(e,ai);var i=ad.Component.create({NAME:N,ATTRS:{draggable:{value:true,validator:ap},label:{validator:at},iconClass:{validator:at},id:{value:ad.guid(),setter:"_setId",validator:at},node:{valueFn:function(aF){var A=this;if(!R(aF)){aF=ad.Node.create(ad.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aF.setData(N,A);}return aF;},validator:R,writeOnce:true},type:{value:r,validator:at}},EXTENDS:ad.Base,buildNodeId:function(A){return Q+H+ao+H+A;},getAvailableFieldByNode:function(A){return ad.one(A).getData(N);},getAvailableFieldById:function(A){return ad.AvailableField.getAvailableFieldByNode(h+ad.AvailableField.buildNodeId(A));},prototype:{FIELD_ITEM_TEMPLATE:'
',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(X);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aG){var A=this;var aF=A.availableFieldsDrag;return(aG===aF.dd);},plotFields:function(){var aF=this;var A=aF.get(t);A.each(function(aG){aF.plotField(aG);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(Q));},syncUI:function(){var A=this;var aF=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aF.addClass(ae);},_afterActiveTabChange:function(aG){var A=this;var aF=aG.newVal.get(P);if(A.get(ax)&&(aF===A.settingsNode)){A._renderSettings();}},_afterRender:function(aF){var A=this;A.plotFields();},_afterUiSetHeight:function(aF){var A=this;A.dropContainer.setStyle(an,M(aF)?aF+A.DEF_UNIT:aF);},_defCancelFn:function(aF){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(Z);},_handleSaveEvent:function(){var A=this;A.fire(al);},_renderViewport:function(){var aF=this;var aG=aF.get(u);var A=aF.viewport;A.appendChild(aF.dropContainer);aG.appendChild(A);},_renderPropertyList:function(){var A=this;
+if(!A.propertyList){A.propertyList=new ad.PropertyList(A.get(ac)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aF=new ad.TabView(A.get(G));A.tabView=aF;A.fieldsNode=aF.getTab(0).get(P);A.settingsNode=aF.getTab(1).get(P);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ad.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ad.DD.Drop(A.get(ak));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ad.DD.Delegate(A.get(az));},_setAvailableFields:function(aG){var aF=this;var A=[];aD.each(aG,function(aI,aH){A.push(D(aI)?aI:new ad.AvailableField(aI));});return A;},_setDropConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,groups:[Q],node:A.dropContainer},aF||{});},_setAvailableFieldsDragConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[Q],plugins:[{cfg:{moveOnEnd:false},fn:ad.Plugin.DDProxy}]},nodes:g+ag},aF||{});},_setPropertyList:function(aF){var A=this;return ad.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aC}},aF);},_setTabView:function(aI){var aF=this;var aH=aF.get(s);var aJ=aH.one(g+aA);var aG={after:{activeTabChange:ad.bind(aF._afterActiveTabChange,aF)},boundingBox:aH.one(g+m),contentBox:aH.one(g+Y),bubbleTargets:aF,contentNode:aH.one(g+av),cssClass:m,listNode:aJ,render:aF.get(u)};if(!aJ){var A=aF.getStrings();aG.items=[{cssClass:ah,label:A[k]},{cssClass:K,label:A[y]}];}return ad.merge(aG,aI);},_setToolbar:function(aG){var aF=this;var A=aF.getStrings();return ad.merge({activeState:false,bubbleTargets:aF,children:[{handler:ad.bind(aF._handleSaveEvent,aF),label:A[al],icon:aa},{handler:ad.bind(aF._handleCancelEvent,aF),label:A[Z]}]},aG);},_uiSetAvailableFields:function(aH){var A=this;var aG=A.fieldsNode;if(aG){var aF=ad.getDoc().invoke(E);aD.each(aH,function(aI){aF.appendChild(aI.get(r));});aG.setContent(A.fieldsContainer.setContent(aF));}},_uiSetFields:function(aF){var A=this;if(A.get(ax)){A.plotFields();}}}});ad.DiagramBuilderBase=ar;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});AUI.add("aui-diagram-builder-impl",function(W){var L=W.Lang,c=L.isArray,w=L.isObject,ar=L.isString,ao=L.isBoolean,ay=W.Array,J=function(A){return(A instanceof W.DiagramBuilderBase);},ap=function(A){return(A instanceof W.DiagramNode);},S=function(A){return(A instanceof W.Anchor);},ab=function(A,aB){var aA=c(aB)?aB:aB.getXY();var aC=c(A)?A:A.getXY();return ay.map(aC,function(aE,aD){return Math.max(0,aE-aA[aD]);});},ad="anchor",X="anchors",R="anchorsDragConfig",F="availableField",k="boundingBox",au="builder",P="cancel",ai="click",U="content",y="controls",ah="controlsToolbar",ag="data",Q="dblclick",I="delete",Y="deleteMessage",al="description",v="diagram",T="diagram-builder",ac="diagramNode",s="diagram-node",am="dragNode",D="editing",a="esc",an="field",m="fields",aa="fieldsDragConfig",l="hover",E="keydown",V="link",n="mouseenter",K="mouseleave",i="name",j="node",af="p1",ae="p2",d="parentNode",O="records",h="recordset",g="region",av="rendered",aq="selected",x="shuffle",t="task",B="tmpConnector",e="type",C="viewport",at="wrapper",q="xy",f=".",z="$",G="",ax="-",p=W.getClassName,Z=p(v,au,ad,l),aj=p(v,au,ad,j),u=p(v,au,ad,j,at),o=p(v,au,y),M=p(v,j),b=p(v,j,U),ak=p(v,j,D),aw=p(v,j,aq);var N=function(){var aA=" ",A="
";W.all(".aui-diagram-node").each(function(aG){var aB=G,aD=W.Widget.getByNode(aG),aC=aD.get("name"),aF=aD.get("boundingBox"),aE=aF.one(".log")||W.Node.create("").appendTo(aF);aB+=aC+A;aD.get(m).each(function(aH){aB+=aA+"a: "+aH.get("id")+A;aH.get("targets").each(function(aI){var aJ=aI.get(ac);aI.get("node").setContent(aI.get("id"));aB+=aA+aA+"t: "+aJ.get("name")+" (s: "+aI.get("id")+")"+A;});aH.get("sources").each(function(aJ){var aI=aJ.get(ac);aJ.get("node").setContent(aJ.get("id"));aB+=aA+aA+"s: "+aI.get("name")+" (t: "+aJ.get("id")+")"+A;});});aE.setContent(aB);});};var r=W.Component.create({NAME:T,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:w},tmpConnector:{setter:"_setTmpConnector",value:{},validator:w}},EXTENDS:W.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=W.getDoc().on(E,W.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ai,W.bind(A._onNodeClick,A),f+M);A.dropContainer.delegate(Q,W.bind(A._onNodeEdit,A),f+M);A.dropContainer.delegate(n,W.bind(A._onMouseenterAnchors,A),f+aj);A.dropContainer.delegate(K,W.bind(A._onMouseleaveAnchors,A),f+aj);},syncUI:function(){var A=this;W.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new W.Connector(A.get(B));},createField:function(aA){var A=this;if(!ap(aA)){aA.builder=A;aA.viewport=A.get(C);aA=new (A.getFieldClass(aA.type||j))(aA);}aA.set(au,A);return aA;},getFieldClass:function(aB){var A=this;var aA=W.DiagramBuilder.types[aB];if(aA){return aA;}else{W.log("The field type: ["+aB+"] couldn't be found.");return null;}},isFieldsDrag:function(aB){var A=this;var aA=A.fieldsDrag;return(aB===aA.dd);},plotField:function(aA){var A=this;if(!aA.get(av)){aA.render(A.dropContainer);}},unselectAll:function(){var A=this;var aA=A.selectedNode;if(aA){aA.set(aq,false);}A.selectedNode=null;},select:function(aA){var A=this;A.unselectAll();A.stopEditingNode();A.selectedNode=aA.set(aq,true).focus();},startEditingNode:function(aA){var A=this;if(aA){A.stopEditingNode();A.tabView.selectTab(W.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(h,aA.getProperties());aA.get(k).addClass(ak);A.editNode=aA;}},stopEditingNode:function(aB){var A=this;var aA=aB||A.editNode;if(aA){A.tabView.selectTab(W.DiagramBuilder.FIELDS_TAB);
+aA.get(k).removeClass(ak);A.editNode=null;}},_afterKeyEvent:function(aA){var A=this;if(!A.selectedNode||aA.hasModifier()||!aA.isKeyInSet(a,I)){return;}if(aA.isKey(a)){A._onEscKey(aA);}else{if(aA.isKey(I)){A._onDeleteKey(aA);}}aA.halt();},_onCancel:function(aA){var A=this;A.stopEditingNode();},_onDrag:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.get(m).each(function(aD){aD.alignConnectors();});}},_onDragEnd:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.set(q,aC.getLeftTop());}},_onDropHit:function(aB){var A=this;var aA=aB.drag;if(A.isAvailableFieldsDrag(aA)){var aD=aA.get(j).getData(F);var aC=A.addField({xy:ab(aA.lastXY,A.dropContainer),type:aD.get(e),fields:[{}]});A.select(aC);}},_onDeleteKey:function(aA){var A=this;A.selectedNode.close();},_onEscKey:function(aA){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aA){var A=this;aA.currentTarget.addClass(Z);},_onMouseleaveAnchors:function(aA){var A=this;aA.currentTarget.removeClass(Z);},_onNodeClick:function(aA){var A=this;var aB=W.Widget.getByNode(aA.currentTarget);A.select(aB);},_onNodeEdit:function(aA){var A=this;if(!aA.target.ancestor(f+b,true)){return;}var aB=W.Widget.getByNode(aA.currentTarget);if(aB){A.startEditingNode(aB);}},_onSave:function(aB){var A=this;var aA=A.editNode;var aC=A.propertyList.get(h);if(aA){ay.each(aC.get(O),function(aD){var aE=aD.get(ag);aA.set(aE.attributeName,aE.value);});A.stopEditingNode(aA);}},_setTmpConnector:function(aA){var A=this;return W.merge({lazyDraw:true,viewport:A.viewport},aA);},_setFieldsDragConfig:function(aB){var A=this;var aA=A.dropContainer;return W.merge({bubbleTargets:A,container:aA,dragConfig:{plugins:[{cfg:{constrain:aA},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll}]},nodes:f+M},aB||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new W.DD.Delegate(A.get(aa));}}});W.DiagramBuilder=r;W.DiagramBuilder.types={};var H=W.Component.create({NAME:s,EXTENDS:W.Overlay,AUGMENTS:[W.FieldSupport]});var az=W.Component.create({NAME:s,UI_ATTRS:[m,i,aq],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:w},builder:{setter:"_setBuilder",validator:J},description:{value:G,validator:ar},height:{value:90},name:{valueFn:function(){var A=this;return A.get(e)+(++W.Env._uidx);},validator:ar},selected:{value:false,validator:ao},strings:{value:{deleteMessage:"Are you sure you want to delete?",description:"Description",name:"Name",type:"Type"}},type:{value:j,validator:ar},controlsToolbar:{setter:"_setControlsToolbar",validator:w,value:null},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:H,buildNodeId:function(A){return s+z+an+z+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(k).addClass(M+ax+A.get(e));A.set("bodyContent",A.get(i));},alignAnchors:function(){var aA=this;var aE=aA.get(m);var aC=aA.get(k).get(g),aD=Math.floor(360/aE.size()),aB=aC.width/2,A=aC.height/2,aG=aC.left+aC.width/2,aF=aC.top+aC.height/2;aE.each(function(aK,aJ){var aI=aK.get(j);var aL=aI.get(g);var aH=aA._getEllipseXY(aB,A,aG,aF,aJ*aD);aI.setXY([aH[0]-aL.width/2,aH[1]-aL.height/2]);aK.alignConnectors();});return aA;},close:function(){var aA=this;var A=aA.getStrings();if(confirm(A[Y])){aA.get(m).each(function(aB){aB.destroy();});aA.destroy();}N();return aA;},createField:function(aB){var A=this;if(!S(aB)){var aA=A.get(au);aB.diagramNode=A;aB.viewport=(aA?aA.get(C):null);aB=new W.Anchor(aB);}return aB;},getLeftTop:function(){var A=this;return ab(A.get(k),A._getContainer());},getProperties:function(){var A=this;var aA=A.getPropertyModel();ay.each(aA,function(aB){aB.value=A.get(aB.attributeName);});return aA;},getPropertyModel:function(){var aA=this;var A=aA.getStrings();return[{attributeName:al,editor:new W.TextAreaCellEditor(),name:A[al]},{attributeName:i,editor:new W.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[i]},{attributeName:e,editor:false,name:A[e]}];},_afterRender:function(aA){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(au).dropContainer||A.get(k).get(d));},_getEllipseXY:function(aA,A,aD,aC,aE){var aB=aE*Math.PI/180;return[aD+aA*Math.cos(aB),aC-A*Math.sin(aB)];},_handleAddAnchorEvent:function(aA){var A=this;A.addField({});},_handleAddTaskEvent:function(aB){var A=this;var aA=A.get(au);var aE=new W.DiagramNode({type:j,xy:[100,100]});aA.addField(aE);var aC=A.addField({});var aD=aE.addField({});aC.connect(aD);},_handleCloseEvent:function(aA){var A=this;A.close();},_onAnchorDrag:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(ae,aB.target.get(am).getCenterXY());},_onAnchorDragEnd:function(aB){var A=this;var aA=A.get(au).tmpConnector.shape;aA.clear();aA.end();},_onAnchorDragStart:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(af,aB.target.get(j).getCenterXY());},_onAnchorDropHit:function(aA){var A=this;var aB=W.Anchor.getAnchorByNode(aA.drag.get(j));var aC=W.Anchor.getAnchorByNode(aA.drop.get(j));aB.connect(aC);N();},_renderControls:function(){var A=this;var aA=A.get(k);A.controlsNode=W.Node.create(A.CONTROLS_TEMPLATE).appendTo(aA);},_renderNodes:function(){var A=this;var aA=A.get(k);A.anchorWrapper=W.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aA);},_renderControlsToolbar:function(aA){var A=this;A.controlsToolbar=new W.Toolbar(A.get(ah)).render(A.controlsNode);},_setBuilder:function(aA){var A=this;A.get(m).each(function(aB){aB.set(C,aA.get(C));});return aA;},_setAnchorsDragConfig:function(aB){var A=this;var aA=A.get(au);return W.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[X],plugins:[{cfg:{constrain:(aA?aA.get(C):null)},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:W.Plugin.DDProxy}]},nodes:f+aj,target:true},aB||{});
+},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new W.DD.Delegate(A.get(R));},_setControlsToolbar:function(aA){var A=this;return W.merge({activeState:false,children:[{handler:W.bind(A._handleAddAnchorEvent,A),icon:V},{handler:W.bind(A._handleAddTaskEvent,A),icon:x},{handler:W.bind(A._handleCloseEvent,A),icon:P}]},aA);},_uiSetFields:function(aA){var A=this;if(A.get(av)){A.alignAnchors();setTimeout(function(){A.anchorsDrag.syncTargets();},50);}},_uiSetName:function(aB){var A=this;var aA=A.get(k);aA.setAttribute(i,W.DiagramNode.buildNodeId(aB));},_uiSetSelected:function(aA){var A=this;A.get(k).toggleClass(aw,aA);if(aA&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aB){var A=this;var aA=A._getContainer().getXY();this._posNode.setXY([aB[0]+aA[0],aB[1]+aA[1]]);}}});W.DiagramNode=az;W.DiagramBuilder.types[j]=W.DiagramNode;W.DiagramNodeTask=W.Component.create({NAME:s,ATTRS:{type:{value:t}},EXTENDS:W.DiagramNode});W.DiagramBuilder.types[t]=W.DiagramNodeTask;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});AUI.add("aui-diagram-builder-connector",function(k){var M=k.Lang,r=M.isArray,v=M.isBoolean,L=M.isNumber,z=M.isObject,h=M.isString,D=k.Array,b=function(A){return(A instanceof k.Anchor);},E=function(A){return(A instanceof k.ArrayList);},y="anchor",F="arrowPoints",C="body",G="boundingBox",N="builder",w="color",n="connector",a="dataAnchor",x="diagram",s="diagramNode",u="height",p="id",I="lazyDraw",j="maxSources",i="maxTargets",J="node",m="p1",l="p2",e="path",q="shape",g="sources",f="targets",B="viewport",c="width",K="wrapper",o=".",t=k.getClassName,d=t(x,N,y,J,K),H=t(x,N,y,J);k.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(T,O,V,A,U,R){var W=this;T.moveTo(O,V);T.lineTo(A,U);var P=Math.atan2(U-V,A-O),S=(A+O)/2,Q=(U+V)/2;W.drawPolygon(T,W.translatePoints(W.rotatePoints(R||W.ARROW_POINTS,P),S,Q));},drawPolygon:function(O,P){var A=this;O.moveTo(P[0][0],P[0][1]);D.each(P,function(R,Q){if(Q>0){O.lineTo(P[Q][0],P[Q][1]);}});O.lineTo(P[0][0],P[0][1]);O.end();},translatePoints:function(P,O,R){var A=this;var Q=[];D.each(P,function(T,S){Q.push([P[S][0]+O,P[S][1]+R]);});return Q;},rotatePoints:function(O,Q){var A=this;var P=[];D.each(O,function(S,R){P.push(A.rotatePoint(Q,O[R][0],O[R][1]));});return P;},rotatePoint:function(O,A,P){return[(A*Math.cos(O))-(P*Math.sin(O)),(A*Math.sin(O))+(P*Math.cos(O))];}};k.Connector=k.Base.create("line",k.Base,[],{graphics:null,shape:null,initializer:function(O){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(I)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var O=A.shape;var Q=A.getCoordinate(A.get(m));var P=A.getCoordinate(A.get(l));O.clear();k.PolygonUtil.drawLineArrow(O,Q[0],Q[1],P[0],P[1],A.get(F));},getCoordinate:function(P){var A=this;var O=A.get(B).getXY();return[P[0]-O[0],P[1]-O[1]];},_initGraphics:function(){var A=this;var O=new k.Graphic({width:A.get(c),height:A.get(u),render:A.get(B)});A.graphics=O;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(q));},_setShape:function(O){var A=this;return k.merge({type:e,stroke:{color:A.get(w),weight:2},fill:{color:A.get(w)}},O);}},{ATTRS:{color:{value:"#666",validator:h},lazyDraw:{value:false,validator:v},viewport:{setter:k.one,value:C},shape:{value:null,setter:"_setShape"},arrowPoints:{value:k.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:r},p2:{value:[0,0],validator:r}}});k.Anchor=k.Base.create("anchor",k.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({targetsChange:A._afterTargetsChange});},addSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O).add(O));},addTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O).add(O));},alignConnectors:function(){var A=this;A.get(f).each(function(O){var P=A.getConnector(O);if(P){P.set(m,A.getCenterXY());P.set(l,O.getCenterXY());}});A.get(g).each(function(O){var P=O.getConnector(A);if(P){P.set(m,O.getCenterXY());P.set(l,A.getCenterXY());}});return A;},destroy:function(){var A=this;A.disconnectTargets();A.disconnectSources();A.get(J).remove();},connect:function(O){var A=this;A.addTarget(O);if(!A.isConnected(O)){var P=O.get(n);P.p1=A.getCenterXY();P.p2=O.getCenterXY();A.connectors[O.get(p)]=new k.Connector(P);}return A;},connectTargets:function(){var A=this;A.get(f).each(k.bind(A.connect,A));return A;},disconnect:function(O){var A=this;A.getConnector(O).destroy();A.removeTarget(O);O.removeSource(A);},disconnectTargets:function(){var A=this;A.get(f).each(function(O){A.disconnect(O);});return A;},disconnectSources:function(){var A=this;A.get(g).each(function(O){O.disconnect(A);});return A;},getCenterXY:function(){var A=this;return A.get(J).getCenterXY();},getConnector:function(O){var A=this;return A.connectors[O.get(p)];},isConnected:function(O){var A=this;return A.connectors.hasOwnProperty(O.get(p));},updateSources:function(O){var A=this;A.set(g,O);return A;},updateTargets:function(O){var A=this;A.set(f,O);return A;},removeSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O));},removeTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O));},_afterActiveChange:function(O){var A=this;A._uiSetActive(O.newVal);},_afterTargetsChange:function(O){var A=this;O.prevVal.each(function(P){P.removeSource(A);});O.newVal.each(function(P){P.addSource(A);});},_renderNode:function(){var A=this;var P=A.get(s);var O=P.get(G);A.wrapper=O.one(o+d)||k.Node.create(A.ANCHOR_WRAPPER_TEMPLATE);A.wrapper.appendTo(O).appendChild(A.get(J));},_setConnector:function(O){var A=this;return k.merge({viewport:A.get(B)},O);},_setSources:function(O){var A=this;return A._setAnchors(O);},_setTargets:function(O){var A=this;O=A._setAnchors(O);O.each(function(P){P.addSource(A);});return O;},_setAnchors:function(P){var A=this;
+if(!E(P)){var O=[];k.Array.each(P,function(Q){if(h(Q)){Q=k.Anchor.getAnchorByNode(Q);}O.push(b(Q)?Q:new k.Anchor(Q));});P=new k.ArrayList(O);}return P;},_setNode:function(O){var A=this;var P=A.get(p);return k.one(O).set(p,P).setData(a,A);}},{ATTRS:{diagramNode:{},connector:{setter:"_setConnector",value:{},validator:z},id:{readOnly:true,valueFn:function(){return k.guid();}},maxSources:{value:Infinity,validator:L},maxTargets:{value:Infinity,validator:L},node:{setter:"_setNode",valueFn:function(){var A=this;return k.Node.create(A.NODE_TEMPLATE);}},sources:{value:[],setter:"_setSources",validator:function(A){return r(A)||E(A);}},targets:{value:[],setter:"_setTargets",validator:function(A){return r(A)||E(A);}},viewport:{setter:k.one,value:C}},getAnchorByNode:function(A){return k.one(A).getData(a);}});},"@VERSION@",{requires:["aui-base","arraylist-add","arraylist-filter","json","graphics","dd"],skinnable:true});AUI.add("aui-diagram-builder",function(a){},"@VERSION@",{use:["aui-diagram-builder-base","aui-diagram-builder-impl","aui-diagram-builder-connector"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder.js b/build/aui-diagram-builder/aui-diagram-builder.js
index fbc2659c074..42d2ebf15d9 100644
--- a/build/aui-diagram-builder/aui-diagram-builder.js
+++ b/build/aui-diagram-builder/aui-diagram-builder.js
@@ -34,7 +34,7 @@ var Lang = A.Lang,
CONTAINER = 'container',
CONTENT = 'content',
CONTENT_BOX = 'contentBox',
- CONTENT_CONTAINER = 'contentContainer',
+ VIEWPORT = 'viewport',
CONTENT_NODE = 'contentNode',
CREATE_DOCUMENT_FRAGMENT = 'createDocumentFragment',
DIAGRAM = 'diagram',
@@ -72,9 +72,10 @@ var Lang = A.Lang,
_SPACE = ' ',
_DOT = '.',
_DOLLAR = '$',
+ _HASH = '#',
CSS_DIAGRAM_BUILDER_BASE_DROP_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, DROP, CONTAINER),
- CSS_DIAGRAM_BUILDER_BASE_CONTENT_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, CONTENT, CONTAINER),
+ CSS_DIAGRAM_BUILDER_BASE_VIEWPORT = AgetClassName(DIAGRAM, BUILDER, BASE, VIEWPORT),
CSS_DIAGRAM_BUILDER_BASE_FIELD = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD),
CSS_DIAGRAM_BUILDER_BASE_FIELDS_CONTAINER = AgetClassName(DIAGRAM, BUILDER, BASE, FIELDS, CONTAINER),
CSS_DIAGRAM_BUILDER_BASE_FIELD_DRAGGABLE = AgetClassName(DIAGRAM, BUILDER, BASE, FIELD, DRAGGABLE),
@@ -144,6 +145,18 @@ var AvailableField = A.Component.create({
EXTENDS: A.Base,
+ buildNodeId: function(id) {
+ return AVAILABLE_FIELDS + _DOLLAR + FIELD + _DOLLAR + id;
+ },
+
+ getAvailableFieldByNode: function(node) {
+ return A.one(node).getData(AVAILABLE_FIELD);
+ },
+
+ getAvailableFieldById: function(id) {
+ return A.AvailableField.getAvailableFieldByNode(_HASH+A.AvailableField.buildNodeId(id));
+ },
+
prototype: {
FIELD_ITEM_TEMPLATE: '',
+ VIEWPORT_TEMPLATE: '',
fieldsNode: null,
propertyList: null,
@@ -422,7 +432,7 @@ var DiagramBuilderBase = A.Component.create(
instance.after(instance._afterUiSetHeight, instance, '_uiSetHeight');
- instance.contentContainer = instance.get(CONTENT_CONTAINER);
+ instance.viewport = instance.get(VIEWPORT);
instance.dropContainer = instance.get(DROP_CONTAINER);
instance.fieldsContainer = instance.get(FIELDS_CONTAINER);
instance.toolbarContainer = instance.get(TOOLBAR_CONTAINER);
@@ -435,14 +445,20 @@ var DiagramBuilderBase = A.Component.create(
return (drag === availableFieldsDrag.dd);
},
- plotFields: function(fields) {
+ plotFields: function() {
+ var instance = this;
+ var fields = instance.get(FIELDS);
+
+ fields.each(function(field) {
+ instance.plotField(field);
+ });
},
renderUI: function() {
var instance = this;
instance._renderTabs();
- instance._renderContentContainer();
+ instance._renderViewport();
instance._uiSetAvailableFields(
instance.get(AVAILABLE_FIELDS)
@@ -498,13 +514,13 @@ var DiagramBuilderBase = A.Component.create(
instance.fire(SAVE);
},
- _renderContentContainer: function() {
+ _renderViewport: function() {
var instance = this;
var contentBox = instance.get(CONTENT_BOX);
- var contentContainer = instance.contentContainer;
+ var viewport = instance.viewport;
- contentContainer.appendChild(instance.dropContainer);
- contentBox.appendChild(contentContainer);
+ viewport.appendChild(instance.dropContainer);
+ contentBox.appendChild(viewport);
},
_renderPropertyList: function() {
@@ -588,6 +604,7 @@ var DiagramBuilderBase = A.Component.create(
return A.merge(
{
bubbleTargets: instance,
+ groups: [AVAILABLE_FIELDS],
node: instance.dropContainer
},
val || {}
@@ -602,6 +619,7 @@ var DiagramBuilderBase = A.Component.create(
bubbleTargets: instance,
container: instance.get(BOUNDING_BOX),
dragConfig: {
+ groups: [AVAILABLE_FIELDS],
plugins: [
{
cfg: {
@@ -723,6 +741,7 @@ var Lang = A.Lang,
isArray = Lang.isArray,
isObject = Lang.isObject,
isString = Lang.isString,
+ isBoolean = Lang.isBoolean,
AArray = A.Array,
@@ -734,6 +753,10 @@ var Lang = A.Lang,
return (val instanceof A.DiagramNode);
},
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
getLeftTop = function(container, node) {
var nodeXY = isArray(node) ? node : node.getXY();
var containerXY = isArray(container) ? container : container.getXY();
@@ -743,44 +766,123 @@ var Lang = A.Lang,
});
},
+ ANCHOR = 'anchor',
+ ANCHORS = 'anchors',
+ ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
+ CANCEL = 'cancel',
+ CLICK = 'click',
+ CONTENT = 'content',
+ CONTROLS = 'controls',
+ CONTROLS_TOOLBAR = 'controlsToolbar',
DATA = 'data',
DBLCLICK = 'dblclick',
+ DELETE = 'delete',
+ DELETE_MESSAGE = 'deleteMessage',
DESCRIPTION = 'description',
DIAGRAM = 'diagram',
- DIAGRAM_BUILDER = 'diagram-builder',
- DIAGRAM_NODE = 'diagram-node',
+ DIAGRAM_BUILDER_NAME = 'diagram-builder',
+ DIAGRAM_NODE = 'diagramNode',
+ DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ ESC = 'esc',
+ FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
+ HOVER = 'hover',
+ KEYDOWN = 'keydown',
+ LINK = 'link',
+ MOUSEENTER = 'mouseenter',
+ MOUSELEAVE = 'mouseleave',
NAME = 'name',
NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
PARENT_NODE = 'parentNode',
RECORDS = 'records',
RECORDSET = 'recordset',
+ REGION = 'region',
RENDERED = 'rendered',
+ SELECTED = 'selected',
+ SHUFFLE = 'shuffle',
+ TASK = 'task',
+ TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
+ VIEWPORT = 'viewport',
+ WRAPPER = 'wrapper',
XY = 'xy',
_DOT = '.',
+ _DOLLAR = '$',
_EMPTY_STR = '',
+ _DASH = '-',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_CONTROLS = AgetClassName(DIAGRAM, BUILDER, CONTROLS),
+ CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE),
+ CSS_DIAGRAM_NODE_CONTENT = AgetClassName(DIAGRAM, NODE, CONTENT),
CSS_DIAGRAM_NODE_EDITING = AgetClassName(DIAGRAM, NODE, EDITING),
- CSS_DIAGRAM_NODE = AgetClassName(DIAGRAM, NODE);
+ CSS_DIAGRAM_NODE_SELECTED = AgetClassName(DIAGRAM, NODE, SELECTED);
+
+// REMOVE THIS!
+var __dump = function() {
+ var PAD = ' ', BR = '
';
+
+ A.all('.aui-diagram-node').each(function(n) {
+ var b = _EMPTY_STR,
+ dn = A.Widget.getByNode(n),
+ dnName = dn.get('name'),
+ dnBB = dn.get('boundingBox'),
+ log = dnBB.one('.log') || A.Node.create('').appendTo(dnBB);
+
+ b += dnName + BR;
+
+ dn.get(FIELDS).each(function(a) {
+ b += PAD + 'a: ' + a.get('id') + BR;
+
+ a.get('targets').each(function(t) {
+ var tdn = t.get(DIAGRAM_NODE);
+
+ t.get('node').setContent(t.get('id'));
+
+ b += PAD + PAD + 't: ' + tdn.get('name') + ' (s: ' + t.get('id') + ')' + BR;
+ });
+
+ a.get('sources').each(function(s) {
+ var sdn = s.get(DIAGRAM_NODE);
+
+ s.get('node').setContent(s.get('id'));
+
+ b += PAD + PAD + 's: ' + sdn.get('name') + ' (t: ' + s.get('id') + ')' + BR;
+ });
+ });
+
+ log.setContent(b);
+ });
+};
+// END.
var DiagramBuilder = A.Component.create({
- NAME: DIAGRAM_BUILDER,
+ NAME: DIAGRAM_BUILDER_NAME,
ATTRS: {
fieldsDragConfig: {
value: null,
setter: '_setFieldsDragConfig',
validator: isObject
+ },
+
+ tmpConnector: {
+ setter: '_setTmpConnector',
+ value: {},
+ validator: isObject
}
},
@@ -797,12 +899,18 @@ var DiagramBuilder = A.Component.create({
instance.on({
cancel: instance._onCancel,
+ 'drag:drag': instance._onDrag,
'drag:end': instance._onDragEnd,
'drop:hit': instance._onDropHit,
save: instance._onSave
});
+ instance.handlerKeyDown = A.getDoc().on(KEYDOWN, A.bind(instance._afterKeyEvent, instance));
+
+ instance.dropContainer.delegate(CLICK, A.bind(instance._onNodeClick, instance), _DOT+CSS_DIAGRAM_NODE);
instance.dropContainer.delegate(DBLCLICK, A.bind(instance._onNodeEdit, instance), _DOT+CSS_DIAGRAM_NODE);
+ instance.dropContainer.delegate(MOUSEENTER, A.bind(instance._onMouseenterAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
+ instance.dropContainer.delegate(MOUSELEAVE, A.bind(instance._onMouseleaveAnchors, instance), _DOT+CSS_DB_ANCHOR_NODE);
},
syncUI: function() {
@@ -811,12 +919,17 @@ var DiagramBuilder = A.Component.create({
A.DiagramBuilder.superclass.syncUI.apply(this, arguments);
instance._setupFieldsDrag();
+
+ instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
createField: function(val) {
var instance = this;
if (!isDiagramNode(val)) {
+ // val.bubbleTargets = instance;
+ val.builder = instance;
+ val.viewport = instance.get(VIEWPORT);
val = new (instance.getFieldClass(val.type || NODE))(val);
}
@@ -846,21 +959,31 @@ var DiagramBuilder = A.Component.create({
return (drag === fieldsDrag.dd);
},
- plotFields: function() {
+ plotField: function(field) {
var instance = this;
- var fields = instance.get(FIELDS);
- fields.each(function(field) {
- instance.plotField(field);
- });
+ if (!field.get(RENDERED)) {
+ field.render(instance.dropContainer);
+ }
},
- plotField: function(field) {
+ unselectAll: function() {
var instance = this;
+ var selectedNode = instance.selectedNode;
- if (!field.get(RENDERED)) {
- field.render(instance.dropContainer);
+ if (selectedNode) {
+ selectedNode.set(SELECTED, false);
}
+
+ instance.selectedNode = null;
+ },
+
+ select: function(diagramNode) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
startEditingNode: function(diagramNode) {
@@ -871,8 +994,6 @@ var DiagramBuilder = A.Component.create({
instance.tabView.selectTab(A.DiagramBuilder.SETTINGS_TAB);
- // instance._renderPropertyList();
-
instance.propertyList.set(RECORDSET, diagramNode.getProperties());
diagramNode.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE_EDITING);
@@ -894,12 +1015,42 @@ var DiagramBuilder = A.Component.create({
}
},
+ _afterKeyEvent: function(event) {
+ var instance = this;
+
+ if (!instance.selectedNode || event.hasModifier() || !event.isKeyInSet(ESC, DELETE)) {
+ return;
+ }
+
+ if (event.isKey(ESC)) {
+ instance._onEscKey(event);
+ }
+ else if (event.isKey(DELETE)) {
+ instance._onDeleteKey(event);
+ }
+
+ event.halt();
+ },
+
_onCancel: function(event) {
var instance = this;
instance.stopEditingNode();
},
+ _onDrag: function(event) {
+ var instance = this;
+ var drag = event.target;
+
+ if (instance.isFieldsDrag(drag)) {
+ var diagramNode = A.Widget.getByNode(drag.get(DRAG_NODE));
+
+ diagramNode.get(FIELDS).each(function(anchor) {
+ anchor.alignConnectors();
+ });
+ }
+ },
+
_onDragEnd: function(event) {
var instance = this;
var drag = event.target;
@@ -918,15 +1069,56 @@ var DiagramBuilder = A.Component.create({
if (instance.isAvailableFieldsDrag(drag)) {
var availableField = drag.get(NODE).getData(AVAILABLE_FIELD);
- instance.addField({
+ var newField = instance.addField({
xy: getLeftTop(drag.lastXY, instance.dropContainer),
- type: availableField.get(TYPE)
+ type: availableField.get(TYPE),
+ fields: [{}]
});
+
+ instance.select(newField);
}
},
+ _onDeleteKey: function(event) {
+ var instance = this;
+
+ instance.selectedNode.close();
+ },
+
+ _onEscKey: function(event) {
+ var instance = this;
+
+ instance.unselectAll();
+ instance.stopEditingNode();
+ },
+
+ _onMouseenterAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.addClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onMouseleaveAnchors: function(event) {
+ var instance = this;
+
+ event.currentTarget.removeClass(CSS_DB_ANCHOR_HOVER);
+ },
+
+ _onNodeClick: function(event) {
+ var instance = this;
+ var diagramNode = A.Widget.getByNode(event.currentTarget);
+
+ instance.select(diagramNode);
+ },
+
_onNodeEdit: function(event) {
var instance = this;
+
+ // Only enable editing if the double clicked node is inside the node contentBox.
+ if (!event.target.ancestor(_DOT+CSS_DIAGRAM_NODE_CONTENT, true)) {
+ return;
+ }
+
var diagramNode = A.Widget.getByNode(event.currentTarget);
if (diagramNode) {
@@ -950,6 +1142,18 @@ var DiagramBuilder = A.Component.create({
}
},
+ _setTmpConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ lazyDraw: true,
+ viewport: instance.viewport
+ },
+ val
+ );
+ },
+
_setFieldsDragConfig: function(val) {
var instance = this;
var dropContainer = instance.dropContainer;
@@ -994,11 +1198,32 @@ A.DiagramBuilder = DiagramBuilder;
A.DiagramBuilder.types = {};
+var DiagramNodeOverlay = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ EXTENDS: A.Overlay,
+
+ // A.FieldSupport augment the class with "fields" attribute and util methods
+ // such as: addField, removeField. Although the attribute is called "fields" due to
+ // the augmentation, those fields are the anchors. TODO: Allow A.FieldSupport to
+ // customize the name of the attribute and method sufixes.
+ AUGMENTS: [A.FieldSupport]
+});
+
var DiagramNode = A.Component.create({
- NAME: DIAGRAM_NODE,
+ NAME: DIAGRAM_NODE_NAME,
+
+ UI_ATTRS: [FIELDS, NAME, SELECTED],
ATTRS: {
+ anchorsDragConfig: {
+ value: null,
+ setter: '_setAnchorsDragConfig',
+ validator: isObject
+ },
+
builder: {
+ setter: '_setBuilder',
validator: isDiagramBuilder
},
@@ -1008,7 +1233,7 @@ var DiagramNode = A.Component.create({
},
height: {
- value: 100
+ value: 90
},
name: {
@@ -1020,8 +1245,14 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ selected: {
+ value: false,
+ validator: isBoolean
+ },
+
strings: {
value: {
+ deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
name: 'Name',
type: 'Type'
@@ -1033,14 +1264,114 @@ var DiagramNode = A.Component.create({
validator: isString
},
+ controlsToolbar: {
+ setter: '_setControlsToolbar',
+ validator: isObject,
+ value: null
+ },
+
width: {
- value: 200
+ value: 90
+ },
+
+ zIndex: {
+ value: 100
+ },
+
+ tabIndex: {
+ value: 1
}
},
- EXTENDS: A.Overlay,
+ EXTENDS: DiagramNodeOverlay,
+
+ buildNodeId: function(id) {
+ return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ },
prototype: {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ CONTROLS_TEMPLATE: '',
+
+ initializer: function() {
+ var instance = this;
+
+ instance._renderNodes();
+ instance._setupAnchorsDrag();
+
+ instance.after({
+ render: instance._afterRender
+ });
+
+ instance.on({
+ 'drag:drag': instance._onAnchorDrag,
+ 'drag:end': instance._onAnchorDragEnd,
+ 'drag:start': instance._onAnchorDragStart,
+ 'drop:hit': instance._onAnchorDropHit
+ });
+
+ instance.get(BOUNDING_BOX).addClass(CSS_DIAGRAM_NODE+_DASH+instance.get(TYPE));
+
+ // REMOVE THIS!
+ instance.set('bodyContent', instance.get(NAME));
+ },
+
+ alignAnchors: function() {
+ var instance = this;
+ var anchors = instance.get(FIELDS);
+
+ var cRegion = instance.get(BOUNDING_BOX).get(REGION),
+ dAngle = Math.floor(360/anchors.size()),
+ a = cRegion.width/2,
+ b = cRegion.height/2,
+ centerX = cRegion.left + cRegion.width/2,
+ centerY = cRegion.top + cRegion.height/2;
+
+ anchors.each(function(anchor, index) {
+ var anchorNode = anchor.get(NODE);
+ var aRegion = anchorNode.get(REGION);
+ var exy = instance._getEllipseXY(a, b, centerX, centerY, index*dAngle);
+
+ anchorNode.setXY([ exy[0] - aRegion.width/2, exy[1] - aRegion.height/2 ]);
+
+ anchor.alignConnectors();
+ });
+
+ return instance;
+ },
+
+ close: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ if (confirm(strings[DELETE_MESSAGE])) {
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.destroy();
+ });
+
+ instance.destroy();
+ }
+
+ __dump();
+
+ return instance;
+ },
+
+ createField: function(val) {
+ var instance = this;
+
+ if (!isAnchor(val)) {
+ var builder = instance.get(BUILDER);
+
+ val.diagramNode = instance;
+ val.viewport = (builder ? builder.get(VIEWPORT) : null);
+
+ val = new A.Anchor(val);
+ }
+
+ return val;
+ },
+
getLeftTop: function() {
var instance = this;
@@ -1089,31 +1420,829 @@ var DiagramNode = A.Component.create({
];
},
+ _afterRender: function(event) {
+ var instance = this;
+
+ instance.alignAnchors();
+
+ instance._renderControls();
+ },
+
_getContainer: function() {
var instance = this;
return (instance.get(BUILDER).dropContainer || instance.get(BOUNDING_BOX).get(PARENT_NODE));
},
- _uiSetXY : function(val) {
+ _getEllipseXY: function(a, b, centerX, centerY, angle) {
+ var t = angle*Math.PI/180;
+
+ return [ centerX + a*Math.cos(t), centerY - b*Math.sin(t) ];
+ },
+
+ _handleAddAnchorEvent: function(event) {
var instance = this;
- var containerXY = instance._getContainer().getXY();
- this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
- }
- }
-});
+ instance.addField({});
-DiagramNode.buildNodeId = function(id) {
- return DIAGRAM_NODE + _DOLLAR + FIELD + _DOLLAR + id;
-};
+ // event.halt();
+ },
-A.DiagramNode = DiagramNode;
+ _handleAddTaskEvent: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
-A.DiagramBuilder.types['node'] = A.DiagramNode;
+ var diagramNode = new A.DiagramNode({
+ type: NODE,
+ xy: [100, 100] // TODO - find best position?
+ });
-}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
+ builder.addField(diagramNode);
+
+ var source = instance.addField({});
+ var target = diagramNode.addField({});
+ source.connect(target);
+ },
+
+ _handleCloseEvent: function(event) {
+ var instance = this;
+
+ instance.close();
+ },
+
+ _onAnchorDrag: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P2, event.target.get(DRAG_NODE).getCenterXY());
+ },
+
+ _onAnchorDragEnd: function(event) {
+ var instance = this;
+ var shape = instance.get(BUILDER).tmpConnector.shape;
+
+ shape.clear();
+ shape.end();
+ },
+
+ _onAnchorDragStart: function(event) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ builder.tmpConnector.set(P1, event.target.get(NODE).getCenterXY());
+ },
+
+ _onAnchorDropHit: function(event) {
+ var instance = this;
+ var source = A.Anchor.getAnchorByNode(event.drag.get(NODE));
+ var target = A.Anchor.getAnchorByNode(event.drop.get(NODE));
+
+ source.connect(target);
+
+ __dump();
+ },
+
+ _renderControls: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.controlsNode = A.Node.create(instance.CONTROLS_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderNodes: function() {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ instance.anchorWrapper = A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE).appendTo(boundingBox);
+ },
+
+ _renderControlsToolbar: function(event) {
+ var instance = this;
+
+ instance.controlsToolbar = new A.Toolbar(
+ instance.get(CONTROLS_TOOLBAR)
+ )
+ .render(instance.controlsNode);
+ },
+
+ _setBuilder: function(val) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ anchor.set(VIEWPORT, val.get(VIEWPORT));
+ });
+
+ return val;
+ },
+
+ _setAnchorsDragConfig: function(val) {
+ var instance = this;
+ var builder = instance.get(BUILDER);
+
+ return A.merge(
+ {
+ bubbleTargets: instance,
+ container: instance.anchorWrapper,
+ dragConfig: {
+ groups: [ANCHORS],
+ plugins: [
+ {
+ cfg: {
+ constrain: (builder ? builder.get(VIEWPORT) : null)
+ },
+ fn: A.Plugin.DDConstrained
+ },
+ {
+ cfg: {
+ scrollDelay: 150
+ },
+ fn: A.Plugin.DDWinScroll
+ },
+ {
+ cfg: {
+ moveOnEnd: false
+ },
+ fn: A.Plugin.DDProxy
+ }
+ ]
+ },
+ nodes: _DOT+CSS_DB_ANCHOR_NODE,
+ target: true
+ },
+ val || {}
+ );
+ },
+
+ _setupAnchorsDrag: function() {
+ var instance = this;
+
+ instance.anchorsDrag = new A.DD.Delegate(
+ instance.get(ANCHORS_DRAG_CONFIG)
+ );
+ },
+
+ _setControlsToolbar: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK
+ },
+ {
+ handler: A.bind(instance._handleAddTaskEvent, instance),
+ icon: SHUFFLE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL
+ }
+ ]
+ },
+ val
+ );
+ },
+
+ _uiSetFields: function(val) {
+ var instance = this;
+
+ if (instance.get(RENDERED)) {
+ instance.alignAnchors();
+
+ setTimeout(function() {
+ instance.anchorsDrag.syncTargets();
+ }, 50);
+ }
+ },
+
+ _uiSetName: function(val) {
+ var instance = this;
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetSelected: function(val) {
+ var instance = this;
+
+ instance.get(BOUNDING_BOX).toggleClass(CSS_DIAGRAM_NODE_SELECTED, val);
+
+ if (val && !instance.controlsToolbar) {
+ instance._renderControlsToolbar();
+ }
+
+ // if (instance.get(RENDERED)) {
+ // instance.alignAnchors();
+ // }
+ },
+
+ _uiSetXY : function(val) {
+ var instance = this;
+ var containerXY = instance._getContainer().getXY();
+
+ this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
+ }
+ }
+});
+
+A.DiagramNode = DiagramNode;
+
+A.DiagramBuilder.types[NODE] = A.DiagramNode;
+
+A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNode
+});
+
+A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+// TODO deletar anchors OK
+// TODO deletar connections (delete) OK
+// TODO Adicionar overlay de controles OK
+// TODO syncTargets dd delegate
+
+
+// TODO gerar XML
+// TODO reposicionar setas?
+// TODO Adicionar groups/validation for connection
+
+}, '@VERSION@' ,{requires:['aui-diagram-builder-base','overlay'], skinnable:true});
+AUI.add('aui-diagram-builder-connector', function(A) {
+var Lang = A.Lang,
+ isArray = Lang.isArray,
+ isBoolean = Lang.isBoolean,
+ isNumber = Lang.isNumber,
+ isObject = Lang.isObject,
+ isString = Lang.isString,
+
+ YArray = A.Array,
+
+ isAnchor = function(val) {
+ return (val instanceof A.Anchor);
+ },
+
+ isArrayList = function(val) {
+ return (val instanceof A.ArrayList);
+ },
+
+ ANCHOR = 'anchor',
+ ARROW_POINTS = 'arrowPoints',
+ BODY = 'body',
+ BOUNDING_BOX = 'boundingBox',
+ BUILDER = 'builder',
+ COLOR = 'color',
+ CONNECTOR = 'connector',
+ DATA_ANCHOR = 'dataAnchor',
+ DIAGRAM = 'diagram',
+ DIAGRAM_NODE = 'diagramNode',
+ HEIGHT = 'height',
+ ID = 'id',
+ LAZY_DRAW = 'lazyDraw',
+ MAX_SOURCES = 'maxSources',
+ MAX_TARGETS = 'maxTargets',
+ NODE = 'node',
+ P1 = 'p1',
+ P2 = 'p2',
+ PATH = 'path',
+ SHAPE = 'shape',
+ SOURCES = 'sources',
+ TARGETS = 'targets',
+ VIEWPORT = 'viewport',
+ WIDTH = 'width',
+ WRAPPER = 'wrapper',
+
+ _DOT = '.',
+
+ AgetClassName = A.getClassName,
+
+ CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
+ CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
+
+A.PolygonUtil = {
+ ARROW_POINTS: [
+ [ -12, -6 ],
+ [ -8, 0 ],
+ [ -12, 6 ],
+ [ 6, 0 ]
+ ],
+
+ drawLineArrow: function(shape, x1, y1, x2, y2, arrowPoints) {
+ var instance = this;
+
+ shape.moveTo(x1, y1);
+ shape.lineTo(x2, y2);
+
+ var angle = Math.atan2(y2-y1, x2-x1), centerX = (x2+x1)/2, centerY = (y2+y1)/2;
+
+ instance.drawPolygon(
+ shape,
+ instance.translatePoints(instance.rotatePoints(arrowPoints || instance.ARROW_POINTS, angle), centerX, centerY)
+ );
+ },
+
+ drawPolygon: function(shape, points) {
+ var instance = this;
+
+ shape.moveTo(points[0][0], points[0][1]);
+
+ YArray.each(points, function(p, i) {
+ if (i > 0) {
+ shape.lineTo(points[i][0], points[i][1]);
+ }
+ });
+
+ shape.lineTo(points[0][0], points[0][1]);
+ shape.end();
+ },
+
+ translatePoints: function(points, x, y) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push([ points[i][0] + x, points[i][1] + y ]);
+ });
+
+ return xy;
+ },
+
+ rotatePoints: function(points, angle) {
+ var instance = this;
+ var xy = [];
+
+ YArray.each(points, function(p, i) {
+ xy.push(instance.rotatePoint(angle, points[i][0], points[i][1]));
+ });
+
+ return xy;
+ },
+
+ rotatePoint: function(angle, x, y) {
+ return [
+ (x * Math.cos(angle)) - (y * Math.sin(angle)),
+ (x * Math.sin(angle)) + (y * Math.cos(angle))
+ ];
+ }
+};
+
+A.Connector = A.Base.create('line', A.Base, [], {
+ graphics: null,
+ shape: null,
+
+ initializer: function(config) {
+ var instance = this;
+
+ instance.after({
+ p1Change: instance.draw,
+ p2Change: instance.draw
+ });
+
+ instance._initGraphics();
+ instance._initShapes();
+
+ if (!instance.get(LAZY_DRAW)) {
+ instance.draw();
+ }
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.graphics.destroy();
+ },
+
+ draw: function() {
+ var instance = this;
+ var shape = instance.shape;
+
+ var c1 = instance.getCoordinate(instance.get(P1));
+ var c2 = instance.getCoordinate(instance.get(P2));
+
+ shape.clear();
+
+ A.PolygonUtil.drawLineArrow(shape, c1[0], c1[1], c2[0], c2[1], instance.get(ARROW_POINTS));
+ },
+
+ getCoordinate: function(p) {
+ var instance = this;
+ var xy = instance.get(VIEWPORT).getXY();
+
+ return [ p[0] - xy[0], p[1] - xy[1] ];
+ },
+
+ _initGraphics: function() {
+ var instance = this;
+
+ var graphics = new A.Graphic({
+ width: instance.get(WIDTH),
+ height: instance.get(HEIGHT),
+ render: instance.get(VIEWPORT)
+ });
+
+ instance.graphics = graphics;
+ },
+
+ _initShapes: function() {
+ var instance = this;
+
+ instance.shape = instance.graphics.getShape(
+ instance.get(SHAPE)
+ );
+ },
+
+ _setShape: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ type: PATH,
+ stroke: {
+ color: instance.get(COLOR),
+ weight: 2
+ },
+ fill: {
+ color: instance.get(COLOR)
+ }
+ },
+ val
+ );
+ }
+},{
+ ATTRS: {
+ color: {
+ value: '#666',
+ validator: isString
+ },
+
+ lazyDraw: {
+ value: false,
+ validator: isBoolean
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ },
+
+ shape: {
+ value: null,
+ setter: '_setShape'
+ },
+
+ arrowPoints: {
+ value: A.PolygonUtil.ARROW_POINTS
+ },
+
+ p1: {
+ value: [0, 0],
+ validator: isArray
+ },
+
+ p2: {
+ value: [0, 0],
+ validator: isArray
+ }
+ }
+});
+
+A.Anchor = A.Base.create('anchor', A.Base, [], {
+ ANCHOR_WRAPPER_TEMPLATE: '',
+ NODE_TEMPLATE: '',
+
+ connectors: null,
+
+ initializer: function() {
+ var instance = this;
+
+ instance.connectors = {};
+
+ instance._renderNode();
+
+ instance.connectTargets();
+
+ instance.after({
+ targetsChange: instance._afterTargetsChange
+ });
+ },
+
+ addSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ },
+
+ addTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ },
+
+ alignConnectors: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ var tConnector = instance.getConnector(target);
+
+ if (tConnector) {
+ tConnector.set(P1, instance.getCenterXY());
+ tConnector.set(P2, target.getCenterXY());
+ }
+ });
+
+ instance.get(SOURCES).each(function(source) {
+ var sConnector = source.getConnector(instance);
+
+ if (sConnector) {
+ sConnector.set(P1, source.getCenterXY());
+ sConnector.set(P2, instance.getCenterXY());
+ }
+ });
+
+ return instance;
+ },
+
+ destroy: function() {
+ var instance = this;
+
+ instance.disconnectTargets();
+ instance.disconnectSources();
+
+ instance.get(NODE).remove();
+ },
+
+ connect: function(target) {
+ var instance = this;
+
+ instance.addTarget(target);
+
+ if (!instance.isConnected(target)) {
+ var tConnector = target.get(CONNECTOR);
+
+ tConnector.p1 = instance.getCenterXY();
+ tConnector.p2 = target.getCenterXY();
+
+ instance.connectors[target.get(ID)] = new A.Connector(tConnector);
+ }
+
+ return instance;
+ },
+
+ connectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(A.bind(instance.connect, instance));
+
+ return instance;
+ },
+
+ disconnect: function(target) {
+ var instance = this;
+
+ instance.getConnector(target).destroy();
+
+ instance.removeTarget(target);
+ target.removeSource(instance);
+ },
+
+ disconnectTargets: function() {
+ var instance = this;
+
+ instance.get(TARGETS).each(function(target) {
+ instance.disconnect(target);
+ });
+
+ return instance;
+ },
+
+ disconnectSources: function() {
+ var instance = this;
+
+ instance.get(SOURCES).each(function(source) {
+ source.disconnect(instance);
+ });
+
+ return instance;
+ },
+
+ getCenterXY: function() {
+ var instance = this;
+
+ return instance.get(NODE).getCenterXY();
+ },
+
+ getConnector: function(target) {
+ var instance = this;
+
+ return instance.connectors[target.get(ID)];
+ },
+
+ isConnected: function(target) {
+ var instance = this;
+
+ return instance.connectors.hasOwnProperty(target.get(ID));
+ },
+
+ updateSources: function(sources) {
+ var instance = this;
+
+ instance.set(SOURCES, sources);
+
+ return instance;
+ },
+
+ updateTargets: function(targets) {
+ var instance = this;
+
+ instance.set(TARGETS, targets);
+
+ return instance;
+ },
+
+ removeSource: function(source) {
+ var instance = this;
+
+ return instance.updateSources(
+ instance.get(SOURCES).remove(source)
+ );
+ },
+
+ removeTarget: function(target) {
+ var instance = this;
+
+ return instance.updateTargets(
+ instance.get(TARGETS).remove(target)
+ );
+ },
+
+ _afterActiveChange: function(event) {
+ var instance = this;
+
+ instance._uiSetActive(event.newVal);
+ },
+
+ _afterTargetsChange: function(event) {
+ var instance = this;
+
+ // TODO - event.prevVal is always equal to event.newVal, review this
+ // logic below, references between anchors needs to be cleaned up otherwise
+ // will store the wrong relationship between nodes.
+
+ event.prevVal.each(function(anchor) {
+ anchor.removeSource(instance);
+ });
+
+ event.newVal.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+ },
+
+ _renderNode: function() {
+ var instance = this;
+ var diagramNode = instance.get(DIAGRAM_NODE);
+ var container = diagramNode.get(BOUNDING_BOX);
+
+ instance.wrapper = container.one(_DOT+CSS_DB_ANCHOR_NODE_WRAPPER) ||
+ A.Node.create(instance.ANCHOR_WRAPPER_TEMPLATE);
+
+ instance.wrapper.appendTo(container).appendChild(instance.get(NODE));
+ },
+
+ _setConnector: function(val) {
+ var instance = this;
+
+ return A.merge(
+ {
+ viewport: instance.get(VIEWPORT)
+ },
+ val
+ );
+ },
+
+ _setSources: function(val) {
+ var instance = this;
+
+ return instance._setAnchors(val);
+ },
+
+ _setTargets: function(val) {
+ var instance = this;
+
+ val = instance._setAnchors(val);
+
+ val.each(function(anchor) {
+ anchor.addSource(instance);
+ });
+
+ return val;
+ },
+
+ _setAnchors: function(val) {
+ var instance = this;
+
+ if (!isArrayList(val)) {
+ var targets = [];
+
+ A.Array.each(val, function(target) {
+ if (isString(target)) {
+ // TODO - need this?
+ target = A.Anchor.getAnchorByNode(target);
+ }
+
+ targets.push( isAnchor(target) ? target : new A.Anchor(target) );
+ });
+
+ val = new A.ArrayList(targets);
+ }
+
+ return val;
+ },
+
+ _setNode: function(val) {
+ var instance = this;
+ var id = instance.get(ID);
+
+ return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ }
+},{
+ ATTRS: {
+ diagramNode: {
+ },
+
+ connector: {
+ setter: '_setConnector',
+ value: {},
+ validator: isObject
+ },
+
+ id: {
+ readOnly: true,
+ valueFn: function() {
+ return A.guid();
+ }
+ },
+
+ maxSources: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ maxTargets: {
+ value: Infinity,
+ validator: isNumber
+ },
+
+ node: {
+ setter: '_setNode',
+ valueFn: function() {
+ var instance = this;
+
+ return A.Node.create(instance.NODE_TEMPLATE);
+ }
+ },
+
+ sources: {
+ value: [],
+ setter: '_setSources',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ targets: {
+ value: [],
+ setter: '_setTargets',
+ validator: function(val) {
+ return isArray(val) || isArrayList(val);
+ }
+ },
+
+ viewport: {
+ setter: A.one,
+ value: BODY
+ }
+ },
+
+ getAnchorByNode: function(node) {
+ return A.one(node).getData(DATA_ANCHOR);
+ }
+});
+
+}, '@VERSION@' ,{requires:['aui-base','arraylist-add','arraylist-filter','json','graphics','dd'], skinnable:true});
-AUI.add('aui-diagram-builder', function(A){}, '@VERSION@' ,{use:['aui-diagram-builder-base','aui-diagram-builder-impl'], skinnable:true});
+AUI.add('aui-diagram-builder', function(A){}, '@VERSION@' ,{use:['aui-diagram-builder-base','aui-diagram-builder-impl','aui-diagram-builder-connector'], skinnable:true});
diff --git a/build/aui/aui-min.js b/build/aui/aui-min.js
index b6ca77c4cf0..f35aa52dadc 100644
--- a/build/aui/aui-min.js
+++ b/build/aui/aui-min.js
@@ -10,5 +10,5 @@ if(typeof YUI!="undefined"){YUI._YUI=YUI;}var YUI=function(){var c=0,f=this,b=ar
if(!(z in v)||(w&&v[z]==w)){v[z]=x.apply(x,arguments);}return v[z];};};b.merge=function(){var x=arguments,y=0,w=x.length,v={};for(;yAlloy - diagram-builder-base Demo
AUI().use('aui-diagram-builder', function(A) {
- var availableFields = [
- {
- type: 'node',
- label: 'Node',
- iconClass: 'node-icon'
+ var Lang = A.Lang,
+ isBoolean = Lang.isBoolean,
+
+ AArray = A.Array,
+
+ ACTIONS = 'actions',
+ ADD_ANCHOR = 'addAnchor',
+ ADD_ANCHOR_MESSAGE = 'addAnchorMessage',
+ ADD_NODE = 'addNode',
+ ASSIGNMENTS = 'assignments',
+ CONDITION = 'condition',
+ DIAGRAM_NODE_NAME = 'diagram-node',
+ EDIT_EVENT = 'editEvent',
+ EDIT_MESSAGE = 'editMessage',
+ END = 'end',
+ FORK = 'fork',
+ INITIAL = 'initial',
+ JOIN = 'join',
+ LINK = 'link',
+ NOTIFICATIONS = 'notifications',
+ PENCIL = 'pencil',
+ SHUFFLE = 'shuffle',
+ START = 'start',
+ STATE = 'state',
+ TASK = 'task';
+
+
+ // Liferay Workflow Nodes - MOVE THIS CODE!
+
+ A.DiagramNodeState = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ initial: {
+ value: false,
+ validation: isBoolean
+ },
+
+ notifications: {
+ // value: {
+ // name: 'string',
+ // description: 'string',
+ // template: 'string',
+ // 'template-type': 'string',
+ // 'notification-type': 'string',
+ // recipients: {
+ // address: 'string',
+ //
+ // role: {
+ // roleId: 1234,
+ // roleType: 'string',
+ // name: 'string',
+ // autoCreate: true
+ // },
+ //
+ // user: {
+ // 'user-id': 1234,
+ // 'screen-name': 'string',
+ // 'email-address': 'string'
+ // }
+ // }
+ // }
+ },
+
+ actions: {
+ // value: {
+ // name: 'string',
+ // description: 'string',
+ // script: 'string',
+ // 'script-language': 'string',
+ // priority: 0
+ // }
+ },
+
+ timers:{
+ },
+
+ strings: {
+ value: {
+ actions: 'Actions',
+ addAnchorMessage: 'Add Anchor',
+ assignments: 'Assignments',
+ closeMessage: 'Close',
+ deleteMessage: 'Are you sure you want to delete?',
+ description: 'Description',
+ editMessage: 'Edit',
+ initial: 'Initial',
+ name: 'Name',
+ notifications: 'Notifications',
+ type: 'Type'
+ }
+ },
+
+ type: {
+ value: STATE
+ }
+ },
+
+ EXTENDS: A.DiagramNode,
+
+ prototype: {
+ getConnectionNode: function() {
+ return new A.DiagramNodeTask({
+ xy: [100, 100] // TODO - find best position?
+ });
+ },
+
+ getPropertyModel: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+ var parentModel = A.DiagramNodeState.superclass.getPropertyModel.apply(this, arguments);
+
+ return AArray(parentModel).concat([
+ {
+ attributeName: ACTIONS,
+ editor: new A.TextAreaCellEditor(),
+ name: strings[ACTIONS]
+ },
+
+ {
+ attributeName: NOTIFICATIONS,
+ editor: new A.TextAreaCellEditor(),
+ name: strings[NOTIFICATIONS]
+ },
+
+ {
+ attributeName: INITIAL,
+ editor: false,
+ name: strings[INITIAL]
+ }
+ ]);
+ }
+ }
+ });
+
+ A.DiagramBuilder.types[STATE] = A.DiagramNodeState;
+
+ A.DiagramNodeCondition = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: CONDITION
+ }
+ },
+
+ EXTENDS: A.DiagramNodeState
+ });
+
+ A.DiagramBuilder.types[CONDITION] = A.DiagramNodeCondition;
+
+ A.DiagramNodeStart = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ initial: {
+ value: true
+ },
+
+ maxFields: {
+ value: 1
+ },
+
+ required: {
+ value: true
+ },
+
+ type: {
+ value: START
+ }
+ },
+
+ EXTENDS: A.DiagramNodeState,
+
+ prototype: {
+ getConnectionNode: function() {
+ return new A.DiagramNodeCondition({
+ xy: [100, 100] // TODO - find best position?
+ });
+ },
+
+ _valueControlsToolbar: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ return {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleEditEvent, instance),
+ icon: PENCIL,
+ id: EDIT_EVENT,
+ title: strings[EDIT_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK,
+ id: ADD_ANCHOR,
+ title: strings[ADD_ANCHOR_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddNodeEvent, instance),
+ icon: SHUFFLE,
+ id: ADD_NODE
+ }
+ ]
+ };
+ }
+ }
+ });
+
+ A.DiagramBuilder.types[START] = A.DiagramNodeStart;
+
+ A.DiagramNodeEnd = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ required: {
+ value: true
+ },
+
+ type: {
+ value: END
+ }
},
+
+ EXTENDS: A.DiagramNodeState,
+
+ prototype: {
+ _handleAddAnchorEvent: function(event) {
+ var instance = this;
+
+ instance.addField({
+ maxTargets: 0
+ });
+ },
+
+ _valueControlsToolbar: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ return {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleEditEvent, instance),
+ icon: PENCIL,
+ id: EDIT_EVENT,
+ title: strings[EDIT_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK,
+ id: ADD_ANCHOR,
+ title: strings[ADD_ANCHOR_MESSAGE]
+ }
+ ]
+ };
+ }
+ }
+ });
+
+ A.DiagramBuilder.types[END] = A.DiagramNodeEnd;
+
+ A.DiagramNodeJoin = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: JOIN
+ }
+ },
+
+ EXTENDS: A.DiagramNodeState
+ });
+
+ A.DiagramBuilder.types[JOIN] = A.DiagramNodeJoin;
+
+ A.DiagramNodeFork = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ type: {
+ value: FORK
+ }
+ },
+
+ EXTENDS: A.DiagramNodeState
+ });
+
+ A.DiagramBuilder.types[FORK] = A.DiagramNodeFork;
+
+ A.DiagramNodeTask = A.Component.create({
+ NAME: DIAGRAM_NODE_NAME,
+
+ ATTRS: {
+ assignments: {
+ value: {
+ // roles: [
+ // {
+ // role: {
+ // roleId: 1234,
+ // roleType: 'string',
+ // name: 'string',
+ // autoCreate: true
+ // }
+ // }
+ // ]
+
+ // 'resource-actions': {
+ // 'resource-action': 'string'
+ // }
+
+ // 'scripted-assignment': {
+ // script: 'string',
+ // 'script-language': 'string'
+ // }
+
+ // user: {
+ // 'user-id': 1234,
+ // 'screen-name': 'string',
+ // 'email-address': 'string'
+ // }
+ }
+ },
+
+ type: {
+ value: TASK
+ }
+ },
+
+ EXTENDS: A.DiagramNodeState,
+
+ prototype: {
+ getPropertyModel: function() {
+ var instance = this;
+ var strings = instance.getStrings();
+ var parentModel = A.DiagramNodeTask.superclass.getPropertyModel.apply(this, arguments);
+
+ return AArray(parentModel).concat([
+ {
+ attributeName: ASSIGNMENTS,
+ editor: new A.TextAreaCellEditor(),
+ name: strings[ASSIGNMENTS]
+ }
+ ]);
+ }
+ }
+ });
+
+ A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
+
+ // End of Liferay Workflow Nodes.
+
+
+
+
+
+
+// console.log(A.DiagramNode.buildNodeId('Task1'));
+
+ // A.on('available', function() { console.log('OKK'); }, '#' + A.DiagramNode.buildNodeId('Task1'));
+
+ var availableFields = [
{
type: 'task',
label: 'Task',
iconClass: 'task-icon'
+ },
+ {
+ type: 'join',
+ label: 'Join',
+ iconClass: 'join-icon'
+ },
+ {
+ type: 'fork',
+ label: 'Fork',
+ iconClass: 'fork-icon'
+ },
+
+ {
+ type: 'start',
+ label: 'Start',
+ iconClass: 'start-icon'
+ },
+ {
+ type: 'end',
+ label: 'End',
+ iconClass: 'end-icon'
+ },
+ {
+ type: 'condition',
+ label: 'Condition',
+ iconClass: 'condition-icon'
}
];
- node = new A.DiagramNode({
- bodyContent: 'Node',
- xy: [400, 200],
+ task0 = new A.DiagramNodeTask({
+ name: 'Task0',
+ xy: [900, 200],
fields: [
- {},
- {}
]
});
@@ -205,82 +596,119 @@ Alloy - diagram-builder-base Demo
// height: 600,
availableFields: availableFields,
fields: [
- node,
+ task0,
{
- bodyContent: 'Node1',
- xy: [200, 200],
+ name: 'StartNode',
+ type: 'start',
+ xy: [1200, 50],
fields: [
- {},
{}
]
},
{
- bodyContent: 'Node2',
+ name: 'EndNode',
+ type: 'end',
+ xy: [50, 50],
fields: [
- {},
- {}
]
},
{
+ name: 'Task1',
+ type: 'task',
xy: [600, 200],
- bodyContent: 'Node3',
- fields: [
- {},
- {},
- {},
- {}
- ]
+ // fields: [
+ // {
+ // }
+ // ]
+ },
+ {
+ name: 'Task2',
+ type: 'task',
+ xy: [300, 200],
+ // fields: [
+ // {},
+ // {}
+ // ]
}
]
}
).render();
- diagramBuilder2 = new A.DiagramBuilder(
+ // diagramBuilder1.connect('StartNode', 'Task1');
+
+ // diagramBuilder1.connect('StartNode', 'Task0');
+ // diagramBuilder1.connect('Task0', 'Task1');
+ // diagramBuilder1.connect('Task1', 'Task2');
+ // diagramBuilder1.connect('Task2', 'EndNode');
+
+ diagramBuilder1.connectAll([
{
- after: {
- cancel: function(event) {
- console.log('cancel', event);
- },
+ source: 'StartNode',
+ target: 'Task0'
+ },
- save: function(event) {
- console.log('save', event);
- },
+ {
+ source: task0,
+ target: 'Task1'
+ },
- addField: function(event) {
- // console.log('addField', event);
- }
- },
- availableFields: availableFields,
+ {
+ source: 'Task1',
+ target: 'Task2'
+ },
- fields: [
- {
- bodyContent: 'Node1',
- xy: [200, 200]
- },
- {
- bodyContent: 'Node2'
- }
- ],
- // fields: new A.ArrayList([{a:1}, {a:2}]),
-
- // propertyList: {
- // recordset: [
- // {
- // name: 'Text1',
- // value: 'Text value'
- // },
- // {
- // name: 'Text2',
- // value: 'Text value'
- // },
- // {
- // name: 'Text3',
- // value: 'Text value'
- // }
- // ]
- // }
+ {
+ source: 'Task2',
+ target: 'EndNode'
}
- ).render('#diagramBuilder2');
+ ]);
+
+ // diagramBuilder2 = new A.DiagramBuilder(
+ // {
+ // after: {
+ // cancel: function(event) {
+ // console.log('cancel', event);
+ // },
+ //
+ // save: function(event) {
+ // console.log('save', event);
+ // },
+ //
+ // addField: function(event) {
+ // // console.log('addField', event);
+ // }
+ // },
+ // availableFields: availableFields,
+ //
+ // fields: [
+ // {
+ // bodyContent: 'Node1',
+ // xy: [200, 200]
+ // },
+ // {
+ // bodyContent: 'Node2'
+ // }
+ // ],
+ // // fields: new A.ArrayList([{a:1}, {a:2}]),
+ //
+ // // propertyList: {
+ // // recordset: [
+ // // {
+ // // name: 'Text1',
+ // // value: 'Text value'
+ // // },
+ // // {
+ // // name: 'Text2',
+ // // value: 'Text value'
+ // // },
+ // // {
+ // // name: 'Text3',
+ // // value: 'Text value'
+ // // }
+ // // ]
+ // // }
+ // }
+ // ).render('#diagramBuilder2');
// diagramBuilder2.set('height', 1000);
// diagramBuilder2.set('fields', [1,2]);
diff --git a/src/aui-diagram-builder/js/aui-diagram-builder-base.js b/src/aui-diagram-builder/js/aui-diagram-builder-base.js
index d5ce666e095..3b8a780266c 100644
--- a/src/aui-diagram-builder/js/aui-diagram-builder-base.js
+++ b/src/aui-diagram-builder/js/aui-diagram-builder-base.js
@@ -19,6 +19,7 @@ var Lang = A.Lang,
AArray = A.Array,
+ MAX_FIELDS = 'maxFields',
ADD = 'add',
ADD_NODE = 'addNode',
AUTO = 'auto',
@@ -247,6 +248,11 @@ FieldSupport.ATTRS = {
validator: function(val) {
return isArray(val) || isArrayList(val);
}
+ },
+
+ maxFields: {
+ value: Infinity,
+ validator: isNumber
}
};
@@ -255,8 +261,10 @@ A.mix(FieldSupport.prototype, {
var instance = this;
var fields = [];
- AArray.each(val, function(field) {
- fields.push(instance.createField(field));
+ AArray.each(val, function(field, index) {
+ if (index < instance.get(MAX_FIELDS)) {
+ fields.push(instance.createField(field));
+ }
});
return new A.ArrayList(fields);
@@ -264,13 +272,20 @@ A.mix(FieldSupport.prototype, {
addField: function(field) {
var instance = this;
- var newField = instance.createField(field);
- instance._updateFields(
- instance.get(FIELDS).add(newField)
- );
+ if (instance.get(FIELDS).size() < instance.get(MAX_FIELDS)) {
+ var newField = instance.createField(field);
+
+ if (newField) {
+ instance._updateFields(
+ instance.get(FIELDS).add(newField)
+ );
+ }
+
+ return newField;
+ }
- return newField;
+ return null;
},
removeField: function(field) {
diff --git a/src/aui-diagram-builder/js/aui-diagram-builder-connector.js b/src/aui-diagram-builder/js/aui-diagram-builder-connector.js
index a21ab0e71b4..72bc3a574f4 100644
--- a/src/aui-diagram-builder/js/aui-diagram-builder-connector.js
+++ b/src/aui-diagram-builder/js/aui-diagram-builder-connector.js
@@ -15,6 +15,10 @@ var Lang = A.Lang,
return (val instanceof A.ArrayList);
},
+ isDiagramNode = function(val) {
+ return (val instanceof A.DiagramNode);
+ },
+
ANCHOR = 'anchor',
ARROW_POINTS = 'arrowPoints',
BODY = 'body',
@@ -28,6 +32,7 @@ var Lang = A.Lang,
HEIGHT = 'height',
ID = 'id',
LAZY_DRAW = 'lazyDraw',
+ MAX = 'max',
MAX_SOURCES = 'maxSources',
MAX_TARGETS = 'maxTargets',
NODE = 'node',
@@ -45,6 +50,8 @@ var Lang = A.Lang,
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_NODE_MAX_TARGETS = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, TARGETS),
+ CSS_DB_ANCHOR_NODE_MAX_SOURCES = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, SOURCES),
CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
@@ -251,24 +258,39 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
instance.connectTargets();
instance.after({
+ sourcesChange: instance._afterSourcesChange,
targetsChange: instance._afterTargetsChange
});
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
},
addSource: function(source) {
var instance = this;
- return instance.updateSources(
- instance.get(SOURCES).remove(source).add(source)
- );
+ if (instance.get(SOURCES).size() < instance.get(MAX_SOURCES)) {
+ instance.set(
+ SOURCES,
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ }
+
+ return instance;
},
addTarget: function(target) {
var instance = this;
- return instance.updateTargets(
- instance.get(TARGETS).remove(target).add(target)
- );
+ if (instance.get(TARGETS).size() < instance.get(MAX_TARGETS)) {
+ instance.set(
+ TARGETS,
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ }
+
+ return instance;
},
alignConnectors: function() {
@@ -307,7 +329,12 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
connect: function(target) {
var instance = this;
+ if (isDiagramNode(target)) {
+ target = target.findAvailableAnchor();
+ }
+
instance.addTarget(target);
+ target.addSource(instance);
if (!instance.isConnected(target)) {
var tConnector = target.get(CONNECTOR);
@@ -318,6 +345,10 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
instance.connectors[target.get(ID)] = new A.Connector(tConnector);
}
+ setTimeout(function() {
+ target.get(DIAGRAM_NODE).syncDropTargets();
+ }, 50);
+
return instance;
},
@@ -333,9 +364,12 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
var instance = this;
instance.getConnector(target).destroy();
-
instance.removeTarget(target);
target.removeSource(instance);
+
+ setTimeout(function() {
+ target.get(DIAGRAM_NODE).syncDropTargets();
+ }, 50);
},
disconnectTargets: function() {
@@ -370,48 +404,44 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return instance.connectors[target.get(ID)];
},
- isConnected: function(target) {
- var instance = this;
-
- return instance.connectors.hasOwnProperty(target.get(ID));
- },
-
- updateSources: function(sources) {
+ hasConnection: function() {
var instance = this;
- instance.set(SOURCES, sources);
-
- return instance;
+ return ((instance.get(TARGETS).size() > 0) || (instance.get(SOURCES).size() > 0));
},
- updateTargets: function(targets) {
+ isConnected: function(target) {
var instance = this;
- instance.set(TARGETS, targets);
-
- return instance;
+ return instance.connectors.hasOwnProperty(target.get(ID));
},
removeSource: function(source) {
var instance = this;
- return instance.updateSources(
+ instance.set(
+ SOURCES,
instance.get(SOURCES).remove(source)
);
+
+ return instance;
},
removeTarget: function(target) {
var instance = this;
- return instance.updateTargets(
+ instance.set(
+ TARGETS,
instance.get(TARGETS).remove(target)
);
+
+ return instance;
},
- _afterActiveChange: function(event) {
+ _afterSourcesChange: function(event) {
var instance = this;
- instance._uiSetActive(event.newVal);
+ instance._uiSetSources(event.newVal);
},
_afterTargetsChange: function(event) {
@@ -428,6 +458,8 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
event.newVal.each(function(anchor) {
anchor.addSource(instance);
});
+
+ instance._uiSetTargets(event.newVal);
},
_renderNode: function() {
@@ -461,7 +493,7 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
_setTargets: function(val) {
var instance = this;
- val = instance._setAnchors(val);
+ val = instance._setAnchors(val, true);
val.each(function(anchor) {
anchor.addSource(instance);
@@ -470,16 +502,15 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return val;
},
- _setAnchors: function(val) {
+ _setAnchors: function(val, target) {
var instance = this;
if (!isArrayList(val)) {
var targets = [];
- A.Array.each(val, function(target) {
- if (isString(target)) {
- // TODO - need this?
- target = A.Anchor.getAnchorByNode(target);
+ A.Array.some(val, function(target, index) {
+ if (index >= instance.get(target ? MAX_TARGETS : MAX_SOURCES)) {
+ return true;
}
targets.push( isAnchor(target) ? target : new A.Anchor(target) );
@@ -491,11 +522,61 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return val;
},
+ _setMaxSources: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxSources(
+ instance.get(MAX_SOURCES)
+ );
+
+ return val;
+ },
+
+ _setMaxTargets: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
+
+ return val;
+ },
+
_setNode: function(val) {
var instance = this;
var id = instance.get(ID);
return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ },
+
+ _uiSetSources: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxSources(
+ instance.get(MAX_SOURCES)
+ );
+ },
+
+ _uiSetMaxSources: function(val) {
+ var instance = this;
+ var node = instance.get(NODE);
+
+ node.toggleClass(CSS_DB_ANCHOR_NODE_MAX_SOURCES, (instance.get(SOURCES).size() === val));
+ },
+
+ _uiSetMaxTargets: function(val) {
+ var instance = this;
+ var node = instance.get(NODE);
+
+ node.toggleClass(CSS_DB_ANCHOR_NODE_MAX_TARGETS, (instance.get(TARGETS).size() === val));
+ },
+
+ _uiSetTargets: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
}
},{
ATTRS: {
@@ -516,12 +597,14 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
},
maxSources: {
- value: Infinity,
+ setter: '_setMaxSources',
+ value: 1,
validator: isNumber
},
maxTargets: {
- value: Infinity,
+ setter: '_setMaxTargets',
+ value: 1,
validator: isNumber
},
@@ -557,6 +640,6 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
},
getAnchorByNode: function(node) {
- return A.one(node).getData(DATA_ANCHOR);
+ return isAnchor(node) ? node : A.one(node).getData(DATA_ANCHOR);
}
});
\ No newline at end of file
diff --git a/src/aui-diagram-builder/js/aui-diagram-builder-impl.js b/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
index 0a02c774f34..81f49f99aba 100644
--- a/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
+++ b/src/aui-diagram-builder/js/aui-diagram-builder-impl.js
@@ -27,14 +27,20 @@ var Lang = A.Lang,
});
},
+ ADD_ANCHOR = 'addAnchor',
+ ADD_ANCHOR_MESSAGE = 'addAnchorMessage',
+ ADD_NODE = 'addNode',
ANCHOR = 'anchor',
ANCHORS = 'anchors',
ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
+ BOOLEAN = 'boolean',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
CANCEL = 'cancel',
CLICK = 'click',
+ CLOSE_EVENT = 'closeEvent',
+ CLOSE_MESSAGE = 'closeMessage',
CONTENT = 'content',
CONTROLS = 'controls',
CONTROLS_TOOLBAR = 'controlsToolbar',
@@ -49,13 +55,18 @@ var Lang = A.Lang,
DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ EDIT_EVENT = 'editEvent',
+ EDIT_MESSAGE = 'editMessage',
ESC = 'esc',
FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
HOVER = 'hover',
+ ID = 'id',
KEYDOWN = 'keydown',
LINK = 'link',
+ MAX = 'max',
+ MAX_SOURCES = 'maxSources',
MOUSEENTER = 'mouseenter',
MOUSELEAVE = 'mouseleave',
NAME = 'name',
@@ -63,26 +74,35 @@ var Lang = A.Lang,
P1 = 'p1',
P2 = 'p2',
PARENT_NODE = 'parentNode',
+ PENCIL = 'pencil',
RECORDS = 'records',
RECORDSET = 'recordset',
REGION = 'region',
RENDERED = 'rendered',
+ REQUIRED = 'required',
SELECTED = 'selected',
SHUFFLE = 'shuffle',
- TASK = 'task',
+ SOURCE = 'source',
+ SOURCES = 'sources',
+ STRING = 'string',
+ TARGET = 'target',
+ TARGETS = 'targets',
TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
VIEWPORT = 'viewport',
WRAPPER = 'wrapper',
XY = 'xy',
+ _DASH = '-',
_DOT = '.',
- _DOLLAR = '$',
_EMPTY_STR = '',
- _DASH = '-',
+ _HASH = '#',
+ _UNDERLINE = '_',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_NODE_MAX_TARGETS = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, TARGETS),
+ // CSS_DB_ANCHOR_NODE_MAX_SOURCES = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, SOURCES),
CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
@@ -184,6 +204,41 @@ var DiagramBuilder = A.Component.create({
instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
+ connect: function(diagramNode1, diagramNode2) {
+ var instance = this;
+
+ if (isString(diagramNode1)) {
+ diagramNode1 = A.Widget.getByNode(_HASH+A.DiagramNode.buildNodeId(diagramNode1));
+ }
+
+ if (isString(diagramNode2)) {
+ diagramNode2 = A.Widget.getByNode(_HASH+A.DiagramNode.buildNodeId(diagramNode2));
+ }
+
+ if (diagramNode1 && diagramNode2) {
+ var a1 = diagramNode1.findAvailableAnchor();
+ var a2 = diagramNode2.findAvailableAnchor();
+
+ if (a1 && a2) {
+ a1.connect(a2);
+ }
+ }
+
+ return instance;
+ },
+
+ connectAll: function(nodes) {
+ var instance = this;
+
+ AArray.each(nodes, function(node) {
+ if (node.hasOwnProperty(SOURCE) && node.hasOwnProperty(TARGET)) {
+ instance.connect(node.source, node.target);
+ }
+ });
+
+ return instance;
+ },
+
createField: function(val) {
var instance = this;
@@ -243,7 +298,7 @@ var DiagramBuilder = A.Component.create({
var instance = this;
instance.unselectAll();
- instance.stopEditingNode();
+ // instance.stopEditingNode();
instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
@@ -342,8 +397,11 @@ var DiagramBuilder = A.Component.create({
_onDeleteKey: function(event) {
var instance = this;
+ var selectedNode = instance.selectedNode;
- instance.selectedNode.close();
+ if (!selectedNode.get(REQUIRED)) {
+ selectedNode.close();
+ }
},
_onEscKey: function(event) {
@@ -474,7 +532,7 @@ var DiagramNodeOverlay = A.Component.create({
var DiagramNode = A.Component.create({
NAME: DIAGRAM_NODE_NAME,
- UI_ATTRS: [FIELDS, NAME, SELECTED],
+ UI_ATTRS: [FIELDS, NAME, REQUIRED, SELECTED],
ATTRS: {
anchorsDragConfig: {
@@ -488,6 +546,11 @@ var DiagramNode = A.Component.create({
validator: isDiagramBuilder
},
+ required: {
+ value: false,
+ validator: isBoolean
+ },
+
description: {
value: _EMPTY_STR,
validator: isString
@@ -513,8 +576,11 @@ var DiagramNode = A.Component.create({
strings: {
value: {
+ addAnchorMessage: 'Add Anchor',
+ closeMessage: 'Close',
deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
+ editMessage: 'Edit',
name: 'Name',
type: 'Type'
}
@@ -526,9 +592,8 @@ var DiagramNode = A.Component.create({
},
controlsToolbar: {
- setter: '_setControlsToolbar',
validator: isObject,
- value: null
+ valueFn: '_valueControlsToolbar'
},
width: {
@@ -547,7 +612,7 @@ var DiagramNode = A.Component.create({
EXTENDS: DiagramNodeOverlay,
buildNodeId: function(id) {
- return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ return DIAGRAM_NODE + _UNDERLINE + FIELD + _UNDERLINE + id;
},
prototype: {
@@ -633,6 +698,33 @@ var DiagramNode = A.Component.create({
return val;
},
+ findAvailableAnchor: function() {
+ var instance = this;
+ var available = null;
+
+ instance.get(FIELDS).some(function(anchor) {
+ if (!anchor.hasConnection()) {
+ available = anchor;
+
+ return true;
+ }
+ });
+
+ if (!available) {
+ available = instance.addField({});
+ }
+
+ return available;
+ },
+
+ getConnectionNode: function() {
+ var instance = this;
+
+ return new A.DiagramNode({
+ xy: [100, 100] // TODO - find best position?
+ });
+ },
+
getLeftTop: function() {
var instance = this;
@@ -644,7 +736,13 @@ var DiagramNode = A.Component.create({
var propertyModel = instance.getPropertyModel();
AArray.each(propertyModel, function(property) {
- property.value = instance.get(property.attributeName);
+ var value = instance.get(property.attributeName), type = Lang.type(value);
+
+ if (type === BOOLEAN || type === STRING) {
+ value = String(value);
+ }
+
+ property.value = value;
});
return propertyModel;
@@ -681,6 +779,29 @@ var DiagramNode = A.Component.create({
];
},
+ syncDragTargets: function() {
+ var instance = this;
+
+ instance.anchorsDrag.syncTargets();
+ },
+
+ syncDropTargets: function(anchor) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ var drop = A.DD.DDM.getDrop(anchor.get(NODE));
+
+ if (drop) {
+ if (anchor.get(SOURCES).size() === anchor.get(MAX_SOURCES)) {
+ drop.removeFromGroup(ANCHORS);
+ }
+ else {
+ drop.addToGroup(ANCHORS);
+ }
+ }
+ });
+ },
+
_afterRender: function(event) {
var instance = this;
@@ -705,30 +826,33 @@ var DiagramNode = A.Component.create({
var instance = this;
instance.addField({});
-
- // event.halt();
},
- _handleAddTaskEvent: function(event) {
+ _handleAddNodeEvent: function(event) {
var instance = this;
var builder = instance.get(BUILDER);
+ var source = instance.findAvailableAnchor();
- var diagramNode = new A.DiagramNode({
- type: NODE,
- xy: [100, 100] // TODO - find best position?
- });
+ if (source) {
+ var diagramNode = instance.getConnectionNode();
- builder.addField(diagramNode);
+ builder.addField(diagramNode);
+ source.connect(diagramNode.addField({}));
+ }
+ },
- var source = instance.addField({});
- var target = diagramNode.addField({});
- source.connect(target);
+ _handleEditEvent: function(event) {
+ var instance = this;
+
+ instance.get(BUILDER).startEditingNode(instance);
},
_handleCloseEvent: function(event) {
var instance = this;
- instance.close();
+ if (!instance.get(REQUIRED)) {
+ instance.close();
+ }
},
_onAnchorDrag: function(event) {
@@ -784,6 +908,10 @@ var DiagramNode = A.Component.create({
instance.get(CONTROLS_TOOLBAR)
)
.render(instance.controlsNode);
+
+ instance._uiSetRequired(
+ instance.get(REQUIRED)
+ );
},
_setBuilder: function(val) {
@@ -840,31 +968,10 @@ var DiagramNode = A.Component.create({
instance.anchorsDrag = new A.DD.Delegate(
instance.get(ANCHORS_DRAG_CONFIG)
);
- },
-
- _setControlsToolbar: function(val) {
- var instance = this;
- return A.merge(
- {
- activeState: false,
- children: [
- {
- handler: A.bind(instance._handleAddAnchorEvent, instance),
- icon: LINK
- },
- {
- handler: A.bind(instance._handleAddTaskEvent, instance),
- icon: SHUFFLE
- },
- {
- handler: A.bind(instance._handleCloseEvent, instance),
- icon: CANCEL
- }
- ]
- },
- val
- );
+ instance.anchorsDrag.dd
+ .addInvalid(_DOT+CSS_DB_ANCHOR_NODE_MAX_TARGETS);
+ // .addInvalid(_DOT+CSS_DB_ANCHOR_NODE_MAX_SOURCES);
},
_uiSetFields: function(val) {
@@ -873,9 +980,10 @@ var DiagramNode = A.Component.create({
if (instance.get(RENDERED)) {
instance.alignAnchors();
- setTimeout(function() {
- instance.anchorsDrag.syncTargets();
- }, 50);
+ // setTimeout(function() {
+ instance.syncDragTargets();
+ instance.syncDropTargets();
+ // }, 50);
}
},
@@ -883,7 +991,27 @@ var DiagramNode = A.Component.create({
var instance = this;
var boundingBox = instance.get(BOUNDING_BOX);
- boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ boundingBox.set(ID, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetRequired: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+ var controlsToolbar = instance.controlsToolbar;
+
+ if (controlsToolbar) {
+ if (val) {
+ controlsToolbar.remove(CLOSE_EVENT);
+ }
+ else {
+ controlsToolbar.add({
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL,
+ id: CLOSE_EVENT,
+ title: strings[CLOSE_MESSAGE]
+ });
+ }
+ }
},
_uiSetSelected: function(val) {
@@ -905,7 +1033,41 @@ var DiagramNode = A.Component.create({
var containerXY = instance._getContainer().getXY();
this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
- }
+ },
+
+ _valueControlsToolbar: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ return {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleEditEvent, instance),
+ icon: PENCIL,
+ id: EDIT_EVENT,
+ title: strings[EDIT_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK,
+ id: ADD_ANCHOR,
+ title: strings[ADD_ANCHOR_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddNodeEvent, instance),
+ icon: SHUFFLE,
+ id: ADD_NODE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL,
+ id: CLOSE_EVENT,
+ title: strings[CLOSE_MESSAGE]
+ }
+ ]
+ };
+ }
}
});
@@ -913,20 +1075,6 @@ A.DiagramNode = DiagramNode;
A.DiagramBuilder.types[NODE] = A.DiagramNode;
-A.DiagramNodeTask = A.Component.create({
- NAME: DIAGRAM_NODE_NAME,
-
- ATTRS: {
- type: {
- value: TASK
- }
- },
-
- EXTENDS: A.DiagramNode
-});
-
-A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
-
// TODO deletar anchors OK
// TODO deletar connections (delete) OK
// TODO Adicionar overlay de controles OK
From 0a4a0b793c6b836e4415d97ba72d796aadcdacc9 Mon Sep 17 00:00:00 2001
From: Eduardo Lundgren ',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(X);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aG){var A=this;var aF=A.availableFieldsDrag;return(aG===aF.dd);},plotFields:function(){var aF=this;var A=aF.get(t);A.each(function(aG){aF.plotField(aG);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(Q));},syncUI:function(){var A=this;var aF=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aF.addClass(ae);},_afterActiveTabChange:function(aG){var A=this;var aF=aG.newVal.get(P);if(A.get(ax)&&(aF===A.settingsNode)){A._renderSettings();}},_afterRender:function(aF){var A=this;A.plotFields();},_afterUiSetHeight:function(aF){var A=this;A.dropContainer.setStyle(an,M(aF)?aF+A.DEF_UNIT:aF);},_defCancelFn:function(aF){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(Z);},_handleSaveEvent:function(){var A=this;A.fire(al);},_renderViewport:function(){var aF=this;var aG=aF.get(u);var A=aF.viewport;A.appendChild(aF.dropContainer);aG.appendChild(A);},_renderPropertyList:function(){var A=this;
-if(!A.propertyList){A.propertyList=new ad.PropertyList(A.get(ac)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aF=new ad.TabView(A.get(G));A.tabView=aF;A.fieldsNode=aF.getTab(0).get(P);A.settingsNode=aF.getTab(1).get(P);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ad.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ad.DD.Drop(A.get(ak));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ad.DD.Delegate(A.get(az));},_setAvailableFields:function(aG){var aF=this;var A=[];aD.each(aG,function(aI,aH){A.push(D(aI)?aI:new ad.AvailableField(aI));});return A;},_setDropConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,groups:[Q],node:A.dropContainer},aF||{});},_setAvailableFieldsDragConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[Q],plugins:[{cfg:{moveOnEnd:false},fn:ad.Plugin.DDProxy}]},nodes:g+ag},aF||{});},_setPropertyList:function(aF){var A=this;return ad.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aC}},aF);},_setTabView:function(aI){var aF=this;var aH=aF.get(s);var aJ=aH.one(g+aA);var aG={after:{activeTabChange:ad.bind(aF._afterActiveTabChange,aF)},boundingBox:aH.one(g+m),contentBox:aH.one(g+Y),bubbleTargets:aF,contentNode:aH.one(g+av),cssClass:m,listNode:aJ,render:aF.get(u)};if(!aJ){var A=aF.getStrings();aG.items=[{cssClass:ah,label:A[k]},{cssClass:K,label:A[y]}];}return ad.merge(aG,aI);},_setToolbar:function(aG){var aF=this;var A=aF.getStrings();return ad.merge({activeState:false,bubbleTargets:aF,children:[{handler:ad.bind(aF._handleSaveEvent,aF),label:A[al],icon:aa},{handler:ad.bind(aF._handleCancelEvent,aF),label:A[Z]}]},aG);},_uiSetAvailableFields:function(aH){var A=this;var aG=A.fieldsNode;if(aG){var aF=ad.getDoc().invoke(E);aD.each(aH,function(aI){aF.appendChild(aI.get(r));});aG.setContent(A.fieldsContainer.setContent(aF));}},_uiSetFields:function(aF){var A=this;if(A.get(ax)){A.plotFields();}}}});ad.DiagramBuilderBase=ar;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-base",function(ae){var U=ae.Lang,d=U.isArray,aq=U.isBoolean,M=U.isNumber,B=U.isObject,au=U.isString,I=function(A){return(A instanceof ae.ArrayList);},S=function(A){return(A instanceof ae.Node);},D=function(A){return(A instanceof ae.AvailableField);},aE=ae.Array,P="maxFields",W="add",k="addNode",aD="auto",N="availableField",R="availableFields",aA="availableFieldsDragConfig",an="base",s="boundingBox",ax="builder",aa="cancel",ar="clearfix",a="container",ac="content",u="contentBox",J="viewport",Q="contentNode",E="createDocumentFragment",z="diagram",F="diagram-builder-base",ab="disk",o="draggable",az="drop",al="dropConfig",Z="dropContainer",ap="field",t="fields",n="fieldsContainer",ao="height",p="helper",X="icon",v="iconClass",ak="id",ag="label",aj="list",r="node",y="nodeSettings",ad="propertyList",ay="rendered",am="save",q="settings",O="tab",b="tabs",e="tabview",G="tabView",L="toolbar",j="toolbarContainer",w=ae.getClassName,aC=" ",g=".",H="$",h="#",aF=w(z,ax,an,az,a),x=w(z,ax,an,J),C=w(z,ax,an,ap),f=w(z,ax,an,t,a),ah=w(z,ax,an,ap,o),c=w(z,ax,an,ap,X),V=w(z,ax,an,ap,ag),m=w(z,ax,an,b,a),Y=w(z,ax,an,b,a,ac),ai=w(z,ax,an,O,W),K=w(z,ax,an,O,q),av=w(z,ax,an,L,a),af=w(p,ar),l=w(X),aw=w(e,ac),aB=w(e,aj);var i=ae.Component.create({NAME:N,ATTRS:{draggable:{value:true,validator:aq},label:{validator:au},iconClass:{validator:au},id:{value:ae.guid(),setter:"_setId",validator:au},node:{valueFn:function(aG){var A=this;if(!S(aG)){aG=ae.Node.create(ae.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aG.setData(N,A);}return aG;},validator:S,writeOnce:true},type:{value:r,validator:au}},EXTENDS:ae.Base,buildNodeId:function(A){return R+H+ap+H+A;},getAvailableFieldByNode:function(A){return ae.one(A).getData(N);},getAvailableFieldById:function(A){return ae.AvailableField.getAvailableFieldByNode(h+ae.AvailableField.buildNodeId(A));},prototype:{FIELD_ITEM_TEMPLATE:'
',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(Z);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aH){var A=this;var aG=A.availableFieldsDrag;return(aH===aG.dd);},plotFields:function(){var aG=this;var A=aG.get(t);A.each(function(aH){aG.plotField(aH);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(R));},syncUI:function(){var A=this;var aG=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aG.addClass(af);},_afterActiveTabChange:function(aH){var A=this;var aG=aH.newVal.get(Q);if(A.get(ay)&&(aG===A.settingsNode)){A._renderSettings();}},_afterRender:function(aG){var A=this;A.plotFields();},_afterUiSetHeight:function(aG){var A=this;A.dropContainer.setStyle(ao,M(aG)?aG+A.DEF_UNIT:aG);},_defCancelFn:function(aG){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(aa);},_handleSaveEvent:function(){var A=this;A.fire(am);},_renderViewport:function(){var aG=this;
+var aH=aG.get(u);var A=aG.viewport;A.appendChild(aG.dropContainer);aH.appendChild(A);},_renderPropertyList:function(){var A=this;if(!A.propertyList){A.propertyList=new ae.PropertyList(A.get(ad)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aG=new ae.TabView(A.get(G));A.tabView=aG;A.fieldsNode=aG.getTab(0).get(Q);A.settingsNode=aG.getTab(1).get(Q);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ae.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ae.DD.Drop(A.get(al));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ae.DD.Delegate(A.get(aA));},_setAvailableFields:function(aH){var aG=this;var A=[];aE.each(aH,function(aJ,aI){A.push(D(aJ)?aJ:new ae.AvailableField(aJ));});return A;},_setDropConfig:function(aG){var A=this;return ae.merge({bubbleTargets:A,groups:[R],node:A.dropContainer},aG||{});},_setAvailableFieldsDragConfig:function(aG){var A=this;return ae.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[R],plugins:[{cfg:{moveOnEnd:false},fn:ae.Plugin.DDProxy}]},nodes:g+ah},aG||{});},_setPropertyList:function(aG){var A=this;return ae.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aD}},aG);},_setTabView:function(aJ){var aG=this;var aI=aG.get(s);var aK=aI.one(g+aB);var aH={after:{activeTabChange:ae.bind(aG._afterActiveTabChange,aG)},boundingBox:aI.one(g+m),contentBox:aI.one(g+Y),bubbleTargets:aG,contentNode:aI.one(g+aw),cssClass:m,listNode:aK,render:aG.get(u)};if(!aK){var A=aG.getStrings();aH.items=[{cssClass:ai,label:A[k]},{cssClass:K,label:A[y]}];}return ae.merge(aH,aJ);},_setToolbar:function(aH){var aG=this;var A=aG.getStrings();return ae.merge({activeState:false,bubbleTargets:aG,children:[{handler:ae.bind(aG._handleSaveEvent,aG),label:A[am],icon:ab},{handler:ae.bind(aG._handleCancelEvent,aG),label:A[aa]}]},aH);},_uiSetAvailableFields:function(aI){var A=this;var aH=A.fieldsNode;if(aH){var aG=ae.getDoc().invoke(E);aE.each(aI,function(aJ){aG.appendChild(aJ.get(r));});aH.setContent(A.fieldsContainer.setContent(aG));}},_uiSetFields:function(aG){var A=this;if(A.get(ay)){A.plotFields();}}}});ae.DiagramBuilderBase=at;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder-base.js b/build/aui-diagram-builder/aui-diagram-builder-base.js
index be9cb6f4650..2cb7217820e 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-base.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-base.js
@@ -20,6 +20,7 @@ var Lang = A.Lang,
AArray = A.Array,
+ MAX_FIELDS = 'maxFields',
ADD = 'add',
ADD_NODE = 'addNode',
AUTO = 'auto',
@@ -248,6 +249,11 @@ FieldSupport.ATTRS = {
validator: function(val) {
return isArray(val) || isArrayList(val);
}
+ },
+
+ maxFields: {
+ value: Infinity,
+ validator: isNumber
}
};
@@ -256,8 +262,10 @@ A.mix(FieldSupport.prototype, {
var instance = this;
var fields = [];
- AArray.each(val, function(field) {
- fields.push(instance.createField(field));
+ AArray.each(val, function(field, index) {
+ if (index < instance.get(MAX_FIELDS)) {
+ fields.push(instance.createField(field));
+ }
});
return new A.ArrayList(fields);
@@ -265,13 +273,20 @@ A.mix(FieldSupport.prototype, {
addField: function(field) {
var instance = this;
- var newField = instance.createField(field);
- instance._updateFields(
- instance.get(FIELDS).add(newField)
- );
+ if (instance.get(FIELDS).size() < instance.get(MAX_FIELDS)) {
+ var newField = instance.createField(field);
+
+ if (newField) {
+ instance._updateFields(
+ instance.get(FIELDS).add(newField)
+ );
+ }
+
+ return newField;
+ }
- return newField;
+ return null;
},
removeField: function(field) {
diff --git a/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js b/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js
index e48234c19d4..7072ce4b6af 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-connector-debug.js
@@ -16,6 +16,10 @@ var Lang = A.Lang,
return (val instanceof A.ArrayList);
},
+ isDiagramNode = function(val) {
+ return (val instanceof A.DiagramNode);
+ },
+
ANCHOR = 'anchor',
ARROW_POINTS = 'arrowPoints',
BODY = 'body',
@@ -29,6 +33,7 @@ var Lang = A.Lang,
HEIGHT = 'height',
ID = 'id',
LAZY_DRAW = 'lazyDraw',
+ MAX = 'max',
MAX_SOURCES = 'maxSources',
MAX_TARGETS = 'maxTargets',
NODE = 'node',
@@ -46,6 +51,8 @@ var Lang = A.Lang,
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_NODE_MAX_TARGETS = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, TARGETS),
+ CSS_DB_ANCHOR_NODE_MAX_SOURCES = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, SOURCES),
CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE);
@@ -252,24 +259,39 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
instance.connectTargets();
instance.after({
+ sourcesChange: instance._afterSourcesChange,
targetsChange: instance._afterTargetsChange
});
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
},
addSource: function(source) {
var instance = this;
- return instance.updateSources(
- instance.get(SOURCES).remove(source).add(source)
- );
+ if (instance.get(SOURCES).size() < instance.get(MAX_SOURCES)) {
+ instance.set(
+ SOURCES,
+ instance.get(SOURCES).remove(source).add(source)
+ );
+ }
+
+ return instance;
},
addTarget: function(target) {
var instance = this;
- return instance.updateTargets(
- instance.get(TARGETS).remove(target).add(target)
- );
+ if (instance.get(TARGETS).size() < instance.get(MAX_TARGETS)) {
+ instance.set(
+ TARGETS,
+ instance.get(TARGETS).remove(target).add(target)
+ );
+ }
+
+ return instance;
},
alignConnectors: function() {
@@ -308,7 +330,12 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
connect: function(target) {
var instance = this;
+ if (isDiagramNode(target)) {
+ target = target.findAvailableAnchor();
+ }
+
instance.addTarget(target);
+ target.addSource(instance);
if (!instance.isConnected(target)) {
var tConnector = target.get(CONNECTOR);
@@ -319,6 +346,10 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
instance.connectors[target.get(ID)] = new A.Connector(tConnector);
}
+ setTimeout(function() {
+ target.get(DIAGRAM_NODE).syncDropTargets();
+ }, 50);
+
return instance;
},
@@ -334,9 +365,12 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
var instance = this;
instance.getConnector(target).destroy();
-
instance.removeTarget(target);
target.removeSource(instance);
+
+ setTimeout(function() {
+ target.get(DIAGRAM_NODE).syncDropTargets();
+ }, 50);
},
disconnectTargets: function() {
@@ -371,48 +405,44 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return instance.connectors[target.get(ID)];
},
- isConnected: function(target) {
- var instance = this;
-
- return instance.connectors.hasOwnProperty(target.get(ID));
- },
-
- updateSources: function(sources) {
+ hasConnection: function() {
var instance = this;
- instance.set(SOURCES, sources);
-
- return instance;
+ return ((instance.get(TARGETS).size() > 0) || (instance.get(SOURCES).size() > 0));
},
- updateTargets: function(targets) {
+ isConnected: function(target) {
var instance = this;
- instance.set(TARGETS, targets);
-
- return instance;
+ return instance.connectors.hasOwnProperty(target.get(ID));
},
removeSource: function(source) {
var instance = this;
- return instance.updateSources(
+ instance.set(
+ SOURCES,
instance.get(SOURCES).remove(source)
);
+
+ return instance;
},
removeTarget: function(target) {
var instance = this;
- return instance.updateTargets(
+ instance.set(
+ TARGETS,
instance.get(TARGETS).remove(target)
);
+
+ return instance;
},
- _afterActiveChange: function(event) {
+ _afterSourcesChange: function(event) {
var instance = this;
- instance._uiSetActive(event.newVal);
+ instance._uiSetSources(event.newVal);
},
_afterTargetsChange: function(event) {
@@ -429,6 +459,8 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
event.newVal.each(function(anchor) {
anchor.addSource(instance);
});
+
+ instance._uiSetTargets(event.newVal);
},
_renderNode: function() {
@@ -462,7 +494,7 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
_setTargets: function(val) {
var instance = this;
- val = instance._setAnchors(val);
+ val = instance._setAnchors(val, true);
val.each(function(anchor) {
anchor.addSource(instance);
@@ -471,16 +503,15 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return val;
},
- _setAnchors: function(val) {
+ _setAnchors: function(val, target) {
var instance = this;
if (!isArrayList(val)) {
var targets = [];
- A.Array.each(val, function(target) {
- if (isString(target)) {
- // TODO - need this?
- target = A.Anchor.getAnchorByNode(target);
+ A.Array.some(val, function(target, index) {
+ if (index >= instance.get(target ? MAX_TARGETS : MAX_SOURCES)) {
+ return true;
}
targets.push( isAnchor(target) ? target : new A.Anchor(target) );
@@ -492,11 +523,61 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
return val;
},
+ _setMaxSources: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxSources(
+ instance.get(MAX_SOURCES)
+ );
+
+ return val;
+ },
+
+ _setMaxTargets: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
+
+ return val;
+ },
+
_setNode: function(val) {
var instance = this;
var id = instance.get(ID);
return A.one(val).set(ID, id).setData(DATA_ANCHOR, instance);
+ },
+
+ _uiSetSources: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxSources(
+ instance.get(MAX_SOURCES)
+ );
+ },
+
+ _uiSetMaxSources: function(val) {
+ var instance = this;
+ var node = instance.get(NODE);
+
+ node.toggleClass(CSS_DB_ANCHOR_NODE_MAX_SOURCES, (instance.get(SOURCES).size() === val));
+ },
+
+ _uiSetMaxTargets: function(val) {
+ var instance = this;
+ var node = instance.get(NODE);
+
+ node.toggleClass(CSS_DB_ANCHOR_NODE_MAX_TARGETS, (instance.get(TARGETS).size() === val));
+ },
+
+ _uiSetTargets: function(val) {
+ var instance = this;
+
+ instance._uiSetMaxTargets(
+ instance.get(MAX_TARGETS)
+ );
}
},{
ATTRS: {
@@ -517,12 +598,14 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
},
maxSources: {
- value: Infinity,
+ setter: '_setMaxSources',
+ value: 1,
validator: isNumber
},
maxTargets: {
- value: Infinity,
+ setter: '_setMaxTargets',
+ value: 1,
validator: isNumber
},
@@ -558,7 +641,7 @@ A.Anchor = A.Base.create('anchor', A.Base, [], {
},
getAnchorByNode: function(node) {
- return A.one(node).getData(DATA_ANCHOR);
+ return isAnchor(node) ? node : A.one(node).getData(DATA_ANCHOR);
}
});
diff --git a/build/aui-diagram-builder/aui-diagram-builder-connector-min.js b/build/aui-diagram-builder/aui-diagram-builder-connector-min.js
index 35a0ebce02c..1a5dd4af85b 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-connector-min.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-connector-min.js
@@ -1 +1,2 @@
-AUI.add("aui-diagram-builder-connector",function(k){var M=k.Lang,r=M.isArray,v=M.isBoolean,L=M.isNumber,z=M.isObject,h=M.isString,D=k.Array,b=function(A){return(A instanceof k.Anchor);},E=function(A){return(A instanceof k.ArrayList);},y="anchor",F="arrowPoints",C="body",G="boundingBox",N="builder",w="color",n="connector",a="dataAnchor",x="diagram",s="diagramNode",u="height",p="id",I="lazyDraw",j="maxSources",i="maxTargets",J="node",m="p1",l="p2",e="path",q="shape",g="sources",f="targets",B="viewport",c="width",K="wrapper",o=".",t=k.getClassName,d=t(x,N,y,J,K),H=t(x,N,y,J);k.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(T,O,V,A,U,R){var W=this;T.moveTo(O,V);T.lineTo(A,U);var P=Math.atan2(U-V,A-O),S=(A+O)/2,Q=(U+V)/2;W.drawPolygon(T,W.translatePoints(W.rotatePoints(R||W.ARROW_POINTS,P),S,Q));},drawPolygon:function(O,P){var A=this;O.moveTo(P[0][0],P[0][1]);D.each(P,function(R,Q){if(Q>0){O.lineTo(P[Q][0],P[Q][1]);}});O.lineTo(P[0][0],P[0][1]);O.end();},translatePoints:function(P,O,R){var A=this;var Q=[];D.each(P,function(T,S){Q.push([P[S][0]+O,P[S][1]+R]);});return Q;},rotatePoints:function(O,Q){var A=this;var P=[];D.each(O,function(S,R){P.push(A.rotatePoint(Q,O[R][0],O[R][1]));});return P;},rotatePoint:function(O,A,P){return[(A*Math.cos(O))-(P*Math.sin(O)),(A*Math.sin(O))+(P*Math.cos(O))];}};k.Connector=k.Base.create("line",k.Base,[],{graphics:null,shape:null,initializer:function(O){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(I)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var O=A.shape;var Q=A.getCoordinate(A.get(m));var P=A.getCoordinate(A.get(l));O.clear();k.PolygonUtil.drawLineArrow(O,Q[0],Q[1],P[0],P[1],A.get(F));},getCoordinate:function(P){var A=this;var O=A.get(B).getXY();return[P[0]-O[0],P[1]-O[1]];},_initGraphics:function(){var A=this;var O=new k.Graphic({width:A.get(c),height:A.get(u),render:A.get(B)});A.graphics=O;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(q));},_setShape:function(O){var A=this;return k.merge({type:e,stroke:{color:A.get(w),weight:2},fill:{color:A.get(w)}},O);}},{ATTRS:{color:{value:"#666",validator:h},lazyDraw:{value:false,validator:v},viewport:{setter:k.one,value:C},shape:{value:null,setter:"_setShape"},arrowPoints:{value:k.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:r},p2:{value:[0,0],validator:r}}});k.Anchor=k.Base.create("anchor",k.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({targetsChange:A._afterTargetsChange});},addSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O).add(O));},addTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O).add(O));},alignConnectors:function(){var A=this;A.get(f).each(function(O){var P=A.getConnector(O);if(P){P.set(m,A.getCenterXY());P.set(l,O.getCenterXY());}});A.get(g).each(function(O){var P=O.getConnector(A);if(P){P.set(m,O.getCenterXY());P.set(l,A.getCenterXY());}});return A;},destroy:function(){var A=this;A.disconnectTargets();A.disconnectSources();A.get(J).remove();},connect:function(O){var A=this;A.addTarget(O);if(!A.isConnected(O)){var P=O.get(n);P.p1=A.getCenterXY();P.p2=O.getCenterXY();A.connectors[O.get(p)]=new k.Connector(P);}return A;},connectTargets:function(){var A=this;A.get(f).each(k.bind(A.connect,A));return A;},disconnect:function(O){var A=this;A.getConnector(O).destroy();A.removeTarget(O);O.removeSource(A);},disconnectTargets:function(){var A=this;A.get(f).each(function(O){A.disconnect(O);});return A;},disconnectSources:function(){var A=this;A.get(g).each(function(O){O.disconnect(A);});return A;},getCenterXY:function(){var A=this;return A.get(J).getCenterXY();},getConnector:function(O){var A=this;return A.connectors[O.get(p)];},isConnected:function(O){var A=this;return A.connectors.hasOwnProperty(O.get(p));},updateSources:function(O){var A=this;A.set(g,O);return A;},updateTargets:function(O){var A=this;A.set(f,O);return A;},removeSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O));},removeTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O));},_afterActiveChange:function(O){var A=this;A._uiSetActive(O.newVal);},_afterTargetsChange:function(O){var A=this;O.prevVal.each(function(P){P.removeSource(A);});O.newVal.each(function(P){P.addSource(A);});},_renderNode:function(){var A=this;var P=A.get(s);var O=P.get(G);A.wrapper=O.one(o+d)||k.Node.create(A.ANCHOR_WRAPPER_TEMPLATE);A.wrapper.appendTo(O).appendChild(A.get(J));},_setConnector:function(O){var A=this;return k.merge({viewport:A.get(B)},O);},_setSources:function(O){var A=this;return A._setAnchors(O);},_setTargets:function(O){var A=this;O=A._setAnchors(O);O.each(function(P){P.addSource(A);});return O;},_setAnchors:function(P){var A=this;if(!E(P)){var O=[];k.Array.each(P,function(Q){if(h(Q)){Q=k.Anchor.getAnchorByNode(Q);}O.push(b(Q)?Q:new k.Anchor(Q));});P=new k.ArrayList(O);}return P;},_setNode:function(O){var A=this;var P=A.get(p);return k.one(O).set(p,P).setData(a,A);}},{ATTRS:{diagramNode:{},connector:{setter:"_setConnector",value:{},validator:z},id:{readOnly:true,valueFn:function(){return k.guid();}},maxSources:{value:Infinity,validator:L},maxTargets:{value:Infinity,validator:L},node:{setter:"_setNode",valueFn:function(){var A=this;return k.Node.create(A.NODE_TEMPLATE);}},sources:{value:[],setter:"_setSources",validator:function(A){return r(A)||E(A);}},targets:{value:[],setter:"_setTargets",validator:function(A){return r(A)||E(A);}},viewport:{setter:k.one,value:C}},getAnchorByNode:function(A){return k.one(A).getData(a);}});},"@VERSION@",{requires:["aui-base","arraylist-add","arraylist-filter","json","graphics","dd"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-connector",function(m){var Q=m.Lang,u=Q.isArray,y=Q.isBoolean,P=Q.isNumber,D=Q.isObject,j=Q.isString,G=m.Array,d=function(A){return(A instanceof m.Anchor);},H=function(A){return(A instanceof m.ArrayList);},p=function(A){return(A instanceof m.DiagramNode);},C="anchor",I="arrowPoints",F="body",J="boundingBox",R="builder",z="color",q="connector",a="dataAnchor",B="diagram",v="diagramNode",x="height",s="id",L="lazyDraw",M="max",l="maxSources",k="maxTargets",O="node",o="p1",n="p2",g="path",t="shape",i="sources",h="targets",E="viewport",c="width",N="wrapper",r=".",w=m.getClassName,b=w(B,R,C,O,M,h),f=w(B,R,C,O,M,i),e=w(B,R,C,O,N),K=w(B,R,C,O);m.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(X,S,Z,A,Y,V){var aa=this;X.moveTo(S,Z);X.lineTo(A,Y);var T=Math.atan2(Y-Z,A-S),W=(A+S)/2,U=(Y+Z)/2;aa.drawPolygon(X,aa.translatePoints(aa.rotatePoints(V||aa.ARROW_POINTS,T),W,U));},drawPolygon:function(S,T){var A=this;S.moveTo(T[0][0],T[0][1]);G.each(T,function(V,U){if(U>0){S.lineTo(T[U][0],T[U][1]);}});S.lineTo(T[0][0],T[0][1]);S.end();},translatePoints:function(T,S,V){var A=this;var U=[];G.each(T,function(X,W){U.push([T[W][0]+S,T[W][1]+V]);});return U;},rotatePoints:function(S,U){var A=this;var T=[];G.each(S,function(W,V){T.push(A.rotatePoint(U,S[V][0],S[V][1]));});return T;},rotatePoint:function(S,A,T){return[(A*Math.cos(S))-(T*Math.sin(S)),(A*Math.sin(S))+(T*Math.cos(S))];}};m.Connector=m.Base.create("line",m.Base,[],{graphics:null,shape:null,initializer:function(S){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(L)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var S=A.shape;var U=A.getCoordinate(A.get(o));var T=A.getCoordinate(A.get(n));S.clear();m.PolygonUtil.drawLineArrow(S,U[0],U[1],T[0],T[1],A.get(I));},getCoordinate:function(T){var A=this;var S=A.get(E).getXY();return[T[0]-S[0],T[1]-S[1]];},_initGraphics:function(){var A=this;var S=new m.Graphic({width:A.get(c),height:A.get(x),render:A.get(E)});A.graphics=S;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(t));},_setShape:function(S){var A=this;return m.merge({type:g,stroke:{color:A.get(z),weight:2},fill:{color:A.get(z)}},S);}},{ATTRS:{color:{value:"#666",validator:j},lazyDraw:{value:false,validator:y},viewport:{setter:m.one,value:F},shape:{value:null,setter:"_setShape"},arrowPoints:{value:m.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:u},p2:{value:[0,0],validator:u}}});m.Anchor=m.Base.create("anchor",m.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({sourcesChange:A._afterSourcesChange,targetsChange:A._afterTargetsChange});A._uiSetMaxTargets(A.get(k));},addSource:function(S){var A=this;if(A.get(i).size()
";W.all(".aui-diagram-node").each(function(aG){var aB=G,aD=W.Widget.getByNode(aG),aC=aD.get("name"),aF=aD.get("boundingBox"),aE=aF.one(".log")||W.Node.create("").appendTo(aF);aB+=aC+A;aD.get(m).each(function(aH){aB+=aA+"a: "+aH.get("id")+A;aH.get("targets").each(function(aI){var aJ=aI.get(ac);aI.get("node").setContent(aI.get("id"));aB+=aA+aA+"t: "+aJ.get("name")+" (s: "+aI.get("id")+")"+A;});aH.get("sources").each(function(aJ){var aI=aJ.get(ac);aJ.get("node").setContent(aJ.get("id"));aB+=aA+aA+"s: "+aI.get("name")+" (t: "+aJ.get("id")+")"+A;});});aE.setContent(aB);});};var r=W.Component.create({NAME:T,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:w},tmpConnector:{setter:"_setTmpConnector",value:{},validator:w}},EXTENDS:W.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=W.getDoc().on(E,W.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ai,W.bind(A._onNodeClick,A),f+M);A.dropContainer.delegate(Q,W.bind(A._onNodeEdit,A),f+M);A.dropContainer.delegate(n,W.bind(A._onMouseenterAnchors,A),f+aj);A.dropContainer.delegate(K,W.bind(A._onMouseleaveAnchors,A),f+aj);},syncUI:function(){var A=this;W.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new W.Connector(A.get(B));},createField:function(aA){var A=this;if(!ap(aA)){aA.builder=A;aA.viewport=A.get(C);aA=new (A.getFieldClass(aA.type||j))(aA);}aA.set(au,A);return aA;},getFieldClass:function(aB){var A=this;var aA=W.DiagramBuilder.types[aB];if(aA){return aA;}else{W.log("The field type: ["+aB+"] couldn't be found.");return null;}},isFieldsDrag:function(aB){var A=this;var aA=A.fieldsDrag;return(aB===aA.dd);},plotField:function(aA){var A=this;if(!aA.get(av)){aA.render(A.dropContainer);}},unselectAll:function(){var A=this;var aA=A.selectedNode;if(aA){aA.set(aq,false);}A.selectedNode=null;},select:function(aA){var A=this;A.unselectAll();A.stopEditingNode();A.selectedNode=aA.set(aq,true).focus();},startEditingNode:function(aA){var A=this;if(aA){A.stopEditingNode();A.tabView.selectTab(W.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(h,aA.getProperties());aA.get(k).addClass(ak);A.editNode=aA;}},stopEditingNode:function(aB){var A=this;var aA=aB||A.editNode;if(aA){A.tabView.selectTab(W.DiagramBuilder.FIELDS_TAB);aA.get(k).removeClass(ak);A.editNode=null;}},_afterKeyEvent:function(aA){var A=this;if(!A.selectedNode||aA.hasModifier()||!aA.isKeyInSet(a,I)){return;}if(aA.isKey(a)){A._onEscKey(aA);}else{if(aA.isKey(I)){A._onDeleteKey(aA);}}aA.halt();},_onCancel:function(aA){var A=this;A.stopEditingNode();},_onDrag:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.get(m).each(function(aD){aD.alignConnectors();});}},_onDragEnd:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.set(q,aC.getLeftTop());}},_onDropHit:function(aB){var A=this;var aA=aB.drag;if(A.isAvailableFieldsDrag(aA)){var aD=aA.get(j).getData(F);var aC=A.addField({xy:ab(aA.lastXY,A.dropContainer),type:aD.get(e),fields:[{}]});A.select(aC);}},_onDeleteKey:function(aA){var A=this;A.selectedNode.close();},_onEscKey:function(aA){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aA){var A=this;aA.currentTarget.addClass(Z);},_onMouseleaveAnchors:function(aA){var A=this;aA.currentTarget.removeClass(Z);},_onNodeClick:function(aA){var A=this;var aB=W.Widget.getByNode(aA.currentTarget);A.select(aB);},_onNodeEdit:function(aA){var A=this;if(!aA.target.ancestor(f+b,true)){return;}var aB=W.Widget.getByNode(aA.currentTarget);if(aB){A.startEditingNode(aB);}},_onSave:function(aB){var A=this;var aA=A.editNode;var aC=A.propertyList.get(h);if(aA){ay.each(aC.get(O),function(aD){var aE=aD.get(ag);aA.set(aE.attributeName,aE.value);});A.stopEditingNode(aA);}},_setTmpConnector:function(aA){var A=this;return W.merge({lazyDraw:true,viewport:A.viewport},aA);},_setFieldsDragConfig:function(aB){var A=this;var aA=A.dropContainer;return W.merge({bubbleTargets:A,container:aA,dragConfig:{plugins:[{cfg:{constrain:aA},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll}]},nodes:f+M},aB||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new W.DD.Delegate(A.get(aa));}}});W.DiagramBuilder=r;W.DiagramBuilder.types={};var H=W.Component.create({NAME:s,EXTENDS:W.Overlay,AUGMENTS:[W.FieldSupport]});var az=W.Component.create({NAME:s,UI_ATTRS:[m,i,aq],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:w},builder:{setter:"_setBuilder",validator:J},description:{value:G,validator:ar},height:{value:90},name:{valueFn:function(){var A=this;
-return A.get(e)+(++W.Env._uidx);},validator:ar},selected:{value:false,validator:ao},strings:{value:{deleteMessage:"Are you sure you want to delete?",description:"Description",name:"Name",type:"Type"}},type:{value:j,validator:ar},controlsToolbar:{setter:"_setControlsToolbar",validator:w,value:null},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:H,buildNodeId:function(A){return s+z+an+z+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(k).addClass(M+ax+A.get(e));A.set("bodyContent",A.get(i));},alignAnchors:function(){var aA=this;var aE=aA.get(m);var aC=aA.get(k).get(g),aD=Math.floor(360/aE.size()),aB=aC.width/2,A=aC.height/2,aG=aC.left+aC.width/2,aF=aC.top+aC.height/2;aE.each(function(aK,aJ){var aI=aK.get(j);var aL=aI.get(g);var aH=aA._getEllipseXY(aB,A,aG,aF,aJ*aD);aI.setXY([aH[0]-aL.width/2,aH[1]-aL.height/2]);aK.alignConnectors();});return aA;},close:function(){var aA=this;var A=aA.getStrings();if(confirm(A[Y])){aA.get(m).each(function(aB){aB.destroy();});aA.destroy();}N();return aA;},createField:function(aB){var A=this;if(!S(aB)){var aA=A.get(au);aB.diagramNode=A;aB.viewport=(aA?aA.get(C):null);aB=new W.Anchor(aB);}return aB;},getLeftTop:function(){var A=this;return ab(A.get(k),A._getContainer());},getProperties:function(){var A=this;var aA=A.getPropertyModel();ay.each(aA,function(aB){aB.value=A.get(aB.attributeName);});return aA;},getPropertyModel:function(){var aA=this;var A=aA.getStrings();return[{attributeName:al,editor:new W.TextAreaCellEditor(),name:A[al]},{attributeName:i,editor:new W.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[i]},{attributeName:e,editor:false,name:A[e]}];},_afterRender:function(aA){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(au).dropContainer||A.get(k).get(d));},_getEllipseXY:function(aA,A,aD,aC,aE){var aB=aE*Math.PI/180;return[aD+aA*Math.cos(aB),aC-A*Math.sin(aB)];},_handleAddAnchorEvent:function(aA){var A=this;A.addField({});},_handleAddTaskEvent:function(aB){var A=this;var aA=A.get(au);var aE=new W.DiagramNode({type:j,xy:[100,100]});aA.addField(aE);var aC=A.addField({});var aD=aE.addField({});aC.connect(aD);},_handleCloseEvent:function(aA){var A=this;A.close();},_onAnchorDrag:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(ae,aB.target.get(am).getCenterXY());},_onAnchorDragEnd:function(aB){var A=this;var aA=A.get(au).tmpConnector.shape;aA.clear();aA.end();},_onAnchorDragStart:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(af,aB.target.get(j).getCenterXY());},_onAnchorDropHit:function(aA){var A=this;var aB=W.Anchor.getAnchorByNode(aA.drag.get(j));var aC=W.Anchor.getAnchorByNode(aA.drop.get(j));aB.connect(aC);N();},_renderControls:function(){var A=this;var aA=A.get(k);A.controlsNode=W.Node.create(A.CONTROLS_TEMPLATE).appendTo(aA);},_renderNodes:function(){var A=this;var aA=A.get(k);A.anchorWrapper=W.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aA);},_renderControlsToolbar:function(aA){var A=this;A.controlsToolbar=new W.Toolbar(A.get(ah)).render(A.controlsNode);},_setBuilder:function(aA){var A=this;A.get(m).each(function(aB){aB.set(C,aA.get(C));});return aA;},_setAnchorsDragConfig:function(aB){var A=this;var aA=A.get(au);return W.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[X],plugins:[{cfg:{constrain:(aA?aA.get(C):null)},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:W.Plugin.DDProxy}]},nodes:f+aj,target:true},aB||{});},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new W.DD.Delegate(A.get(R));},_setControlsToolbar:function(aA){var A=this;return W.merge({activeState:false,children:[{handler:W.bind(A._handleAddAnchorEvent,A),icon:V},{handler:W.bind(A._handleAddTaskEvent,A),icon:x},{handler:W.bind(A._handleCloseEvent,A),icon:P}]},aA);},_uiSetFields:function(aA){var A=this;if(A.get(av)){A.alignAnchors();setTimeout(function(){A.anchorsDrag.syncTargets();},50);}},_uiSetName:function(aB){var A=this;var aA=A.get(k);aA.setAttribute(i,W.DiagramNode.buildNodeId(aB));},_uiSetSelected:function(aA){var A=this;A.get(k).toggleClass(aw,aA);if(aA&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aB){var A=this;var aA=A._getContainer().getXY();this._posNode.setXY([aB[0]+aA[0],aB[1]+aA[1]]);}}});W.DiagramNode=az;W.DiagramBuilder.types[j]=W.DiagramNode;W.DiagramNodeTask=W.Component.create({NAME:s,ATTRS:{type:{value:t}},EXTENDS:W.DiagramNode});W.DiagramBuilder.types[t]=W.DiagramNodeTask;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-impl",function(aj){var Y=aj.Lang,c=Y.isArray,E=Y.isObject,aJ=Y.isString,aE=Y.isBoolean,aR=aj.Array,U=function(A){return(A instanceof aj.DiagramBuilderBase);},aF=function(A){return(A instanceof aj.DiagramNode);},af=function(A){return(A instanceof aj.Anchor);},ap=function(A,aU){var aT=c(aU)?aU:aU.getXY();var aV=c(A)?A:A.getXY();return aR.map(aV,function(aX,aW){return Math.max(0,aX-aT[aW]);});},an="addAnchor",aM="addAnchorMessage",j="addNode",ar="anchor",ak="anchors",ae="anchorsDragConfig",Q="availableField",V="boolean",o="boundingBox",aN="builder",ac="cancel",ay="click",aK="closeEvent",B="closeMessage",ah="content",J="controls",aw="controlsToolbar",av="data",ad="dblclick",T="delete",al="deleteMessage",aB="description",C="diagram",ag="diagram-builder",aq="diagramNode",x="diagram-node",aC="dragNode",M="editing",y="editEvent",H="editMessage",a="esc",aD="field",q="fields",ao="fieldsDragConfig",p="hover",ax="id",N="keydown",ai="link",aa="max",t="maxSources",r="mouseenter",W="mouseleave",m="name",n="node",au="p1",at="p2",d="parentNode",l="pencil",ab="records",k="recordset",h="region",aO="rendered",F="required",aG="selected",G="shuffle",O="source",aH="sources",aI="string",i="target",I="targets",K="tmpConnector",e="type",L="viewport",aL="wrapper",v="xy",aQ="-",g=".",P="",f="#",D="_",u=aj.getClassName,R=u(C,aN,ar,n,aa,I),am=u(C,aN,ar,p),aA=u(C,aN,ar,n),z=u(C,aN,ar,n,aL),s=u(C,aN,J),X=u(C,n),b=u(C,n,ah),az=u(C,n,M),aP=u(C,n,aG);var Z=function(){var aT=" ",A="
";aj.all(".aui-diagram-node").each(function(aZ){var aU=P,aW=aj.Widget.getByNode(aZ),aV=aW.get("name"),aY=aW.get("boundingBox"),aX=aY.one(".log")||aj.Node.create("").appendTo(aY);aU+=aV+A;aW.get(q).each(function(a0){aU+=aT+"a: "+a0.get("id")+A;a0.get("targets").each(function(a1){var a2=a1.get(aq);a1.get("node").setContent(a1.get("id"));aU+=aT+aT+"t: "+a2.get("name")+" (s: "+a1.get("id")+")"+A;});a0.get("sources").each(function(a2){var a1=a2.get(aq);a2.get("node").setContent(a2.get("id"));aU+=aT+aT+"s: "+a1.get("name")+" (t: "+a2.get("id")+")"+A;});});aX.setContent(aU);});};var w=aj.Component.create({NAME:ag,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:E},tmpConnector:{setter:"_setTmpConnector",value:{},validator:E}},EXTENDS:aj.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=aj.getDoc().on(N,aj.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ay,aj.bind(A._onNodeClick,A),g+X);A.dropContainer.delegate(ad,aj.bind(A._onNodeEdit,A),g+X);A.dropContainer.delegate(r,aj.bind(A._onMouseenterAnchors,A),g+aA);A.dropContainer.delegate(W,aj.bind(A._onMouseleaveAnchors,A),g+aA);},syncUI:function(){var A=this;aj.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new aj.Connector(A.get(K));},connect:function(aU,aW){var aT=this;if(aJ(aU)){aU=aj.Widget.getByNode(f+aj.DiagramNode.buildNodeId(aU));}if(aJ(aW)){aW=aj.Widget.getByNode(f+aj.DiagramNode.buildNodeId(aW));}if(aU&&aW){var aV=aU.findAvailableAnchor();var A=aW.findAvailableAnchor();if(aV&&A){aV.connect(A);}}return aT;},connectAll:function(aT){var A=this;aR.each(aT,function(aU){if(aU.hasOwnProperty(O)&&aU.hasOwnProperty(i)){A.connect(aU.source,aU.target);}});return A;},createField:function(aT){var A=this;if(!aF(aT)){aT.builder=A;aT.viewport=A.get(L);aT=new (A.getFieldClass(aT.type||n))(aT);}aT.set(aN,A);return aT;},getFieldClass:function(aU){var A=this;var aT=aj.DiagramBuilder.types[aU];if(aT){return aT;}else{aj.log("The field type: ["+aU+"] couldn't be found.");return null;}},isFieldsDrag:function(aU){var A=this;var aT=A.fieldsDrag;return(aU===aT.dd);},plotField:function(aT){var A=this;if(!aT.get(aO)){aT.render(A.dropContainer);}},unselectAll:function(){var A=this;var aT=A.selectedNode;if(aT){aT.set(aG,false);}A.selectedNode=null;},select:function(aT){var A=this;A.unselectAll();A.selectedNode=aT.set(aG,true).focus();},startEditingNode:function(aT){var A=this;if(aT){A.stopEditingNode();A.tabView.selectTab(aj.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(k,aT.getProperties());aT.get(o).addClass(az);A.editNode=aT;}},stopEditingNode:function(aU){var A=this;var aT=aU||A.editNode;if(aT){A.tabView.selectTab(aj.DiagramBuilder.FIELDS_TAB);aT.get(o).removeClass(az);A.editNode=null;}},_afterKeyEvent:function(aT){var A=this;if(!A.selectedNode||aT.hasModifier()||!aT.isKeyInSet(a,T)){return;}if(aT.isKey(a)){A._onEscKey(aT);}else{if(aT.isKey(T)){A._onDeleteKey(aT);}}aT.halt();},_onCancel:function(aT){var A=this;A.stopEditingNode();},_onDrag:function(aU){var A=this;var aT=aU.target;if(A.isFieldsDrag(aT)){var aV=aj.Widget.getByNode(aT.get(aC));aV.get(q).each(function(aW){aW.alignConnectors();});}},_onDragEnd:function(aU){var A=this;var aT=aU.target;if(A.isFieldsDrag(aT)){var aV=aj.Widget.getByNode(aT.get(aC));aV.set(v,aV.getLeftTop());}},_onDropHit:function(aU){var A=this;var aT=aU.drag;if(A.isAvailableFieldsDrag(aT)){var aW=aT.get(n).getData(Q);var aV=A.addField({xy:ap(aT.lastXY,A.dropContainer),type:aW.get(e),fields:[{}]});A.select(aV);}},_onDeleteKey:function(aT){var A=this;var aU=A.selectedNode;if(!aU.get(F)){aU.close();}},_onEscKey:function(aT){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aT){var A=this;aT.currentTarget.addClass(am);},_onMouseleaveAnchors:function(aT){var A=this;aT.currentTarget.removeClass(am);},_onNodeClick:function(aT){var A=this;var aU=aj.Widget.getByNode(aT.currentTarget);A.select(aU);},_onNodeEdit:function(aT){var A=this;if(!aT.target.ancestor(g+b,true)){return;}var aU=aj.Widget.getByNode(aT.currentTarget);if(aU){A.startEditingNode(aU);}},_onSave:function(aU){var A=this;var aT=A.editNode;var aV=A.propertyList.get(k);if(aT){aR.each(aV.get(ab),function(aW){var aX=aW.get(av);aT.set(aX.attributeName,aX.value);});A.stopEditingNode(aT);
+}},_setTmpConnector:function(aT){var A=this;return aj.merge({lazyDraw:true,viewport:A.viewport},aT);},_setFieldsDragConfig:function(aU){var A=this;var aT=A.dropContainer;return aj.merge({bubbleTargets:A,container:aT,dragConfig:{plugins:[{cfg:{constrain:aT},fn:aj.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:aj.Plugin.DDWinScroll}]},nodes:g+X},aU||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new aj.DD.Delegate(A.get(ao));}}});aj.DiagramBuilder=w;aj.DiagramBuilder.types={};var S=aj.Component.create({NAME:x,EXTENDS:aj.Overlay,AUGMENTS:[aj.FieldSupport]});var aS=aj.Component.create({NAME:x,UI_ATTRS:[q,m,F,aG],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:E},builder:{setter:"_setBuilder",validator:U},required:{value:false,validator:aE},description:{value:P,validator:aJ},height:{value:90},name:{valueFn:function(){var A=this;return A.get(e)+(++aj.Env._uidx);},validator:aJ},selected:{value:false,validator:aE},strings:{value:{addAnchorMessage:"Add Anchor",closeMessage:"Close",deleteMessage:"Are you sure you want to delete?",description:"Description",editMessage:"Edit",name:"Name",type:"Type"}},type:{value:n,validator:aJ},controlsToolbar:{validator:E,valueFn:"_valueControlsToolbar"},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:S,buildNodeId:function(A){return aq+D+aD+D+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(o).addClass(X+aQ+A.get(e));A.set("bodyContent",A.get(m));},alignAnchors:function(){var aT=this;var aX=aT.get(q);var aV=aT.get(o).get(h),aW=Math.floor(360/aX.size()),aU=aV.width/2,A=aV.height/2,aZ=aV.left+aV.width/2,aY=aV.top+aV.height/2;aX.each(function(a3,a2){var a1=a3.get(n);var a4=a1.get(h);var a0=aT._getEllipseXY(aU,A,aZ,aY,a2*aW);a1.setXY([a0[0]-a4.width/2,a0[1]-a4.height/2]);a3.alignConnectors();});return aT;},close:function(){var aT=this;var A=aT.getStrings();if(confirm(A[al])){aT.get(q).each(function(aU){aU.destroy();});aT.destroy();}Z();return aT;},createField:function(aU){var A=this;if(!af(aU)){var aT=A.get(aN);aU.diagramNode=A;aU.viewport=(aT?aT.get(L):null);aU=new aj.Anchor(aU);}return aU;},findAvailableAnchor:function(){var A=this;var aT=null;A.get(q).some(function(aU){if(!aU.hasConnection()){aT=aU;return true;}});if(!aT){aT=A.addField({});}return aT;},getConnectionNode:function(){var A=this;return new aj.DiagramNode({xy:[100,100]});},getLeftTop:function(){var A=this;return ap(A.get(o),A._getContainer());},getProperties:function(){var A=this;var aT=A.getPropertyModel();aR.each(aT,function(aW){var aV=A.get(aW.attributeName),aU=Y.type(aV);if(aU===V||aU===aI){aV=String(aV);}aW.value=aV;});return aT;},getPropertyModel:function(){var aT=this;var A=aT.getStrings();return[{attributeName:aB,editor:new aj.TextAreaCellEditor(),name:A[aB]},{attributeName:m,editor:new aj.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[m]},{attributeName:e,editor:false,name:A[e]}];},syncDragTargets:function(){var A=this;A.anchorsDrag.syncTargets();},syncDropTargets:function(aT){var A=this;A.get(q).each(function(aV){var aU=aj.DD.DDM.getDrop(aV.get(n));if(aU){if(aV.get(aH).size()===aV.get(t)){aU.removeFromGroup(ak);}else{aU.addToGroup(ak);}}});},_afterRender:function(aT){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(aN).dropContainer||A.get(o).get(d));},_getEllipseXY:function(aT,A,aW,aV,aX){var aU=aX*Math.PI/180;return[aW+aT*Math.cos(aU),aV-A*Math.sin(aU)];},_handleAddAnchorEvent:function(aT){var A=this;A.addField({});},_handleAddNodeEvent:function(aU){var A=this;var aT=A.get(aN);var aV=A.findAvailableAnchor();if(aV){var aW=A.getConnectionNode();aT.addField(aW);aV.connect(aW.addField({}));}},_handleEditEvent:function(aT){var A=this;A.get(aN).startEditingNode(A);},_handleCloseEvent:function(aT){var A=this;if(!A.get(F)){A.close();}},_onAnchorDrag:function(aU){var A=this;var aT=A.get(aN);aT.tmpConnector.set(at,aU.target.get(aC).getCenterXY());},_onAnchorDragEnd:function(aU){var A=this;var aT=A.get(aN).tmpConnector.shape;aT.clear();aT.end();},_onAnchorDragStart:function(aU){var A=this;var aT=A.get(aN);aT.tmpConnector.set(au,aU.target.get(n).getCenterXY());},_onAnchorDropHit:function(aT){var A=this;var aU=aj.Anchor.getAnchorByNode(aT.drag.get(n));var aV=aj.Anchor.getAnchorByNode(aT.drop.get(n));aU.connect(aV);Z();},_renderControls:function(){var A=this;var aT=A.get(o);A.controlsNode=aj.Node.create(A.CONTROLS_TEMPLATE).appendTo(aT);},_renderNodes:function(){var A=this;var aT=A.get(o);A.anchorWrapper=aj.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aT);},_renderControlsToolbar:function(aT){var A=this;A.controlsToolbar=new aj.Toolbar(A.get(aw)).render(A.controlsNode);A._uiSetRequired(A.get(F));},_setBuilder:function(aT){var A=this;A.get(q).each(function(aU){aU.set(L,aT.get(L));});return aT;},_setAnchorsDragConfig:function(aU){var A=this;var aT=A.get(aN);return aj.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[ak],plugins:[{cfg:{constrain:(aT?aT.get(L):null)},fn:aj.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:aj.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:aj.Plugin.DDProxy}]},nodes:g+aA,target:true},aU||{});},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new aj.DD.Delegate(A.get(ae));A.anchorsDrag.dd.addInvalid(g+R);},_uiSetFields:function(aT){var A=this;if(A.get(aO)){A.alignAnchors();A.syncDragTargets();A.syncDropTargets();}},_uiSetName:function(aU){var A=this;var aT=A.get(o);aT.set(ax,aj.DiagramNode.buildNodeId(aU));},_uiSetRequired:function(aV){var aU=this;var aT=aU.getStrings();var A=aU.controlsToolbar;if(A){if(aV){A.remove(aK);}else{A.add({handler:aj.bind(aU._handleCloseEvent,aU),icon:ac,id:aK,title:aT[B]});}}},_uiSetSelected:function(aT){var A=this;
+A.get(o).toggleClass(aP,aT);if(aT&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aU){var A=this;var aT=A._getContainer().getXY();this._posNode.setXY([aU[0]+aT[0],aU[1]+aT[1]]);},_valueControlsToolbar:function(aU){var aT=this;var A=aT.getStrings();return{activeState:false,children:[{handler:aj.bind(aT._handleEditEvent,aT),icon:l,id:y,title:A[H]},{handler:aj.bind(aT._handleAddAnchorEvent,aT),icon:ai,id:an,title:A[aM]},{handler:aj.bind(aT._handleAddNodeEvent,aT),icon:G,id:j},{handler:aj.bind(aT._handleCloseEvent,aT),icon:ac,id:aK,title:A[B]}]};}}});aj.DiagramNode=aS;aj.DiagramBuilder.types[n]=aj.DiagramNode;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});
\ No newline at end of file
diff --git a/build/aui-diagram-builder/aui-diagram-builder-impl.js b/build/aui-diagram-builder/aui-diagram-builder-impl.js
index 47d1c299466..73cdda06816 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-impl.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-impl.js
@@ -28,14 +28,20 @@ var Lang = A.Lang,
});
},
+ ADD_ANCHOR = 'addAnchor',
+ ADD_ANCHOR_MESSAGE = 'addAnchorMessage',
+ ADD_NODE = 'addNode',
ANCHOR = 'anchor',
ANCHORS = 'anchors',
ANCHORS_DRAG_CONFIG = 'anchorsDragConfig',
AVAILABLE_FIELD = 'availableField',
+ BOOLEAN = 'boolean',
BOUNDING_BOX = 'boundingBox',
BUILDER = 'builder',
CANCEL = 'cancel',
CLICK = 'click',
+ CLOSE_EVENT = 'closeEvent',
+ CLOSE_MESSAGE = 'closeMessage',
CONTENT = 'content',
CONTROLS = 'controls',
CONTROLS_TOOLBAR = 'controlsToolbar',
@@ -50,13 +56,18 @@ var Lang = A.Lang,
DIAGRAM_NODE_NAME = 'diagram-node',
DRAG_NODE = 'dragNode',
EDITING = 'editing',
+ EDIT_EVENT = 'editEvent',
+ EDIT_MESSAGE = 'editMessage',
ESC = 'esc',
FIELD = 'field',
FIELDS = 'fields',
FIELDS_DRAG_CONFIG = 'fieldsDragConfig',
HOVER = 'hover',
+ ID = 'id',
KEYDOWN = 'keydown',
LINK = 'link',
+ MAX = 'max',
+ MAX_SOURCES = 'maxSources',
MOUSEENTER = 'mouseenter',
MOUSELEAVE = 'mouseleave',
NAME = 'name',
@@ -64,26 +75,35 @@ var Lang = A.Lang,
P1 = 'p1',
P2 = 'p2',
PARENT_NODE = 'parentNode',
+ PENCIL = 'pencil',
RECORDS = 'records',
RECORDSET = 'recordset',
REGION = 'region',
RENDERED = 'rendered',
+ REQUIRED = 'required',
SELECTED = 'selected',
SHUFFLE = 'shuffle',
- TASK = 'task',
+ SOURCE = 'source',
+ SOURCES = 'sources',
+ STRING = 'string',
+ TARGET = 'target',
+ TARGETS = 'targets',
TMP_CONNECTOR = 'tmpConnector',
TYPE = 'type',
VIEWPORT = 'viewport',
WRAPPER = 'wrapper',
XY = 'xy',
+ _DASH = '-',
_DOT = '.',
- _DOLLAR = '$',
_EMPTY_STR = '',
- _DASH = '-',
+ _HASH = '#',
+ _UNDERLINE = '_',
AgetClassName = A.getClassName,
+ CSS_DB_ANCHOR_NODE_MAX_TARGETS = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, TARGETS),
+ // CSS_DB_ANCHOR_NODE_MAX_SOURCES = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, MAX, SOURCES),
CSS_DB_ANCHOR_HOVER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, HOVER),
CSS_DB_ANCHOR_NODE = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE),
CSS_DB_ANCHOR_NODE_WRAPPER = AgetClassName(DIAGRAM, BUILDER, ANCHOR, NODE, WRAPPER),
@@ -185,6 +205,41 @@ var DiagramBuilder = A.Component.create({
instance.tmpConnector = new A.Connector(instance.get(TMP_CONNECTOR));
},
+ connect: function(diagramNode1, diagramNode2) {
+ var instance = this;
+
+ if (isString(diagramNode1)) {
+ diagramNode1 = A.Widget.getByNode(_HASH+A.DiagramNode.buildNodeId(diagramNode1));
+ }
+
+ if (isString(diagramNode2)) {
+ diagramNode2 = A.Widget.getByNode(_HASH+A.DiagramNode.buildNodeId(diagramNode2));
+ }
+
+ if (diagramNode1 && diagramNode2) {
+ var a1 = diagramNode1.findAvailableAnchor();
+ var a2 = diagramNode2.findAvailableAnchor();
+
+ if (a1 && a2) {
+ a1.connect(a2);
+ }
+ }
+
+ return instance;
+ },
+
+ connectAll: function(nodes) {
+ var instance = this;
+
+ AArray.each(nodes, function(node) {
+ if (node.hasOwnProperty(SOURCE) && node.hasOwnProperty(TARGET)) {
+ instance.connect(node.source, node.target);
+ }
+ });
+
+ return instance;
+ },
+
createField: function(val) {
var instance = this;
@@ -244,7 +299,7 @@ var DiagramBuilder = A.Component.create({
var instance = this;
instance.unselectAll();
- instance.stopEditingNode();
+ // instance.stopEditingNode();
instance.selectedNode = diagramNode.set(SELECTED, true).focus();
},
@@ -343,8 +398,11 @@ var DiagramBuilder = A.Component.create({
_onDeleteKey: function(event) {
var instance = this;
+ var selectedNode = instance.selectedNode;
- instance.selectedNode.close();
+ if (!selectedNode.get(REQUIRED)) {
+ selectedNode.close();
+ }
},
_onEscKey: function(event) {
@@ -475,7 +533,7 @@ var DiagramNodeOverlay = A.Component.create({
var DiagramNode = A.Component.create({
NAME: DIAGRAM_NODE_NAME,
- UI_ATTRS: [FIELDS, NAME, SELECTED],
+ UI_ATTRS: [FIELDS, NAME, REQUIRED, SELECTED],
ATTRS: {
anchorsDragConfig: {
@@ -489,6 +547,11 @@ var DiagramNode = A.Component.create({
validator: isDiagramBuilder
},
+ required: {
+ value: false,
+ validator: isBoolean
+ },
+
description: {
value: _EMPTY_STR,
validator: isString
@@ -514,8 +577,11 @@ var DiagramNode = A.Component.create({
strings: {
value: {
+ addAnchorMessage: 'Add Anchor',
+ closeMessage: 'Close',
deleteMessage: 'Are you sure you want to delete?',
description: 'Description',
+ editMessage: 'Edit',
name: 'Name',
type: 'Type'
}
@@ -527,9 +593,8 @@ var DiagramNode = A.Component.create({
},
controlsToolbar: {
- setter: '_setControlsToolbar',
validator: isObject,
- value: null
+ valueFn: '_valueControlsToolbar'
},
width: {
@@ -548,7 +613,7 @@ var DiagramNode = A.Component.create({
EXTENDS: DiagramNodeOverlay,
buildNodeId: function(id) {
- return DIAGRAM_NODE_NAME + _DOLLAR + FIELD + _DOLLAR + id;
+ return DIAGRAM_NODE + _UNDERLINE + FIELD + _UNDERLINE + id;
},
prototype: {
@@ -634,6 +699,33 @@ var DiagramNode = A.Component.create({
return val;
},
+ findAvailableAnchor: function() {
+ var instance = this;
+ var available = null;
+
+ instance.get(FIELDS).some(function(anchor) {
+ if (!anchor.hasConnection()) {
+ available = anchor;
+
+ return true;
+ }
+ });
+
+ if (!available) {
+ available = instance.addField({});
+ }
+
+ return available;
+ },
+
+ getConnectionNode: function() {
+ var instance = this;
+
+ return new A.DiagramNode({
+ xy: [100, 100] // TODO - find best position?
+ });
+ },
+
getLeftTop: function() {
var instance = this;
@@ -645,7 +737,13 @@ var DiagramNode = A.Component.create({
var propertyModel = instance.getPropertyModel();
AArray.each(propertyModel, function(property) {
- property.value = instance.get(property.attributeName);
+ var value = instance.get(property.attributeName), type = Lang.type(value);
+
+ if (type === BOOLEAN || type === STRING) {
+ value = String(value);
+ }
+
+ property.value = value;
});
return propertyModel;
@@ -682,6 +780,29 @@ var DiagramNode = A.Component.create({
];
},
+ syncDragTargets: function() {
+ var instance = this;
+
+ instance.anchorsDrag.syncTargets();
+ },
+
+ syncDropTargets: function(anchor) {
+ var instance = this;
+
+ instance.get(FIELDS).each(function(anchor) {
+ var drop = A.DD.DDM.getDrop(anchor.get(NODE));
+
+ if (drop) {
+ if (anchor.get(SOURCES).size() === anchor.get(MAX_SOURCES)) {
+ drop.removeFromGroup(ANCHORS);
+ }
+ else {
+ drop.addToGroup(ANCHORS);
+ }
+ }
+ });
+ },
+
_afterRender: function(event) {
var instance = this;
@@ -706,30 +827,33 @@ var DiagramNode = A.Component.create({
var instance = this;
instance.addField({});
-
- // event.halt();
},
- _handleAddTaskEvent: function(event) {
+ _handleAddNodeEvent: function(event) {
var instance = this;
var builder = instance.get(BUILDER);
+ var source = instance.findAvailableAnchor();
- var diagramNode = new A.DiagramNode({
- type: NODE,
- xy: [100, 100] // TODO - find best position?
- });
+ if (source) {
+ var diagramNode = instance.getConnectionNode();
- builder.addField(diagramNode);
+ builder.addField(diagramNode);
+ source.connect(diagramNode.addField({}));
+ }
+ },
- var source = instance.addField({});
- var target = diagramNode.addField({});
- source.connect(target);
+ _handleEditEvent: function(event) {
+ var instance = this;
+
+ instance.get(BUILDER).startEditingNode(instance);
},
_handleCloseEvent: function(event) {
var instance = this;
- instance.close();
+ if (!instance.get(REQUIRED)) {
+ instance.close();
+ }
},
_onAnchorDrag: function(event) {
@@ -785,6 +909,10 @@ var DiagramNode = A.Component.create({
instance.get(CONTROLS_TOOLBAR)
)
.render(instance.controlsNode);
+
+ instance._uiSetRequired(
+ instance.get(REQUIRED)
+ );
},
_setBuilder: function(val) {
@@ -841,31 +969,10 @@ var DiagramNode = A.Component.create({
instance.anchorsDrag = new A.DD.Delegate(
instance.get(ANCHORS_DRAG_CONFIG)
);
- },
-
- _setControlsToolbar: function(val) {
- var instance = this;
- return A.merge(
- {
- activeState: false,
- children: [
- {
- handler: A.bind(instance._handleAddAnchorEvent, instance),
- icon: LINK
- },
- {
- handler: A.bind(instance._handleAddTaskEvent, instance),
- icon: SHUFFLE
- },
- {
- handler: A.bind(instance._handleCloseEvent, instance),
- icon: CANCEL
- }
- ]
- },
- val
- );
+ instance.anchorsDrag.dd
+ .addInvalid(_DOT+CSS_DB_ANCHOR_NODE_MAX_TARGETS);
+ // .addInvalid(_DOT+CSS_DB_ANCHOR_NODE_MAX_SOURCES);
},
_uiSetFields: function(val) {
@@ -874,9 +981,10 @@ var DiagramNode = A.Component.create({
if (instance.get(RENDERED)) {
instance.alignAnchors();
- setTimeout(function() {
- instance.anchorsDrag.syncTargets();
- }, 50);
+ // setTimeout(function() {
+ instance.syncDragTargets();
+ instance.syncDropTargets();
+ // }, 50);
}
},
@@ -884,7 +992,27 @@ var DiagramNode = A.Component.create({
var instance = this;
var boundingBox = instance.get(BOUNDING_BOX);
- boundingBox.setAttribute(NAME, A.DiagramNode.buildNodeId(val));
+ boundingBox.set(ID, A.DiagramNode.buildNodeId(val));
+ },
+
+ _uiSetRequired: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+ var controlsToolbar = instance.controlsToolbar;
+
+ if (controlsToolbar) {
+ if (val) {
+ controlsToolbar.remove(CLOSE_EVENT);
+ }
+ else {
+ controlsToolbar.add({
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL,
+ id: CLOSE_EVENT,
+ title: strings[CLOSE_MESSAGE]
+ });
+ }
+ }
},
_uiSetSelected: function(val) {
@@ -906,7 +1034,41 @@ var DiagramNode = A.Component.create({
var containerXY = instance._getContainer().getXY();
this._posNode.setXY([ val[0] + containerXY[0], val[1] + containerXY[1] ]);
- }
+ },
+
+ _valueControlsToolbar: function(val) {
+ var instance = this;
+ var strings = instance.getStrings();
+
+ return {
+ activeState: false,
+ children: [
+ {
+ handler: A.bind(instance._handleEditEvent, instance),
+ icon: PENCIL,
+ id: EDIT_EVENT,
+ title: strings[EDIT_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddAnchorEvent, instance),
+ icon: LINK,
+ id: ADD_ANCHOR,
+ title: strings[ADD_ANCHOR_MESSAGE]
+ },
+ {
+ handler: A.bind(instance._handleAddNodeEvent, instance),
+ icon: SHUFFLE,
+ id: ADD_NODE
+ },
+ {
+ handler: A.bind(instance._handleCloseEvent, instance),
+ icon: CANCEL,
+ id: CLOSE_EVENT,
+ title: strings[CLOSE_MESSAGE]
+ }
+ ]
+ };
+ }
}
});
@@ -914,20 +1076,6 @@ A.DiagramNode = DiagramNode;
A.DiagramBuilder.types[NODE] = A.DiagramNode;
-A.DiagramNodeTask = A.Component.create({
- NAME: DIAGRAM_NODE_NAME,
-
- ATTRS: {
- type: {
- value: TASK
- }
- },
-
- EXTENDS: A.DiagramNode
-});
-
-A.DiagramBuilder.types[TASK] = A.DiagramNodeTask;
-
// TODO deletar anchors OK
// TODO deletar connections (delete) OK
// TODO Adicionar overlay de controles OK
diff --git a/build/aui-diagram-builder/aui-diagram-builder-min.js b/build/aui-diagram-builder/aui-diagram-builder-min.js
index b99565609b2..4bbe548178a 100644
--- a/build/aui-diagram-builder/aui-diagram-builder-min.js
+++ b/build/aui-diagram-builder/aui-diagram-builder-min.js
@@ -1,5 +1,5 @@
-AUI.add("aui-diagram-builder-base",function(ad){var T=ad.Lang,d=T.isArray,ap=T.isBoolean,M=T.isNumber,B=T.isObject,at=T.isString,I=function(A){return(A instanceof ad.ArrayList);},R=function(A){return(A instanceof ad.Node);},D=function(A){return(A instanceof ad.AvailableField);},aD=ad.Array,V="add",k="addNode",aC="auto",N="availableField",Q="availableFields",az="availableFieldsDragConfig",am="base",s="boundingBox",aw="builder",Z="cancel",aq="clearfix",a="container",ab="content",u="contentBox",J="viewport",P="contentNode",E="createDocumentFragment",z="diagram",F="diagram-builder-base",aa="disk",o="draggable",ay="drop",ak="dropConfig",X="dropContainer",ao="field",t="fields",n="fieldsContainer",an="height",p="helper",W="icon",v="iconClass",aj="id",af="label",ai="list",r="node",y="nodeSettings",ac="propertyList",ax="rendered",al="save",q="settings",O="tab",b="tabs",e="tabview",G="tabView",L="toolbar",j="toolbarContainer",w=ad.getClassName,aB=" ",g=".",H="$",h="#",aE=w(z,aw,am,ay,a),x=w(z,aw,am,J),C=w(z,aw,am,ao),f=w(z,aw,am,t,a),ag=w(z,aw,am,ao,o),c=w(z,aw,am,ao,W),U=w(z,aw,am,ao,af),m=w(z,aw,am,b,a),Y=w(z,aw,am,b,a,ab),ah=w(z,aw,am,O,V),K=w(z,aw,am,O,q),au=w(z,aw,am,L,a),ae=w(p,aq),l=w(W),av=w(e,ab),aA=w(e,ai);var i=ad.Component.create({NAME:N,ATTRS:{draggable:{value:true,validator:ap},label:{validator:at},iconClass:{validator:at},id:{value:ad.guid(),setter:"_setId",validator:at},node:{valueFn:function(aF){var A=this;if(!R(aF)){aF=ad.Node.create(ad.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aF.setData(N,A);}return aF;},validator:R,writeOnce:true},type:{value:r,validator:at}},EXTENDS:ad.Base,buildNodeId:function(A){return Q+H+ao+H+A;},getAvailableFieldByNode:function(A){return ad.one(A).getData(N);},getAvailableFieldById:function(A){return ad.AvailableField.getAvailableFieldByNode(h+ad.AvailableField.buildNodeId(A));},prototype:{FIELD_ITEM_TEMPLATE:'',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(X);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aG){var A=this;var aF=A.availableFieldsDrag;return(aG===aF.dd);},plotFields:function(){var aF=this;var A=aF.get(t);A.each(function(aG){aF.plotField(aG);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(Q));},syncUI:function(){var A=this;var aF=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aF.addClass(ae);},_afterActiveTabChange:function(aG){var A=this;var aF=aG.newVal.get(P);if(A.get(ax)&&(aF===A.settingsNode)){A._renderSettings();}},_afterRender:function(aF){var A=this;A.plotFields();},_afterUiSetHeight:function(aF){var A=this;A.dropContainer.setStyle(an,M(aF)?aF+A.DEF_UNIT:aF);},_defCancelFn:function(aF){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(Z);},_handleSaveEvent:function(){var A=this;A.fire(al);},_renderViewport:function(){var aF=this;var aG=aF.get(u);var A=aF.viewport;A.appendChild(aF.dropContainer);aG.appendChild(A);},_renderPropertyList:function(){var A=this;
-if(!A.propertyList){A.propertyList=new ad.PropertyList(A.get(ac)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aF=new ad.TabView(A.get(G));A.tabView=aF;A.fieldsNode=aF.getTab(0).get(P);A.settingsNode=aF.getTab(1).get(P);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ad.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ad.DD.Drop(A.get(ak));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ad.DD.Delegate(A.get(az));},_setAvailableFields:function(aG){var aF=this;var A=[];aD.each(aG,function(aI,aH){A.push(D(aI)?aI:new ad.AvailableField(aI));});return A;},_setDropConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,groups:[Q],node:A.dropContainer},aF||{});},_setAvailableFieldsDragConfig:function(aF){var A=this;return ad.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[Q],plugins:[{cfg:{moveOnEnd:false},fn:ad.Plugin.DDProxy}]},nodes:g+ag},aF||{});},_setPropertyList:function(aF){var A=this;return ad.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aC}},aF);},_setTabView:function(aI){var aF=this;var aH=aF.get(s);var aJ=aH.one(g+aA);var aG={after:{activeTabChange:ad.bind(aF._afterActiveTabChange,aF)},boundingBox:aH.one(g+m),contentBox:aH.one(g+Y),bubbleTargets:aF,contentNode:aH.one(g+av),cssClass:m,listNode:aJ,render:aF.get(u)};if(!aJ){var A=aF.getStrings();aG.items=[{cssClass:ah,label:A[k]},{cssClass:K,label:A[y]}];}return ad.merge(aG,aI);},_setToolbar:function(aG){var aF=this;var A=aF.getStrings();return ad.merge({activeState:false,bubbleTargets:aF,children:[{handler:ad.bind(aF._handleSaveEvent,aF),label:A[al],icon:aa},{handler:ad.bind(aF._handleCancelEvent,aF),label:A[Z]}]},aG);},_uiSetAvailableFields:function(aH){var A=this;var aG=A.fieldsNode;if(aG){var aF=ad.getDoc().invoke(E);aD.each(aH,function(aI){aF.appendChild(aI.get(r));});aG.setContent(A.fieldsContainer.setContent(aF));}},_uiSetFields:function(aF){var A=this;if(A.get(ax)){A.plotFields();}}}});ad.DiagramBuilderBase=ar;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});AUI.add("aui-diagram-builder-impl",function(W){var L=W.Lang,c=L.isArray,w=L.isObject,ar=L.isString,ao=L.isBoolean,ay=W.Array,J=function(A){return(A instanceof W.DiagramBuilderBase);},ap=function(A){return(A instanceof W.DiagramNode);},S=function(A){return(A instanceof W.Anchor);},ab=function(A,aB){var aA=c(aB)?aB:aB.getXY();var aC=c(A)?A:A.getXY();return ay.map(aC,function(aE,aD){return Math.max(0,aE-aA[aD]);});},ad="anchor",X="anchors",R="anchorsDragConfig",F="availableField",k="boundingBox",au="builder",P="cancel",ai="click",U="content",y="controls",ah="controlsToolbar",ag="data",Q="dblclick",I="delete",Y="deleteMessage",al="description",v="diagram",T="diagram-builder",ac="diagramNode",s="diagram-node",am="dragNode",D="editing",a="esc",an="field",m="fields",aa="fieldsDragConfig",l="hover",E="keydown",V="link",n="mouseenter",K="mouseleave",i="name",j="node",af="p1",ae="p2",d="parentNode",O="records",h="recordset",g="region",av="rendered",aq="selected",x="shuffle",t="task",B="tmpConnector",e="type",C="viewport",at="wrapper",q="xy",f=".",z="$",G="",ax="-",p=W.getClassName,Z=p(v,au,ad,l),aj=p(v,au,ad,j),u=p(v,au,ad,j,at),o=p(v,au,y),M=p(v,j),b=p(v,j,U),ak=p(v,j,D),aw=p(v,j,aq);var N=function(){var aA=" ",A="
";W.all(".aui-diagram-node").each(function(aG){var aB=G,aD=W.Widget.getByNode(aG),aC=aD.get("name"),aF=aD.get("boundingBox"),aE=aF.one(".log")||W.Node.create("").appendTo(aF);aB+=aC+A;aD.get(m).each(function(aH){aB+=aA+"a: "+aH.get("id")+A;aH.get("targets").each(function(aI){var aJ=aI.get(ac);aI.get("node").setContent(aI.get("id"));aB+=aA+aA+"t: "+aJ.get("name")+" (s: "+aI.get("id")+")"+A;});aH.get("sources").each(function(aJ){var aI=aJ.get(ac);aJ.get("node").setContent(aJ.get("id"));aB+=aA+aA+"s: "+aI.get("name")+" (t: "+aJ.get("id")+")"+A;});});aE.setContent(aB);});};var r=W.Component.create({NAME:T,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:w},tmpConnector:{setter:"_setTmpConnector",value:{},validator:w}},EXTENDS:W.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=W.getDoc().on(E,W.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ai,W.bind(A._onNodeClick,A),f+M);A.dropContainer.delegate(Q,W.bind(A._onNodeEdit,A),f+M);A.dropContainer.delegate(n,W.bind(A._onMouseenterAnchors,A),f+aj);A.dropContainer.delegate(K,W.bind(A._onMouseleaveAnchors,A),f+aj);},syncUI:function(){var A=this;W.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new W.Connector(A.get(B));},createField:function(aA){var A=this;if(!ap(aA)){aA.builder=A;aA.viewport=A.get(C);aA=new (A.getFieldClass(aA.type||j))(aA);}aA.set(au,A);return aA;},getFieldClass:function(aB){var A=this;var aA=W.DiagramBuilder.types[aB];if(aA){return aA;}else{W.log("The field type: ["+aB+"] couldn't be found.");return null;}},isFieldsDrag:function(aB){var A=this;var aA=A.fieldsDrag;return(aB===aA.dd);},plotField:function(aA){var A=this;if(!aA.get(av)){aA.render(A.dropContainer);}},unselectAll:function(){var A=this;var aA=A.selectedNode;if(aA){aA.set(aq,false);}A.selectedNode=null;},select:function(aA){var A=this;A.unselectAll();A.stopEditingNode();A.selectedNode=aA.set(aq,true).focus();},startEditingNode:function(aA){var A=this;if(aA){A.stopEditingNode();A.tabView.selectTab(W.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(h,aA.getProperties());aA.get(k).addClass(ak);A.editNode=aA;}},stopEditingNode:function(aB){var A=this;var aA=aB||A.editNode;if(aA){A.tabView.selectTab(W.DiagramBuilder.FIELDS_TAB);
-aA.get(k).removeClass(ak);A.editNode=null;}},_afterKeyEvent:function(aA){var A=this;if(!A.selectedNode||aA.hasModifier()||!aA.isKeyInSet(a,I)){return;}if(aA.isKey(a)){A._onEscKey(aA);}else{if(aA.isKey(I)){A._onDeleteKey(aA);}}aA.halt();},_onCancel:function(aA){var A=this;A.stopEditingNode();},_onDrag:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.get(m).each(function(aD){aD.alignConnectors();});}},_onDragEnd:function(aB){var A=this;var aA=aB.target;if(A.isFieldsDrag(aA)){var aC=W.Widget.getByNode(aA.get(am));aC.set(q,aC.getLeftTop());}},_onDropHit:function(aB){var A=this;var aA=aB.drag;if(A.isAvailableFieldsDrag(aA)){var aD=aA.get(j).getData(F);var aC=A.addField({xy:ab(aA.lastXY,A.dropContainer),type:aD.get(e),fields:[{}]});A.select(aC);}},_onDeleteKey:function(aA){var A=this;A.selectedNode.close();},_onEscKey:function(aA){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aA){var A=this;aA.currentTarget.addClass(Z);},_onMouseleaveAnchors:function(aA){var A=this;aA.currentTarget.removeClass(Z);},_onNodeClick:function(aA){var A=this;var aB=W.Widget.getByNode(aA.currentTarget);A.select(aB);},_onNodeEdit:function(aA){var A=this;if(!aA.target.ancestor(f+b,true)){return;}var aB=W.Widget.getByNode(aA.currentTarget);if(aB){A.startEditingNode(aB);}},_onSave:function(aB){var A=this;var aA=A.editNode;var aC=A.propertyList.get(h);if(aA){ay.each(aC.get(O),function(aD){var aE=aD.get(ag);aA.set(aE.attributeName,aE.value);});A.stopEditingNode(aA);}},_setTmpConnector:function(aA){var A=this;return W.merge({lazyDraw:true,viewport:A.viewport},aA);},_setFieldsDragConfig:function(aB){var A=this;var aA=A.dropContainer;return W.merge({bubbleTargets:A,container:aA,dragConfig:{plugins:[{cfg:{constrain:aA},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll}]},nodes:f+M},aB||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new W.DD.Delegate(A.get(aa));}}});W.DiagramBuilder=r;W.DiagramBuilder.types={};var H=W.Component.create({NAME:s,EXTENDS:W.Overlay,AUGMENTS:[W.FieldSupport]});var az=W.Component.create({NAME:s,UI_ATTRS:[m,i,aq],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:w},builder:{setter:"_setBuilder",validator:J},description:{value:G,validator:ar},height:{value:90},name:{valueFn:function(){var A=this;return A.get(e)+(++W.Env._uidx);},validator:ar},selected:{value:false,validator:ao},strings:{value:{deleteMessage:"Are you sure you want to delete?",description:"Description",name:"Name",type:"Type"}},type:{value:j,validator:ar},controlsToolbar:{setter:"_setControlsToolbar",validator:w,value:null},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:H,buildNodeId:function(A){return s+z+an+z+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(k).addClass(M+ax+A.get(e));A.set("bodyContent",A.get(i));},alignAnchors:function(){var aA=this;var aE=aA.get(m);var aC=aA.get(k).get(g),aD=Math.floor(360/aE.size()),aB=aC.width/2,A=aC.height/2,aG=aC.left+aC.width/2,aF=aC.top+aC.height/2;aE.each(function(aK,aJ){var aI=aK.get(j);var aL=aI.get(g);var aH=aA._getEllipseXY(aB,A,aG,aF,aJ*aD);aI.setXY([aH[0]-aL.width/2,aH[1]-aL.height/2]);aK.alignConnectors();});return aA;},close:function(){var aA=this;var A=aA.getStrings();if(confirm(A[Y])){aA.get(m).each(function(aB){aB.destroy();});aA.destroy();}N();return aA;},createField:function(aB){var A=this;if(!S(aB)){var aA=A.get(au);aB.diagramNode=A;aB.viewport=(aA?aA.get(C):null);aB=new W.Anchor(aB);}return aB;},getLeftTop:function(){var A=this;return ab(A.get(k),A._getContainer());},getProperties:function(){var A=this;var aA=A.getPropertyModel();ay.each(aA,function(aB){aB.value=A.get(aB.attributeName);});return aA;},getPropertyModel:function(){var aA=this;var A=aA.getStrings();return[{attributeName:al,editor:new W.TextAreaCellEditor(),name:A[al]},{attributeName:i,editor:new W.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[i]},{attributeName:e,editor:false,name:A[e]}];},_afterRender:function(aA){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(au).dropContainer||A.get(k).get(d));},_getEllipseXY:function(aA,A,aD,aC,aE){var aB=aE*Math.PI/180;return[aD+aA*Math.cos(aB),aC-A*Math.sin(aB)];},_handleAddAnchorEvent:function(aA){var A=this;A.addField({});},_handleAddTaskEvent:function(aB){var A=this;var aA=A.get(au);var aE=new W.DiagramNode({type:j,xy:[100,100]});aA.addField(aE);var aC=A.addField({});var aD=aE.addField({});aC.connect(aD);},_handleCloseEvent:function(aA){var A=this;A.close();},_onAnchorDrag:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(ae,aB.target.get(am).getCenterXY());},_onAnchorDragEnd:function(aB){var A=this;var aA=A.get(au).tmpConnector.shape;aA.clear();aA.end();},_onAnchorDragStart:function(aB){var A=this;var aA=A.get(au);aA.tmpConnector.set(af,aB.target.get(j).getCenterXY());},_onAnchorDropHit:function(aA){var A=this;var aB=W.Anchor.getAnchorByNode(aA.drag.get(j));var aC=W.Anchor.getAnchorByNode(aA.drop.get(j));aB.connect(aC);N();},_renderControls:function(){var A=this;var aA=A.get(k);A.controlsNode=W.Node.create(A.CONTROLS_TEMPLATE).appendTo(aA);},_renderNodes:function(){var A=this;var aA=A.get(k);A.anchorWrapper=W.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aA);},_renderControlsToolbar:function(aA){var A=this;A.controlsToolbar=new W.Toolbar(A.get(ah)).render(A.controlsNode);},_setBuilder:function(aA){var A=this;A.get(m).each(function(aB){aB.set(C,aA.get(C));});return aA;},_setAnchorsDragConfig:function(aB){var A=this;var aA=A.get(au);return W.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[X],plugins:[{cfg:{constrain:(aA?aA.get(C):null)},fn:W.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:W.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:W.Plugin.DDProxy}]},nodes:f+aj,target:true},aB||{});
-},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new W.DD.Delegate(A.get(R));},_setControlsToolbar:function(aA){var A=this;return W.merge({activeState:false,children:[{handler:W.bind(A._handleAddAnchorEvent,A),icon:V},{handler:W.bind(A._handleAddTaskEvent,A),icon:x},{handler:W.bind(A._handleCloseEvent,A),icon:P}]},aA);},_uiSetFields:function(aA){var A=this;if(A.get(av)){A.alignAnchors();setTimeout(function(){A.anchorsDrag.syncTargets();},50);}},_uiSetName:function(aB){var A=this;var aA=A.get(k);aA.setAttribute(i,W.DiagramNode.buildNodeId(aB));},_uiSetSelected:function(aA){var A=this;A.get(k).toggleClass(aw,aA);if(aA&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aB){var A=this;var aA=A._getContainer().getXY();this._posNode.setXY([aB[0]+aA[0],aB[1]+aA[1]]);}}});W.DiagramNode=az;W.DiagramBuilder.types[j]=W.DiagramNode;W.DiagramNodeTask=W.Component.create({NAME:s,ATTRS:{type:{value:t}},EXTENDS:W.DiagramNode});W.DiagramBuilder.types[t]=W.DiagramNodeTask;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});AUI.add("aui-diagram-builder-connector",function(k){var M=k.Lang,r=M.isArray,v=M.isBoolean,L=M.isNumber,z=M.isObject,h=M.isString,D=k.Array,b=function(A){return(A instanceof k.Anchor);},E=function(A){return(A instanceof k.ArrayList);},y="anchor",F="arrowPoints",C="body",G="boundingBox",N="builder",w="color",n="connector",a="dataAnchor",x="diagram",s="diagramNode",u="height",p="id",I="lazyDraw",j="maxSources",i="maxTargets",J="node",m="p1",l="p2",e="path",q="shape",g="sources",f="targets",B="viewport",c="width",K="wrapper",o=".",t=k.getClassName,d=t(x,N,y,J,K),H=t(x,N,y,J);k.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(T,O,V,A,U,R){var W=this;T.moveTo(O,V);T.lineTo(A,U);var P=Math.atan2(U-V,A-O),S=(A+O)/2,Q=(U+V)/2;W.drawPolygon(T,W.translatePoints(W.rotatePoints(R||W.ARROW_POINTS,P),S,Q));},drawPolygon:function(O,P){var A=this;O.moveTo(P[0][0],P[0][1]);D.each(P,function(R,Q){if(Q>0){O.lineTo(P[Q][0],P[Q][1]);}});O.lineTo(P[0][0],P[0][1]);O.end();},translatePoints:function(P,O,R){var A=this;var Q=[];D.each(P,function(T,S){Q.push([P[S][0]+O,P[S][1]+R]);});return Q;},rotatePoints:function(O,Q){var A=this;var P=[];D.each(O,function(S,R){P.push(A.rotatePoint(Q,O[R][0],O[R][1]));});return P;},rotatePoint:function(O,A,P){return[(A*Math.cos(O))-(P*Math.sin(O)),(A*Math.sin(O))+(P*Math.cos(O))];}};k.Connector=k.Base.create("line",k.Base,[],{graphics:null,shape:null,initializer:function(O){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(I)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var O=A.shape;var Q=A.getCoordinate(A.get(m));var P=A.getCoordinate(A.get(l));O.clear();k.PolygonUtil.drawLineArrow(O,Q[0],Q[1],P[0],P[1],A.get(F));},getCoordinate:function(P){var A=this;var O=A.get(B).getXY();return[P[0]-O[0],P[1]-O[1]];},_initGraphics:function(){var A=this;var O=new k.Graphic({width:A.get(c),height:A.get(u),render:A.get(B)});A.graphics=O;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(q));},_setShape:function(O){var A=this;return k.merge({type:e,stroke:{color:A.get(w),weight:2},fill:{color:A.get(w)}},O);}},{ATTRS:{color:{value:"#666",validator:h},lazyDraw:{value:false,validator:v},viewport:{setter:k.one,value:C},shape:{value:null,setter:"_setShape"},arrowPoints:{value:k.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:r},p2:{value:[0,0],validator:r}}});k.Anchor=k.Base.create("anchor",k.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({targetsChange:A._afterTargetsChange});},addSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O).add(O));},addTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O).add(O));},alignConnectors:function(){var A=this;A.get(f).each(function(O){var P=A.getConnector(O);if(P){P.set(m,A.getCenterXY());P.set(l,O.getCenterXY());}});A.get(g).each(function(O){var P=O.getConnector(A);if(P){P.set(m,O.getCenterXY());P.set(l,A.getCenterXY());}});return A;},destroy:function(){var A=this;A.disconnectTargets();A.disconnectSources();A.get(J).remove();},connect:function(O){var A=this;A.addTarget(O);if(!A.isConnected(O)){var P=O.get(n);P.p1=A.getCenterXY();P.p2=O.getCenterXY();A.connectors[O.get(p)]=new k.Connector(P);}return A;},connectTargets:function(){var A=this;A.get(f).each(k.bind(A.connect,A));return A;},disconnect:function(O){var A=this;A.getConnector(O).destroy();A.removeTarget(O);O.removeSource(A);},disconnectTargets:function(){var A=this;A.get(f).each(function(O){A.disconnect(O);});return A;},disconnectSources:function(){var A=this;A.get(g).each(function(O){O.disconnect(A);});return A;},getCenterXY:function(){var A=this;return A.get(J).getCenterXY();},getConnector:function(O){var A=this;return A.connectors[O.get(p)];},isConnected:function(O){var A=this;return A.connectors.hasOwnProperty(O.get(p));},updateSources:function(O){var A=this;A.set(g,O);return A;},updateTargets:function(O){var A=this;A.set(f,O);return A;},removeSource:function(O){var A=this;return A.updateSources(A.get(g).remove(O));},removeTarget:function(O){var A=this;return A.updateTargets(A.get(f).remove(O));},_afterActiveChange:function(O){var A=this;A._uiSetActive(O.newVal);},_afterTargetsChange:function(O){var A=this;O.prevVal.each(function(P){P.removeSource(A);});O.newVal.each(function(P){P.addSource(A);});},_renderNode:function(){var A=this;var P=A.get(s);var O=P.get(G);A.wrapper=O.one(o+d)||k.Node.create(A.ANCHOR_WRAPPER_TEMPLATE);A.wrapper.appendTo(O).appendChild(A.get(J));},_setConnector:function(O){var A=this;return k.merge({viewport:A.get(B)},O);},_setSources:function(O){var A=this;return A._setAnchors(O);},_setTargets:function(O){var A=this;O=A._setAnchors(O);O.each(function(P){P.addSource(A);});return O;},_setAnchors:function(P){var A=this;
-if(!E(P)){var O=[];k.Array.each(P,function(Q){if(h(Q)){Q=k.Anchor.getAnchorByNode(Q);}O.push(b(Q)?Q:new k.Anchor(Q));});P=new k.ArrayList(O);}return P;},_setNode:function(O){var A=this;var P=A.get(p);return k.one(O).set(p,P).setData(a,A);}},{ATTRS:{diagramNode:{},connector:{setter:"_setConnector",value:{},validator:z},id:{readOnly:true,valueFn:function(){return k.guid();}},maxSources:{value:Infinity,validator:L},maxTargets:{value:Infinity,validator:L},node:{setter:"_setNode",valueFn:function(){var A=this;return k.Node.create(A.NODE_TEMPLATE);}},sources:{value:[],setter:"_setSources",validator:function(A){return r(A)||E(A);}},targets:{value:[],setter:"_setTargets",validator:function(A){return r(A)||E(A);}},viewport:{setter:k.one,value:C}},getAnchorByNode:function(A){return k.one(A).getData(a);}});},"@VERSION@",{requires:["aui-base","arraylist-add","arraylist-filter","json","graphics","dd"],skinnable:true});AUI.add("aui-diagram-builder",function(a){},"@VERSION@",{use:["aui-diagram-builder-base","aui-diagram-builder-impl","aui-diagram-builder-connector"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-diagram-builder-base",function(ae){var U=ae.Lang,d=U.isArray,aq=U.isBoolean,M=U.isNumber,B=U.isObject,au=U.isString,I=function(A){return(A instanceof ae.ArrayList);},S=function(A){return(A instanceof ae.Node);},D=function(A){return(A instanceof ae.AvailableField);},aE=ae.Array,P="maxFields",W="add",k="addNode",aD="auto",N="availableField",R="availableFields",aA="availableFieldsDragConfig",an="base",s="boundingBox",ax="builder",aa="cancel",ar="clearfix",a="container",ac="content",u="contentBox",J="viewport",Q="contentNode",E="createDocumentFragment",z="diagram",F="diagram-builder-base",ab="disk",o="draggable",az="drop",al="dropConfig",Z="dropContainer",ap="field",t="fields",n="fieldsContainer",ao="height",p="helper",X="icon",v="iconClass",ak="id",ag="label",aj="list",r="node",y="nodeSettings",ad="propertyList",ay="rendered",am="save",q="settings",O="tab",b="tabs",e="tabview",G="tabView",L="toolbar",j="toolbarContainer",w=ae.getClassName,aC=" ",g=".",H="$",h="#",aF=w(z,ax,an,az,a),x=w(z,ax,an,J),C=w(z,ax,an,ap),f=w(z,ax,an,t,a),ah=w(z,ax,an,ap,o),c=w(z,ax,an,ap,X),V=w(z,ax,an,ap,ag),m=w(z,ax,an,b,a),Y=w(z,ax,an,b,a,ac),ai=w(z,ax,an,O,W),K=w(z,ax,an,O,q),av=w(z,ax,an,L,a),af=w(p,ar),l=w(X),aw=w(e,ac),aB=w(e,aj);var i=ae.Component.create({NAME:N,ATTRS:{draggable:{value:true,validator:aq},label:{validator:au},iconClass:{validator:au},id:{value:ae.guid(),setter:"_setId",validator:au},node:{valueFn:function(aG){var A=this;if(!S(aG)){aG=ae.Node.create(ae.Lang.sub(A.FIELD_ITEM_TEMPLATE,{iconClass:A.get(v)}));aG.setData(N,A);}return aG;},validator:S,writeOnce:true},type:{value:r,validator:au}},EXTENDS:ae.Base,buildNodeId:function(A){return R+H+ap+H+A;},getAvailableFieldByNode:function(A){return ae.one(A).getData(N);},getAvailableFieldById:function(A){return ae.AvailableField.getAvailableFieldByNode(h+ae.AvailableField.buildNodeId(A));},prototype:{FIELD_ITEM_TEMPLATE:'',VIEWPORT_TEMPLATE:'',fieldsNode:null,propertyList:null,settingsNode:null,tabView:null,toolbar:null,initializer:function(){var A=this;A.publish({cancel:{defaultFn:A._defCancelFn}});A.after({render:A._afterRender});A.after(A._afterUiSetHeight,A,"_uiSetHeight");A.viewport=A.get(J);A.dropContainer=A.get(Z);A.fieldsContainer=A.get(n);A.toolbarContainer=A.get(j);},isAvailableFieldsDrag:function(aH){var A=this;var aG=A.availableFieldsDrag;return(aH===aG.dd);},plotFields:function(){var aG=this;var A=aG.get(t);A.each(function(aH){aG.plotField(aH);});},renderUI:function(){var A=this;A._renderTabs();A._renderViewport();A._uiSetAvailableFields(A.get(R));},syncUI:function(){var A=this;var aG=A.get(u);A._setupDrop();A._setupAvailableFieldsDrag();aG.addClass(af);},_afterActiveTabChange:function(aH){var A=this;var aG=aH.newVal.get(Q);if(A.get(ay)&&(aG===A.settingsNode)){A._renderSettings();}},_afterRender:function(aG){var A=this;A.plotFields();},_afterUiSetHeight:function(aG){var A=this;A.dropContainer.setStyle(ao,M(aG)?aG+A.DEF_UNIT:aG);},_defCancelFn:function(aG){var A=this;A.tabView.selectTab(0);},_handleCancelEvent:function(){var A=this;A.fire(aa);},_handleSaveEvent:function(){var A=this;A.fire(am);},_renderViewport:function(){var aG=this;
+var aH=aG.get(u);var A=aG.viewport;A.appendChild(aG.dropContainer);aH.appendChild(A);},_renderPropertyList:function(){var A=this;if(!A.propertyList){A.propertyList=new ae.PropertyList(A.get(ad)).render(A.settingsNode);A.propertyList.get(s).unselectable();}},_renderSettings:function(){var A=this;A._renderPropertyList();A._renderToolbar();},_renderTabs:function(){var A=this;if(!A.tabView){var aG=new ae.TabView(A.get(G));A.tabView=aG;A.fieldsNode=aG.getTab(0).get(Q);A.settingsNode=aG.getTab(1).get(Q);}},_renderToolbar:function(){var A=this;if(!A.toolbar){A.toolbar=new ae.Toolbar(A.get(L)).render(A.settingsNode);}},_setupDrop:function(){var A=this;A.drop=new ae.DD.Drop(A.get(al));},_setupAvailableFieldsDrag:function(){var A=this;A.availableFieldsDrag=new ae.DD.Delegate(A.get(aA));},_setAvailableFields:function(aH){var aG=this;var A=[];aE.each(aH,function(aJ,aI){A.push(D(aJ)?aJ:new ae.AvailableField(aJ));});return A;},_setDropConfig:function(aG){var A=this;return ae.merge({bubbleTargets:A,groups:[R],node:A.dropContainer},aG||{});},_setAvailableFieldsDragConfig:function(aG){var A=this;return ae.merge({bubbleTargets:A,container:A.get(s),dragConfig:{groups:[R],plugins:[{cfg:{moveOnEnd:false},fn:ae.Plugin.DDProxy}]},nodes:g+ah},aG||{});},_setPropertyList:function(aG){var A=this;return ae.merge({bubbleTargets:A,width:250,scroll:{height:400,width:aD}},aG);},_setTabView:function(aJ){var aG=this;var aI=aG.get(s);var aK=aI.one(g+aB);var aH={after:{activeTabChange:ae.bind(aG._afterActiveTabChange,aG)},boundingBox:aI.one(g+m),contentBox:aI.one(g+Y),bubbleTargets:aG,contentNode:aI.one(g+aw),cssClass:m,listNode:aK,render:aG.get(u)};if(!aK){var A=aG.getStrings();aH.items=[{cssClass:ai,label:A[k]},{cssClass:K,label:A[y]}];}return ae.merge(aH,aJ);},_setToolbar:function(aH){var aG=this;var A=aG.getStrings();return ae.merge({activeState:false,bubbleTargets:aG,children:[{handler:ae.bind(aG._handleSaveEvent,aG),label:A[am],icon:ab},{handler:ae.bind(aG._handleCancelEvent,aG),label:A[aa]}]},aH);},_uiSetAvailableFields:function(aI){var A=this;var aH=A.fieldsNode;if(aH){var aG=ae.getDoc().invoke(E);aE.each(aI,function(aJ){aG.appendChild(aJ.get(r));});aH.setContent(A.fieldsContainer.setContent(aG));}},_uiSetFields:function(aG){var A=this;if(A.get(ay)){A.plotFields();}}}});ae.DiagramBuilderBase=at;},"@VERSION@",{requires:["aui-tabs","aui-property-list","collection","dd"],skinnable:true});AUI.add("aui-diagram-builder-impl",function(aj){var Y=aj.Lang,c=Y.isArray,E=Y.isObject,aJ=Y.isString,aE=Y.isBoolean,aR=aj.Array,U=function(A){return(A instanceof aj.DiagramBuilderBase);},aF=function(A){return(A instanceof aj.DiagramNode);},af=function(A){return(A instanceof aj.Anchor);},ap=function(A,aU){var aT=c(aU)?aU:aU.getXY();var aV=c(A)?A:A.getXY();return aR.map(aV,function(aX,aW){return Math.max(0,aX-aT[aW]);});},an="addAnchor",aM="addAnchorMessage",j="addNode",ar="anchor",ak="anchors",ae="anchorsDragConfig",Q="availableField",V="boolean",o="boundingBox",aN="builder",ac="cancel",ay="click",aK="closeEvent",B="closeMessage",ah="content",J="controls",aw="controlsToolbar",av="data",ad="dblclick",T="delete",al="deleteMessage",aB="description",C="diagram",ag="diagram-builder",aq="diagramNode",x="diagram-node",aC="dragNode",M="editing",y="editEvent",H="editMessage",a="esc",aD="field",q="fields",ao="fieldsDragConfig",p="hover",ax="id",N="keydown",ai="link",aa="max",t="maxSources",r="mouseenter",W="mouseleave",m="name",n="node",au="p1",at="p2",d="parentNode",l="pencil",ab="records",k="recordset",h="region",aO="rendered",F="required",aG="selected",G="shuffle",O="source",aH="sources",aI="string",i="target",I="targets",K="tmpConnector",e="type",L="viewport",aL="wrapper",v="xy",aQ="-",g=".",P="",f="#",D="_",u=aj.getClassName,R=u(C,aN,ar,n,aa,I),am=u(C,aN,ar,p),aA=u(C,aN,ar,n),z=u(C,aN,ar,n,aL),s=u(C,aN,J),X=u(C,n),b=u(C,n,ah),az=u(C,n,M),aP=u(C,n,aG);var Z=function(){var aT=" ",A="
";aj.all(".aui-diagram-node").each(function(aZ){var aU=P,aW=aj.Widget.getByNode(aZ),aV=aW.get("name"),aY=aW.get("boundingBox"),aX=aY.one(".log")||aj.Node.create("").appendTo(aY);aU+=aV+A;aW.get(q).each(function(a0){aU+=aT+"a: "+a0.get("id")+A;a0.get("targets").each(function(a1){var a2=a1.get(aq);a1.get("node").setContent(a1.get("id"));aU+=aT+aT+"t: "+a2.get("name")+" (s: "+a1.get("id")+")"+A;});a0.get("sources").each(function(a2){var a1=a2.get(aq);a2.get("node").setContent(a2.get("id"));aU+=aT+aT+"s: "+a1.get("name")+" (t: "+a2.get("id")+")"+A;});});aX.setContent(aU);});};var w=aj.Component.create({NAME:ag,ATTRS:{fieldsDragConfig:{value:null,setter:"_setFieldsDragConfig",validator:E},tmpConnector:{setter:"_setTmpConnector",value:{},validator:E}},EXTENDS:aj.DiagramBuilderBase,FIELDS_TAB:0,SETTINGS_TAB:1,prototype:{editNode:null,initializer:function(){var A=this;A.on({cancel:A._onCancel,"drag:drag":A._onDrag,"drag:end":A._onDragEnd,"drop:hit":A._onDropHit,save:A._onSave});A.handlerKeyDown=aj.getDoc().on(N,aj.bind(A._afterKeyEvent,A));A.dropContainer.delegate(ay,aj.bind(A._onNodeClick,A),g+X);A.dropContainer.delegate(ad,aj.bind(A._onNodeEdit,A),g+X);A.dropContainer.delegate(r,aj.bind(A._onMouseenterAnchors,A),g+aA);A.dropContainer.delegate(W,aj.bind(A._onMouseleaveAnchors,A),g+aA);},syncUI:function(){var A=this;aj.DiagramBuilder.superclass.syncUI.apply(this,arguments);A._setupFieldsDrag();A.tmpConnector=new aj.Connector(A.get(K));},connect:function(aU,aW){var aT=this;if(aJ(aU)){aU=aj.Widget.getByNode(f+aj.DiagramNode.buildNodeId(aU));}if(aJ(aW)){aW=aj.Widget.getByNode(f+aj.DiagramNode.buildNodeId(aW));}if(aU&&aW){var aV=aU.findAvailableAnchor();var A=aW.findAvailableAnchor();if(aV&&A){aV.connect(A);}}return aT;},connectAll:function(aT){var A=this;aR.each(aT,function(aU){if(aU.hasOwnProperty(O)&&aU.hasOwnProperty(i)){A.connect(aU.source,aU.target);}});return A;},createField:function(aT){var A=this;if(!aF(aT)){aT.builder=A;aT.viewport=A.get(L);aT=new (A.getFieldClass(aT.type||n))(aT);}aT.set(aN,A);return aT;
+},getFieldClass:function(aU){var A=this;var aT=aj.DiagramBuilder.types[aU];if(aT){return aT;}else{aj.log("The field type: ["+aU+"] couldn't be found.");return null;}},isFieldsDrag:function(aU){var A=this;var aT=A.fieldsDrag;return(aU===aT.dd);},plotField:function(aT){var A=this;if(!aT.get(aO)){aT.render(A.dropContainer);}},unselectAll:function(){var A=this;var aT=A.selectedNode;if(aT){aT.set(aG,false);}A.selectedNode=null;},select:function(aT){var A=this;A.unselectAll();A.selectedNode=aT.set(aG,true).focus();},startEditingNode:function(aT){var A=this;if(aT){A.stopEditingNode();A.tabView.selectTab(aj.DiagramBuilder.SETTINGS_TAB);A.propertyList.set(k,aT.getProperties());aT.get(o).addClass(az);A.editNode=aT;}},stopEditingNode:function(aU){var A=this;var aT=aU||A.editNode;if(aT){A.tabView.selectTab(aj.DiagramBuilder.FIELDS_TAB);aT.get(o).removeClass(az);A.editNode=null;}},_afterKeyEvent:function(aT){var A=this;if(!A.selectedNode||aT.hasModifier()||!aT.isKeyInSet(a,T)){return;}if(aT.isKey(a)){A._onEscKey(aT);}else{if(aT.isKey(T)){A._onDeleteKey(aT);}}aT.halt();},_onCancel:function(aT){var A=this;A.stopEditingNode();},_onDrag:function(aU){var A=this;var aT=aU.target;if(A.isFieldsDrag(aT)){var aV=aj.Widget.getByNode(aT.get(aC));aV.get(q).each(function(aW){aW.alignConnectors();});}},_onDragEnd:function(aU){var A=this;var aT=aU.target;if(A.isFieldsDrag(aT)){var aV=aj.Widget.getByNode(aT.get(aC));aV.set(v,aV.getLeftTop());}},_onDropHit:function(aU){var A=this;var aT=aU.drag;if(A.isAvailableFieldsDrag(aT)){var aW=aT.get(n).getData(Q);var aV=A.addField({xy:ap(aT.lastXY,A.dropContainer),type:aW.get(e),fields:[{}]});A.select(aV);}},_onDeleteKey:function(aT){var A=this;var aU=A.selectedNode;if(!aU.get(F)){aU.close();}},_onEscKey:function(aT){var A=this;A.unselectAll();A.stopEditingNode();},_onMouseenterAnchors:function(aT){var A=this;aT.currentTarget.addClass(am);},_onMouseleaveAnchors:function(aT){var A=this;aT.currentTarget.removeClass(am);},_onNodeClick:function(aT){var A=this;var aU=aj.Widget.getByNode(aT.currentTarget);A.select(aU);},_onNodeEdit:function(aT){var A=this;if(!aT.target.ancestor(g+b,true)){return;}var aU=aj.Widget.getByNode(aT.currentTarget);if(aU){A.startEditingNode(aU);}},_onSave:function(aU){var A=this;var aT=A.editNode;var aV=A.propertyList.get(k);if(aT){aR.each(aV.get(ab),function(aW){var aX=aW.get(av);aT.set(aX.attributeName,aX.value);});A.stopEditingNode(aT);}},_setTmpConnector:function(aT){var A=this;return aj.merge({lazyDraw:true,viewport:A.viewport},aT);},_setFieldsDragConfig:function(aU){var A=this;var aT=A.dropContainer;return aj.merge({bubbleTargets:A,container:aT,dragConfig:{plugins:[{cfg:{constrain:aT},fn:aj.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:aj.Plugin.DDWinScroll}]},nodes:g+X},aU||{});},_setupFieldsDrag:function(){var A=this;A.fieldsDrag=new aj.DD.Delegate(A.get(ao));}}});aj.DiagramBuilder=w;aj.DiagramBuilder.types={};var S=aj.Component.create({NAME:x,EXTENDS:aj.Overlay,AUGMENTS:[aj.FieldSupport]});var aS=aj.Component.create({NAME:x,UI_ATTRS:[q,m,F,aG],ATTRS:{anchorsDragConfig:{value:null,setter:"_setAnchorsDragConfig",validator:E},builder:{setter:"_setBuilder",validator:U},required:{value:false,validator:aE},description:{value:P,validator:aJ},height:{value:90},name:{valueFn:function(){var A=this;return A.get(e)+(++aj.Env._uidx);},validator:aJ},selected:{value:false,validator:aE},strings:{value:{addAnchorMessage:"Add Anchor",closeMessage:"Close",deleteMessage:"Are you sure you want to delete?",description:"Description",editMessage:"Edit",name:"Name",type:"Type"}},type:{value:n,validator:aJ},controlsToolbar:{validator:E,valueFn:"_valueControlsToolbar"},width:{value:90},zIndex:{value:100},tabIndex:{value:1}},EXTENDS:S,buildNodeId:function(A){return aq+D+aD+D+A;},prototype:{ANCHOR_WRAPPER_TEMPLATE:'',CONTROLS_TEMPLATE:'',initializer:function(){var A=this;A._renderNodes();A._setupAnchorsDrag();A.after({render:A._afterRender});A.on({"drag:drag":A._onAnchorDrag,"drag:end":A._onAnchorDragEnd,"drag:start":A._onAnchorDragStart,"drop:hit":A._onAnchorDropHit});A.get(o).addClass(X+aQ+A.get(e));A.set("bodyContent",A.get(m));},alignAnchors:function(){var aT=this;var aX=aT.get(q);var aV=aT.get(o).get(h),aW=Math.floor(360/aX.size()),aU=aV.width/2,A=aV.height/2,aZ=aV.left+aV.width/2,aY=aV.top+aV.height/2;aX.each(function(a3,a2){var a1=a3.get(n);var a4=a1.get(h);var a0=aT._getEllipseXY(aU,A,aZ,aY,a2*aW);a1.setXY([a0[0]-a4.width/2,a0[1]-a4.height/2]);a3.alignConnectors();});return aT;},close:function(){var aT=this;var A=aT.getStrings();if(confirm(A[al])){aT.get(q).each(function(aU){aU.destroy();});aT.destroy();}Z();return aT;},createField:function(aU){var A=this;if(!af(aU)){var aT=A.get(aN);aU.diagramNode=A;aU.viewport=(aT?aT.get(L):null);aU=new aj.Anchor(aU);}return aU;},findAvailableAnchor:function(){var A=this;var aT=null;A.get(q).some(function(aU){if(!aU.hasConnection()){aT=aU;return true;}});if(!aT){aT=A.addField({});}return aT;},getConnectionNode:function(){var A=this;return new aj.DiagramNode({xy:[100,100]});},getLeftTop:function(){var A=this;return ap(A.get(o),A._getContainer());},getProperties:function(){var A=this;var aT=A.getPropertyModel();aR.each(aT,function(aW){var aV=A.get(aW.attributeName),aU=Y.type(aV);if(aU===V||aU===aI){aV=String(aV);}aW.value=aV;});return aT;},getPropertyModel:function(){var aT=this;var A=aT.getStrings();return[{attributeName:aB,editor:new aj.TextAreaCellEditor(),name:A[aB]},{attributeName:m,editor:new aj.TextCellEditor({validator:{rules:{value:{required:true}}}}),name:A[m]},{attributeName:e,editor:false,name:A[e]}];},syncDragTargets:function(){var A=this;A.anchorsDrag.syncTargets();},syncDropTargets:function(aT){var A=this;A.get(q).each(function(aV){var aU=aj.DD.DDM.getDrop(aV.get(n));if(aU){if(aV.get(aH).size()===aV.get(t)){aU.removeFromGroup(ak);}else{aU.addToGroup(ak);}}});},_afterRender:function(aT){var A=this;A.alignAnchors();A._renderControls();},_getContainer:function(){var A=this;return(A.get(aN).dropContainer||A.get(o).get(d));
+},_getEllipseXY:function(aT,A,aW,aV,aX){var aU=aX*Math.PI/180;return[aW+aT*Math.cos(aU),aV-A*Math.sin(aU)];},_handleAddAnchorEvent:function(aT){var A=this;A.addField({});},_handleAddNodeEvent:function(aU){var A=this;var aT=A.get(aN);var aV=A.findAvailableAnchor();if(aV){var aW=A.getConnectionNode();aT.addField(aW);aV.connect(aW.addField({}));}},_handleEditEvent:function(aT){var A=this;A.get(aN).startEditingNode(A);},_handleCloseEvent:function(aT){var A=this;if(!A.get(F)){A.close();}},_onAnchorDrag:function(aU){var A=this;var aT=A.get(aN);aT.tmpConnector.set(at,aU.target.get(aC).getCenterXY());},_onAnchorDragEnd:function(aU){var A=this;var aT=A.get(aN).tmpConnector.shape;aT.clear();aT.end();},_onAnchorDragStart:function(aU){var A=this;var aT=A.get(aN);aT.tmpConnector.set(au,aU.target.get(n).getCenterXY());},_onAnchorDropHit:function(aT){var A=this;var aU=aj.Anchor.getAnchorByNode(aT.drag.get(n));var aV=aj.Anchor.getAnchorByNode(aT.drop.get(n));aU.connect(aV);Z();},_renderControls:function(){var A=this;var aT=A.get(o);A.controlsNode=aj.Node.create(A.CONTROLS_TEMPLATE).appendTo(aT);},_renderNodes:function(){var A=this;var aT=A.get(o);A.anchorWrapper=aj.Node.create(A.ANCHOR_WRAPPER_TEMPLATE).appendTo(aT);},_renderControlsToolbar:function(aT){var A=this;A.controlsToolbar=new aj.Toolbar(A.get(aw)).render(A.controlsNode);A._uiSetRequired(A.get(F));},_setBuilder:function(aT){var A=this;A.get(q).each(function(aU){aU.set(L,aT.get(L));});return aT;},_setAnchorsDragConfig:function(aU){var A=this;var aT=A.get(aN);return aj.merge({bubbleTargets:A,container:A.anchorWrapper,dragConfig:{groups:[ak],plugins:[{cfg:{constrain:(aT?aT.get(L):null)},fn:aj.Plugin.DDConstrained},{cfg:{scrollDelay:150},fn:aj.Plugin.DDWinScroll},{cfg:{moveOnEnd:false},fn:aj.Plugin.DDProxy}]},nodes:g+aA,target:true},aU||{});},_setupAnchorsDrag:function(){var A=this;A.anchorsDrag=new aj.DD.Delegate(A.get(ae));A.anchorsDrag.dd.addInvalid(g+R);},_uiSetFields:function(aT){var A=this;if(A.get(aO)){A.alignAnchors();A.syncDragTargets();A.syncDropTargets();}},_uiSetName:function(aU){var A=this;var aT=A.get(o);aT.set(ax,aj.DiagramNode.buildNodeId(aU));},_uiSetRequired:function(aV){var aU=this;var aT=aU.getStrings();var A=aU.controlsToolbar;if(A){if(aV){A.remove(aK);}else{A.add({handler:aj.bind(aU._handleCloseEvent,aU),icon:ac,id:aK,title:aT[B]});}}},_uiSetSelected:function(aT){var A=this;A.get(o).toggleClass(aP,aT);if(aT&&!A.controlsToolbar){A._renderControlsToolbar();}},_uiSetXY:function(aU){var A=this;var aT=A._getContainer().getXY();this._posNode.setXY([aU[0]+aT[0],aU[1]+aT[1]]);},_valueControlsToolbar:function(aU){var aT=this;var A=aT.getStrings();return{activeState:false,children:[{handler:aj.bind(aT._handleEditEvent,aT),icon:l,id:y,title:A[H]},{handler:aj.bind(aT._handleAddAnchorEvent,aT),icon:ai,id:an,title:A[aM]},{handler:aj.bind(aT._handleAddNodeEvent,aT),icon:G,id:j},{handler:aj.bind(aT._handleCloseEvent,aT),icon:ac,id:aK,title:A[B]}]};}}});aj.DiagramNode=aS;aj.DiagramBuilder.types[n]=aj.DiagramNode;},"@VERSION@",{requires:["aui-diagram-builder-base","overlay"],skinnable:true});AUI.add("aui-diagram-builder-connector",function(m){var Q=m.Lang,u=Q.isArray,y=Q.isBoolean,P=Q.isNumber,D=Q.isObject,j=Q.isString,G=m.Array,d=function(A){return(A instanceof m.Anchor);},H=function(A){return(A instanceof m.ArrayList);},p=function(A){return(A instanceof m.DiagramNode);},C="anchor",I="arrowPoints",F="body",J="boundingBox",R="builder",z="color",q="connector",a="dataAnchor",B="diagram",v="diagramNode",x="height",s="id",L="lazyDraw",M="max",l="maxSources",k="maxTargets",O="node",o="p1",n="p2",g="path",t="shape",i="sources",h="targets",E="viewport",c="width",N="wrapper",r=".",w=m.getClassName,b=w(B,R,C,O,M,h),f=w(B,R,C,O,M,i),e=w(B,R,C,O,N),K=w(B,R,C,O);m.PolygonUtil={ARROW_POINTS:[[-12,-6],[-8,0],[-12,6],[6,0]],drawLineArrow:function(X,S,Z,A,Y,V){var aa=this;X.moveTo(S,Z);X.lineTo(A,Y);var T=Math.atan2(Y-Z,A-S),W=(A+S)/2,U=(Y+Z)/2;aa.drawPolygon(X,aa.translatePoints(aa.rotatePoints(V||aa.ARROW_POINTS,T),W,U));},drawPolygon:function(S,T){var A=this;S.moveTo(T[0][0],T[0][1]);G.each(T,function(V,U){if(U>0){S.lineTo(T[U][0],T[U][1]);}});S.lineTo(T[0][0],T[0][1]);S.end();},translatePoints:function(T,S,V){var A=this;var U=[];G.each(T,function(X,W){U.push([T[W][0]+S,T[W][1]+V]);});return U;},rotatePoints:function(S,U){var A=this;var T=[];G.each(S,function(W,V){T.push(A.rotatePoint(U,S[V][0],S[V][1]));});return T;},rotatePoint:function(S,A,T){return[(A*Math.cos(S))-(T*Math.sin(S)),(A*Math.sin(S))+(T*Math.cos(S))];}};m.Connector=m.Base.create("line",m.Base,[],{graphics:null,shape:null,initializer:function(S){var A=this;A.after({p1Change:A.draw,p2Change:A.draw});A._initGraphics();A._initShapes();if(!A.get(L)){A.draw();}},destroy:function(){var A=this;A.graphics.destroy();},draw:function(){var A=this;var S=A.shape;var U=A.getCoordinate(A.get(o));var T=A.getCoordinate(A.get(n));S.clear();m.PolygonUtil.drawLineArrow(S,U[0],U[1],T[0],T[1],A.get(I));},getCoordinate:function(T){var A=this;var S=A.get(E).getXY();return[T[0]-S[0],T[1]-S[1]];},_initGraphics:function(){var A=this;var S=new m.Graphic({width:A.get(c),height:A.get(x),render:A.get(E)});A.graphics=S;},_initShapes:function(){var A=this;A.shape=A.graphics.getShape(A.get(t));},_setShape:function(S){var A=this;return m.merge({type:g,stroke:{color:A.get(z),weight:2},fill:{color:A.get(z)}},S);}},{ATTRS:{color:{value:"#666",validator:j},lazyDraw:{value:false,validator:y},viewport:{setter:m.one,value:F},shape:{value:null,setter:"_setShape"},arrowPoints:{value:m.PolygonUtil.ARROW_POINTS},p1:{value:[0,0],validator:u},p2:{value:[0,0],validator:u}}});m.Anchor=m.Base.create("anchor",m.Base,[],{ANCHOR_WRAPPER_TEMPLATE:'',NODE_TEMPLATE:'',connectors:null,initializer:function(){var A=this;A.connectors={};A._renderNode();A.connectTargets();A.after({sourcesChange:A._afterSourcesChange,targetsChange:A._afterTargetsChange});A._uiSetMaxTargets(A.get(k));},addSource:function(S){var A=this;
+if(A.get(i).size()Alloy - diagram-builder-base Demo
- ');
- NodeList._tempNode = tmp;
- }
+ return ok;
+ }
- tmp._node = node;
- tmp._stateProxy = node;
- return tmp;
-};
+ if (!type || !fn || !fn.call) {
+ return Event.purgeElement(el, false, type);
+ }
-Y.mix(NodeList.prototype, {
- /**
- * Retrieves the Node instance at the given index.
- * @method item
- *
- * @param {Number} index The index of the target Node.
- * @return {Node} The Node instance at the given index.
- */
- item: function(index) {
- return Y.one((this._nodes || [])[index]);
- },
+ id = 'event:' + Y.stamp(el) + type;
+ ce = _wrappers[id];
- /**
- * Applies the given function to each Node in the NodeList.
- * @method each
- * @param {Function} fn The function to apply. It receives 3 arguments:
- * the current node instance, the node's index, and the NodeList instance
- * @param {Object} context optional An optional context to apply the function with
- * Default context is the current Node instance
- * @chainable
- */
- each: function(fn, context) {
- var instance = this;
- Y.Array.each(this._nodes, function(node, index) {
- node = Y.one(node);
- return fn.call(context || node, node, index, instance);
- });
- return instance;
- },
+ if (ce) {
+ return ce.detach(fn);
+ } else {
+ return false;
+ }
- batch: function(fn, context) {
- var nodelist = this;
+ },
- Y.Array.each(this._nodes, function(node, index) {
- var instance = Y.Node._instances[node[UID]];
- if (!instance) {
- instance = NodeList._getTempNode(node);
- }
+ /**
+ * Finds the event in the window object, the caller's arguments, or
+ * in the arguments of another method in the callstack. This is
+ * executed automatically for events registered through the event
+ * manager, so the implementer should not normally need to execute
+ * this function at all.
+ * @method getEvent
+ * @param {Event} e the event parameter from the handler
+ * @param {HTMLElement} el the element the listener was attached to
+ * @return {Event} the event
+ * @static
+ */
+ getEvent: function(e, el, noFacade) {
+ var ev = e || win.event;
- return fn.call(context || instance, instance, index, nodelist);
- });
- return nodelist;
- },
+ return (noFacade) ? ev :
+ new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
+ },
- /**
- * Executes the function once for each node until a true value is returned.
- * @method some
- * @param {Function} fn The function to apply. It receives 3 arguments:
- * the current node instance, the node's index, and the NodeList instance
- * @param {Object} context optional An optional context to execute the function from.
- * Default context is the current Node instance
- * @return {Boolean} Whether or not the function returned true for any node.
- */
- some: function(fn, context) {
- var instance = this;
- return Y.Array.some(this._nodes, function(node, index) {
- node = Y.one(node);
- context = context || node;
- return fn.call(context, node, index, instance);
- });
- },
+ /**
+ * Generates an unique ID for the element if it does not already
+ * have one.
+ * @method generateId
+ * @param el the element to create the id for
+ * @return {string} the resulting id of the element
+ * @static
+ */
+ generateId: function(el) {
+ return Y.DOM.generateID(el);
+ },
- /**
- * Creates a documenFragment from the nodes bound to the NodeList instance
- * @method toFrag
- * @return Node a Node instance bound to the documentFragment
- */
- toFrag: function() {
- return Y.one(Y.DOM._nl2frag(this._nodes));
- },
+ /**
+ * We want to be able to use getElementsByTagName as a collection
+ * to attach a group of events to. Unfortunately, different
+ * browsers return different types of collections. This function
+ * tests to determine if the object is array-like. It will also
+ * fail if the object is an array, but is empty.
+ * @method _isValidCollection
+ * @param o the object to test
+ * @return {boolean} true if the object is array-like and populated
+ * @deprecated was not meant to be used directly
+ * @static
+ * @private
+ */
+ _isValidCollection: shouldIterate,
- /**
- * Returns the index of the node in the NodeList instance
- * or -1 if the node isn't found.
- * @method indexOf
- * @param {Y.Node || DOMNode} node the node to search for
- * @return {Int} the index of the node value or -1 if not found
- */
- indexOf: function(node) {
- return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
- },
+ /**
+ * hook up any deferred listeners
+ * @method _load
+ * @static
+ * @private
+ */
+ _load: function(e) {
+ if (!_loadComplete) {
+ // Y.log('Load Complete', 'info', 'event');
+ _loadComplete = true;
- /**
- * Filters the NodeList instance down to only nodes matching the given selector.
- * @method filter
- * @param {String} selector The selector to filter against
- * @return {NodeList} NodeList containing the updated collection
- * @see Selector
- */
- filter: function(selector) {
- return Y.all(Y.Selector.filter(this._nodes, selector));
- },
+ // Just in case DOMReady did not go off for some reason
+ // E._ready();
+ if (Y.fire) {
+ Y.fire(EVENT_READY);
+ }
+ // Available elements may not have been detected before the
+ // window load event fires. Try to find them now so that the
+ // the user is more likely to get the onAvailable notifications
+ // before the window load notification
+ Event._poll();
+ }
+ },
- /**
- * Creates a new NodeList containing all nodes at every n indices, where
- * remainder n % index equals r.
- * (zero-based index).
- * @method modulus
- * @param {Int} n The offset to use (return every nth node)
- * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
- * @return {NodeList} NodeList containing the updated collection
- */
- modulus: function(n, r) {
- r = r || 0;
- var nodes = [];
- NodeList.each(this, function(node, i) {
- if (i % n === r) {
- nodes.push(node);
+ /**
+ * Polling function that runs before the onload event fires,
+ * attempting to attach to DOM Nodes as soon as they are
+ * available
+ * @method _poll
+ * @static
+ * @private
+ */
+ _poll: function() {
+ if (Event.locked) {
+ return;
}
- });
- return Y.all(nodes);
- },
+ if (Y.UA.ie && !YUI.Env.DOMReady) {
+ // Hold off if DOMReady has not fired and check current
+ // readyState to protect against the IE operation aborted
+ // issue.
+ Event.startInterval();
+ return;
+ }
- /**
- * Creates a new NodeList containing all nodes at odd indices
- * (zero-based index).
- * @method odd
- * @return {NodeList} NodeList containing the updated collection
- */
- odd: function() {
- return this.modulus(2, 1);
- },
+ Event.locked = true;
- /**
- * Creates a new NodeList containing all nodes at even indices
- * (zero-based index), including zero.
- * @method even
- * @return {NodeList} NodeList containing the updated collection
- */
- even: function() {
- return this.modulus(2);
- },
+ // Y.log.debug("poll");
+ // keep trying until after the page is loaded. We need to
+ // check the page load state prior to trying to bind the
+ // elements so that we can be certain all elements have been
+ // tested appropriately
+ var i, len, item, el, notAvail, executeItem,
+ tryAgain = !_loadComplete;
- destructor: function() {
- },
+ if (!tryAgain) {
+ tryAgain = (_retryCount > 0);
+ }
- /**
- * Reruns the initial query, when created using a selector query
- * @method refresh
- * @chainable
- */
- refresh: function() {
- var doc,
- nodes = this._nodes,
- query = this._query,
- root = this._queryRoot;
+ // onAvailable
+ notAvail = [];
- if (query) {
- if (!root) {
- if (nodes && nodes[0] && nodes[0].ownerDocument) {
- root = nodes[0].ownerDocument;
+ executeItem = function (el, item) {
+ var context, ov = item.override;
+ if (item.compat) {
+ if (item.override) {
+ if (ov === true) {
+ context = item.obj;
+ } else {
+ context = ov;
+ }
+ } else {
+ context = el;
+ }
+ item.fn.call(context, item.obj);
+ } else {
+ context = item.obj || Y.one(el);
+ item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
+ }
+ };
+
+ // onAvailable
+ for (i=0,len=_avail.length; i
+ * Plug.Host's protected _initPlugins and _destroyPlugins + * methods should be invoked by the host class at the appropriate point in the host's lifecyle. + *
+ * + * @class Plugin.Host */ - 'scrollIntoView', - /** - * Passes through to DOM method. - * @method getElementsByTagName - * @param {String} tagName The tagName to collect - * @return {NodeList} A NodeList representing the HTMLCollection - */ - 'getElementsByTagName', + var L = Y.Lang; - /** - * Passes through to DOM method. - * @method focus - * @chainable - */ - 'focus', + function PluginHost() { + this._plugins = {}; + } - /** - * Passes through to DOM method. - * @method blur - * @chainable - */ - 'blur', + PluginHost.prototype = { - /** - * Passes through to DOM method. - * Only valid on FORM elements - * @method submit - * @chainable - */ - 'submit', + /** + * Adds a plugin to the host object. This will instantiate the + * plugin and attach it to the configured namespace on the host object. + * + * @method plug + * @chainable + * @param P {Function | Object |Array} Accepts the plugin class, or an + * object with a "fn" property specifying the plugin class and + * a "cfg" property specifying the configuration for the Plugin. + *+ * Additionally an Array can also be passed in, with the above function or + * object values, allowing the user to add multiple plugins in a single call. + *
+ * @param config (Optional) If the first argument is the plugin class, the second argument + * can be the configuration for the plugin. + * @return {Base} A reference to the host object + */ + plug: function(Plugin, config) { + var i, ln, ns; - /** - * Passes through to DOM method. - * Only valid on FORM elements - * @method reset - * @chainable - */ - 'reset', + if (L.isArray(Plugin)) { + for (i = 0, ln = Plugin.length; i < ln; i++) { + this.plug(Plugin[i]); + } + } else { + if (Plugin && !L.isFunction(Plugin)) { + config = Plugin.cfg; + Plugin = Plugin.fn; + } - /** - * Passes through to DOM method. - * @method select - * @chainable - */ - 'select', - - /** - * Passes through to DOM method. - * Only valid on TABLE elements - * @method createCaption - * @chainable - */ - 'createCaption' - -], function(method) { - Y.log('adding: ' + method, 'info', 'node'); - Y.Node.prototype[method] = function(arg1, arg2, arg3) { - var ret = this.invoke(method, arg1, arg2, arg3); - return ret; - }; -}); - -Y.Node.importMethod(Y.DOM, [ - /** - * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy. - * @method contains - * @param {Node | HTMLElement} needle The possible node or descendent - * @return {Boolean} Whether or not this node is the needle its ancestor - */ - 'contains', - /** - * Allows setting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method setAttribute - * @for Node - * @for NodeList - * @chainable - * @param {string} name The attribute name - * @param {string} value The value to set - */ - 'setAttribute', - /** - * Allows getting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method getAttribute - * @for Node - * @for NodeList - * @param {string} name The attribute name - * @return {string} The attribute value - */ - 'getAttribute', - - /** - * Wraps the given HTML around the node. - * @method wrap - * @param {String} html The markup to wrap around the node. - * @chainable - */ - 'wrap', - - /** - * Removes the node's parent node. - * @method unwrap - * @chainable - */ - 'unwrap', + // Plugin should be fn by now + if (Plugin && Plugin.NS) { + ns = Plugin.NS; + + config = config || {}; + config.host = this; + + if (this.hasPlugin(ns)) { + // Update config + this[ns].setAttrs(config); + } else { + // Create new instance + this[ns] = new Plugin(config); + this._plugins[ns] = Plugin; + } + } + else { Y.log("Attempt to plug in an invalid plugin. Host:" + this + ", Plugin:" + Plugin); } + } + return this; + }, - /** - * Applies a unique ID to the node if none exists - * @method generateID - * @return {String} The existing or generated ID - */ - 'generateID' -]); + /** + * Removes a plugin from the host object. This will destroy the + * plugin instance and delete the namepsace from the host object. + * + * @method unplug + * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, + * all registered plugins are unplugged. + * @return {Base} A reference to the host object + * @chainable + */ + unplug: function(plugin) { + var ns = plugin, + plugins = this._plugins; + + if (plugin) { + if (L.isFunction(plugin)) { + ns = plugin.NS; + if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { + ns = null; + } + } + + if (ns) { + if (this[ns]) { + this[ns].destroy(); + delete this[ns]; + } + if (plugins[ns]) { + delete plugins[ns]; + } + } + } else { + for (ns in this._plugins) { + if (this._plugins.hasOwnProperty(ns)) { + this.unplug(ns); + } + } + } + return this; + }, -Y.NodeList.importMethod(Y.Node.prototype, [ -/** - * Allows getting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method getAttribute - * @see Node - * @for NodeList - * @param {string} name The attribute name - * @return {string} The attribute value - */ + /** + * Determines if a plugin has plugged into this host. + * + * @method hasPlugin + * @param {String} ns The plugin's namespace + * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise. + */ + hasPlugin : function(ns) { + return (this._plugins[ns] && this[ns]); + }, - 'getAttribute', -/** - * Allows setting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method setAttribute - * @see Node - * @for NodeList - * @chainable - * @param {string} name The attribute name - * @param {string} value The value to set - */ - 'setAttribute', - -/** - * Allows for removing attributes on DOM nodes. - * This passes through to the DOM node, allowing for custom attributes. - * @method removeAttribute - * @see Node - * @for NodeList - * @param {string} name The attribute to remove - */ - 'removeAttribute', -/** - * Removes the parent node from node in the list. - * @method unwrap - * @chainable - */ - 'unwrap', -/** - * Wraps the given HTML around each node. - * @method wrap - * @param {String} html The markup to wrap around the node. - * @chainable - */ - 'wrap', + /** + * Initializes static plugins registered on the host (using the + * Base.plug static method) and any plugins passed to the + * instance through the "plugins" configuration property. + * + * @method _initPlugins + * @param {Config} config The configuration object with property name/value pairs. + * @private + */ + + _initPlugins: function(config) { + this._plugins = this._plugins || {}; -/** - * Applies a unique ID to each node if none exists - * @method generateID - * @return {String} The existing or generated ID - */ - 'generateID' -]); -(function(Y) { - var methods = [ - /** - * Determines whether each node has the given className. - * @method hasClass - * @for Node - * @param {String} className the class name to search for - * @return {Boolean} Whether or not the element has the specified class - */ - 'hasClass', + if (this._initConfigPlugins) { + this._initConfigPlugins(config); + } + }, - /** - * Adds a class name to each node. - * @method addClass - * @param {String} className the class name to add to the node's class attribute - * @chainable - */ - 'addClass', + /** + * Unplugs and destroys all plugins on the host + * @method _destroyPlugins + * @private + */ + _destroyPlugins: function() { + this.unplug(); + } + }; - /** - * Removes a class name from each node. - * @method removeClass - * @param {String} className the class name to remove from the node's class attribute - * @chainable - */ - 'removeClass', + Y.namespace("Plugin").Host = PluginHost; - /** - * Replace a class with another class for each node. - * If no oldClassName is present, the newClassName is simply added. - * @method replaceClass - * @param {String} oldClassName the class name to be replaced - * @param {String} newClassName the class name that will be replacing the old class name - * @chainable - */ - 'replaceClass', - /** - * If the className exists on the node it is removed, if it doesn't exist it is added. - * @method toggleClass - * @param {String} className the class name to be toggled - * @param {Boolean} force Option to force adding or removing the class. - * @chainable - */ - 'toggleClass' - ]; +}, '3.4.0' ,{requires:['yui-base']}); +YUI.add('pluginhost-config', function(Y) { - Y.Node.importMethod(Y.DOM, methods); /** - * Determines whether each node has the given className. - * @method hasClass - * @see Node.hasClass - * @for NodeList - * @param {String} className the class name to search for - * @return {Array} An array of booleans for each node bound to the NodeList. + * Adds pluginhost constructor configuration and static configuration support + * @submodule pluginhost-config */ - /** - * Adds a class name to each node. - * @method addClass - * @see Node.addClass - * @param {String} className the class name to add to the node's class attribute - * @chainable - */ + var PluginHost = Y.Plugin.Host, + L = Y.Lang; /** - * Removes a class name from each node. - * @method removeClass - * @see Node.removeClass - * @param {String} className the class name to remove from the node's class attribute - * @chainable + * A protected initialization method, used by the host class to initialize + * plugin configurations passed the constructor, through the config object. + * + * Host objects should invoke this method at the appropriate time in their + * construction lifecycle. + * + * @method _initConfigPlugins + * @param {Object} config The configuration object passed to the constructor + * @protected + * @for Plugin.Host */ + PluginHost.prototype._initConfigPlugins = function(config) { - /** - * Replace a class with another class for each node. - * If no oldClassName is present, the newClassName is simply added. - * @method replaceClass - * @see Node.replaceClass - * @param {String} oldClassName the class name to be replaced - * @param {String} newClassName the class name that will be replacing the old class name - * @chainable - */ - - /** - * If the className exists on the node it is removed, if it doesn't exist it is added. - * @method toggleClass - * @see Node.toggleClass - * @param {String} className the class name to be toggled - * @chainable - */ - Y.NodeList.importMethod(Y.Node.prototype, methods); -})(Y); - -if (!Y.config.doc.documentElement.hasAttribute) { // IE < 8 - Y.Node.prototype.hasAttribute = function(attr) { - if (attr === 'value') { - if (this.get('value') !== "") { // IE < 8 fails to populate specified when set in HTML - return true; - } - } - return !!(this._node.attributes[attr] && - this._node.attributes[attr].specified); - }; -} - -// IE throws an error when calling focus() on an element that's invisible, not -// displayed, or disabled. -Y.Node.prototype.focus = function () { - try { - this._node.focus(); - } catch (e) { - Y.log('error focusing node: ' + e.toString(), 'error', 'node'); - } - - return this; -}; - -// IE throws error when setting input.type = 'hidden', -// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden' -Y.Node.ATTRS.type = { - setter: function(val) { - if (val === 'hidden') { - try { - this._node.type = 'hidden'; - } catch(e) { - this.setStyle('display', 'none'); - this._inputType = 'hidden'; - } - } else { - try { // IE errors when changing the type from "hidden' - this._node.type = val; - } catch (e) { - Y.log('error setting type: ' + val, 'info', 'node'); - } - } - return val; - }, - - getter: function() { - return this._inputType || this._node.type; - }, - - _bypassProxy: true // don't update DOM when using with Attribute -}; - -if (Y.config.doc.createElement('form').elements.nodeType) { - // IE: elements collection is also FORM node which trips up scrubVal. - Y.Node.ATTRS.elements = { - getter: function() { - return this.all('input, textarea, button, select'); - } - }; -} - -Y.mix(Y.Node.ATTRS, { - offsetHeight: { - setter: function(h) { - Y.DOM.setHeight(this._node, h); - return h; - }, - - getter: function() { - return this._node.offsetHeight; - } - }, - - offsetWidth: { - setter: function(w) { - Y.DOM.setWidth(this._node, w); - return w; - }, - - getter: function() { - return this._node.offsetWidth; - } - } -}); - -Y.mix(Y.Node.prototype, { - sizeTo: function(w, h) { - var node; - if (arguments.length < 2) { - node = Y.one(w); - w = node.get('offsetWidth'); - h = node.get('offsetHeight'); - } - - this.setAttrs({ - offsetWidth: w, - offsetHeight: h - }); - } -}); -var Y_NodeList = Y.NodeList, - ArrayProto = Array.prototype, - ArrayMethods = { - /** Returns a new NodeList combining the given NodeList(s) - * @for NodeList - * @method concat - * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to - * concatenate to the resulting NodeList - * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. - */ - 'concat': 1, - /** Removes the first last from the NodeList and returns it. - * @for NodeList - * @method pop - * @return {Node} The last item in the NodeList. - */ - 'pop': 0, - /** Adds the given Node(s) to the end of the NodeList. - * @for NodeList - * @method push - * @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList. - */ - 'push': 0, - /** Removes the first item from the NodeList and returns it. - * @for NodeList - * @method shift - * @return {Node} The first item in the NodeList. - */ - 'shift': 0, - /** Returns a new NodeList comprising the Nodes in the given range. - * @for NodeList - * @method slice - * @param {Number} begin Zero-based index at which to begin extraction. - As a negative index, start indicates an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element in the sequence. - * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end. - slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3). - As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence. - If end is omitted, slice extracts to the end of the sequence. - * @return {NodeList} A new NodeList comprised of this NodeList joined with the input. - */ - 'slice': 1, - /** Changes the content of the NodeList, adding new elements while removing old elements. - * @for NodeList - * @method splice - * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end. - * @param {Number} howMany An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed. In this case, you should specify at least one new element. If no howMany parameter is specified (second syntax above, which is a SpiderMonkey extension), all elements after index are removed. - * {Node | DOMNode| element1, ..., elementN - The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array. - * @return {NodeList} The element(s) removed. - */ - 'splice': 1, - /** Adds the given Node(s) to the beginning of the NodeList. - * @for NodeList - * @method push - * @param {Node | DOMNode} nodes One or more nodes to add to the NodeList. - */ - 'unshift': 0 - }; - - -Y.Object.each(ArrayMethods, function(returnNodeList, name) { - Y_NodeList.prototype[name] = function() { - var args = [], - i = 0, - arg, - ret; - - while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists - args.push(arg._node || arg._nodes || arg); - } - - ret = ArrayProto[name].apply(this._nodes, args); - - if (returnNodeList) { - ret = Y.all(ret); - } else { - ret = Y.Node.scrubVal(ret); - } - - return ret; - }; -}); - - -}, '3.4.0' ,{requires:['dom-base', 'selector-css2', 'event-base']}); -YUI.add('node-style', function(Y) { - -(function(Y) { -/** - * Extended Node interface for managing node styles. - * @module node - * @submodule node-style - */ - -var methods = [ - /** - * Returns the style's current value. - * @method getStyle - * @for Node - * @param {String} attr The style attribute to retrieve. - * @return {String} The current value of the style property for the element. - */ - 'getStyle', - - /** - * Returns the computed value for the given style property. - * @method getComputedStyle - * @param {String} attr The style attribute to retrieve. - * @return {String} The computed value of the style property for the element. - */ - 'getComputedStyle', - - /** - * Sets a style property of the node. - * @method setStyle - * @param {String} attr The style attribute to set. - * @param {String|Number} val The value. - * @chainable - */ - 'setStyle', - - /** - * Sets multiple style properties on the node. - * @method setStyles - * @param {Object} hash An object literal of property:value pairs. - * @chainable - */ - 'setStyles' -]; -Y.Node.importMethod(Y.DOM, methods); -/** - * Returns an array of values for each node. - * @method getStyle - * @for NodeList - * @see Node.getStyle - * @param {String} attr The style attribute to retrieve. - * @return {Array} The current values of the style property for the element. - */ - -/** - * Returns an array of the computed value for each node. - * @method getComputedStyle - * @see Node.getComputedStyle - * @param {String} attr The style attribute to retrieve. - * @return {Array} The computed values for each node. - */ - -/** - * Sets a style property on each node. - * @method setStyle - * @see Node.setStyle - * @param {String} attr The style attribute to set. - * @param {String|Number} val The value. - * @chainable - */ - -/** - * Sets multiple style properties on each node. - * @method setStyles - * @see Node.setStyles - * @param {Object} hash An object literal of property:value pairs. - * @chainable - */ -Y.NodeList.importMethod(Y.Node.prototype, methods); -})(Y); - - -}, '3.4.0' ,{requires:['dom-style', 'node-base']}); -YUI.add('node-screen', function(Y) { - -/** - * Extended Node interface for managing regions and screen positioning. - * Adds support for positioning elements and normalizes window size and scroll detection. - * @module node - * @submodule node-screen - */ - -// these are all "safe" returns, no wrapping required -Y.each([ - /** - * Returns the inner width of the viewport (exludes scrollbar). - * @config winWidth - * @for Node - * @type {Int} - */ - 'winWidth', - - /** - * Returns the inner height of the viewport (exludes scrollbar). - * @config winHeight - * @type {Int} - */ - 'winHeight', - - /** - * Document width - * @config winHeight - * @type {Int} - */ - 'docWidth', - - /** - * Document height - * @config docHeight - * @type {Int} - */ - 'docHeight', - - /** - * Amount page has been scroll vertically - * @config docScrollX - * @type {Int} - */ - 'docScrollX', - - /** - * Amount page has been scroll horizontally - * @config docScrollY - * @type {Int} - */ - 'docScrollY' - ], - function(name) { - Y.Node.ATTRS[name] = { - getter: function() { - var args = Array.prototype.slice.call(arguments); - args.unshift(Y.Node.getDOMNode(this)); - - return Y.DOM[name].apply(this, args); - } - }; - } -); - -Y.Node.ATTRS.scrollLeft = { - getter: function() { - var node = Y.Node.getDOMNode(this); - return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node); - }, - - setter: function(val) { - var node = Y.Node.getDOMNode(this); - if (node) { - if ('scrollLeft' in node) { - node.scrollLeft = val; - } else if (node.document || node.nodeType === 9) { - Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc - } - } else { - Y.log('unable to set scrollLeft for ' + node, 'error', 'Node'); - } - } -}; - -Y.Node.ATTRS.scrollTop = { - getter: function() { - var node = Y.Node.getDOMNode(this); - return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node); - }, - - setter: function(val) { - var node = Y.Node.getDOMNode(this); - if (node) { - if ('scrollTop' in node) { - node.scrollTop = val; - } else if (node.document || node.nodeType === 9) { - Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc - } - } else { - Y.log('unable to set scrollTop for ' + node, 'error', 'Node'); - } - } -}; - -Y.Node.importMethod(Y.DOM, [ -/** - * Gets the current position of the node in page coordinates. - * @method getXY - * @for Node - * @return {Array} The XY position of the node -*/ - 'getXY', - -/** - * Set the position of the node in page coordinates, regardless of how the node is positioned. - * @method setXY - * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) - * @chainable - */ - 'setXY', - -/** - * Gets the current position of the node in page coordinates. - * @method getX - * @return {Int} The X position of the node -*/ - 'getX', - -/** - * Set the position of the node in page coordinates, regardless of how the node is positioned. - * @method setX - * @param {Int} x X value for new position (coordinates are page-based) - * @chainable - */ - 'setX', - -/** - * Gets the current position of the node in page coordinates. - * @method getY - * @return {Int} The Y position of the node -*/ - 'getY', - -/** - * Set the position of the node in page coordinates, regardless of how the node is positioned. - * @method setY - * @param {Int} y Y value for new position (coordinates are page-based) - * @chainable - */ - 'setY', - -/** - * Swaps the XY position of this node with another node. - * @method swapXY - * @param {Y.Node || HTMLElement} otherNode The node to swap with. - * @chainable - */ - 'swapXY' -]); - -/** - * Returns a region object for the node - * @config region - * @for Node - * @type Node - */ -Y.Node.ATTRS.region = { - getter: function() { - var node = this.getDOMNode(), - region; - - if (node && !node.tagName) { - if (node.nodeType === 9) { // document - node = node.documentElement; - } - } - if (Y.DOM.isWindow(node)) { - region = Y.DOM.viewportRegion(node); - } else { - region = Y.DOM.region(node); - } - return region; - } -}; - -/** - * Returns a region object for the node's viewport - * @config viewportRegion - * @type Node - */ -Y.Node.ATTRS.viewportRegion = { - getter: function() { - return Y.DOM.viewportRegion(Y.Node.getDOMNode(this)); - } -}; - -Y.Node.importMethod(Y.DOM, 'inViewportRegion'); - -// these need special treatment to extract 2nd node arg -/** - * Compares the intersection of the node with another node or region - * @method intersect - * @for Node - * @param {Node|Object} node2 The node or region to compare with. - * @param {Object} altRegion An alternate region to use (rather than this node's). - * @return {Object} An object representing the intersection of the regions. - */ -Y.Node.prototype.intersect = function(node2, altRegion) { - var node1 = Y.Node.getDOMNode(this); - if (Y.instanceOf(node2, Y.Node)) { // might be a region object - node2 = Y.Node.getDOMNode(node2); - } - return Y.DOM.intersect(node1, node2, altRegion); -}; - -/** - * Determines whether or not the node is within the giving region. - * @method inRegion - * @param {Node|Object} node2 The node or region to compare with. - * @param {Boolean} all Whether or not all of the node must be in the region. - * @param {Object} altRegion An alternate region to use (rather than this node's). - * @return {Object} An object representing the intersection of the regions. - */ -Y.Node.prototype.inRegion = function(node2, all, altRegion) { - var node1 = Y.Node.getDOMNode(this); - if (Y.instanceOf(node2, Y.Node)) { // might be a region object - node2 = Y.Node.getDOMNode(node2); - } - return Y.DOM.inRegion(node1, node2, all, altRegion); -}; - - -}, '3.4.0' ,{requires:['node-base', 'dom-screen']}); -YUI.add('node-pluginhost', function(Y) { - -/** - * Registers plugins to be instantiated at the class level (plugins - * which should be plugged into every instance of Node by default). - * - * @method Node.plug - * @static - * - * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) - * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin - */ -Y.Node.plug = function() { - var args = Y.Array(arguments); - args.unshift(Y.Node); - Y.Plugin.Host.plug.apply(Y.Base, args); - return Y.Node; -}; - -/** - * Unregisters any class level plugins which have been registered by the Node - * - * @method Node.unplug - * @static - * - * @param {Function | Array} plugin The plugin class, or an array of plugin classes - */ -Y.Node.unplug = function() { - var args = Y.Array(arguments); - args.unshift(Y.Node); - Y.Plugin.Host.unplug.apply(Y.Base, args); - return Y.Node; -}; - -Y.mix(Y.Node, Y.Plugin.Host, false, null, 1); - -// allow batching of plug/unplug via NodeList -// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode) -Y.NodeList.prototype.plug = function() { - var args = arguments; - Y.NodeList.each(this, function(node) { - Y.Node.prototype.plug.apply(Y.one(node), args); - }); -}; - -Y.NodeList.prototype.unplug = function() { - var args = arguments; - Y.NodeList.each(this, function(node) { - Y.Node.prototype.unplug.apply(Y.one(node), args); - }); -}; - - -}, '3.4.0' ,{requires:['node-base', 'pluginhost']}); -YUI.add('node-event-delegate', function(Y) { - -/** - * Functionality to make the node a delegated event container - * @module node - * @submodule node-event-delegate - */ - -/** - *Sets up a delegation listener for an event occurring inside the Node. - * The delegated event will be verified against a supplied selector or - * filtering function to test if the event references at least one node that - * should trigger the subscription callback.
- * - *Selector string filters will trigger the callback if the event originated - * from a node that matches it or is contained in a node that matches it. - * Function filters are called for each Node up the parent axis to the - * subscribing container node, and receive at each level the Node and the event - * object. The function should return true (or a truthy value) if that Node - * should trigger the subscription callback. Note, it is possible for filters - * to match multiple Nodes for a single event. In this case, the delegate - * callback will be executed for each matching Node.
- * - *For each matching Node, the callback will be executed with its 'this'
- * object set to the Node matched by the filter (unless a specific context was
- * provided during subscription), and the provided event's
- * currentTarget
will also be set to the matching Node. The
- * containing Node from which the subscription was originally made can be
- * referenced as e.container
.
- *
- * @method delegate
- * @param type {String} the event type to delegate
- * @param fn {Function} the callback function to execute. This function
- * will be provided the event object for the delegated event.
- * @param spec {String|Function} a selector that must match the target of the
- * event or a function to test target and its parents for a match
- * @param context {Object} optional argument that specifies what 'this' refers to.
- * @param args* {any} 0..n additional arguments to pass on to the callback function.
- * These arguments will be added after the event object.
- * @return {EventHandle} the detach handle
- * @for Node
- */
-Y.Node.prototype.delegate = function(type) {
+ // Class Configuration
+ var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
+ plug = [],
+ unplug = {},
+ constructor, i, classPlug, classUnplug, pluginClassName;
- var args = Y.Array(arguments, 0, true),
- index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2;
+ // TODO: Room for optimization. Can we apply statically/unplug in same pass?
+ for (i = classes.length - 1; i >= 0; i--) {
+ constructor = classes[i];
- args.splice(index, 0, this._node);
+ classUnplug = constructor._UNPLUG;
+ if (classUnplug) {
+ // subclasses over-write
+ Y.mix(unplug, classUnplug, true);
+ }
- return Y.delegate.apply(Y, args);
-};
+ classPlug = constructor._PLUG;
+ if (classPlug) {
+ // subclasses over-write
+ Y.mix(plug, classPlug, true);
+ }
+ }
+ for (pluginClassName in plug) {
+ if (plug.hasOwnProperty(pluginClassName)) {
+ if (!unplug[pluginClassName]) {
+ this.plug(plug[pluginClassName]);
+ }
+ }
+ }
-}, '3.4.0' ,{requires:['node-base', 'event-delegate']});
+ // User Configuration
+ if (config && config.plugins) {
+ this.plug(config.plugins);
+ }
+ };
+
+ /**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of the class by default).
+ *
+ * @method Plugin.Host.plug
+ * @static
+ *
+ * @param {Function} hostClass The host class on which to register the plugins
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+ PluginHost.plug = function(hostClass, plugin, config) {
+ // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ]
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._PLUG = hostClass._PLUG || {};
+
+ if (!L.isArray(plugin)) {
+ if (config) {
+ plugin = {fn:plugin, cfg:config};
+ }
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l;i++) {
+ p = plugin[i];
+ name = p.NAME || p.fn.NAME;
+ hostClass._PLUG[name] = p;
+ }
+ }
+ };
+ /**
+ * Unregisters any class level plugins which have been registered by the host class, or any
+ * other class in the hierarchy.
+ *
+ * @method Plugin.Host.unplug
+ * @static
+ *
+ * @param {Function} hostClass The host class from which to unregister the plugins
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+ PluginHost.unplug = function(hostClass, plugin) {
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._UNPLUG = hostClass._UNPLUG || {};
+
+ if (!L.isArray(plugin)) {
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l; i++) {
+ p = plugin[i];
+ name = p.NAME;
+ if (!hostClass._PLUG[name]) {
+ hostClass._UNPLUG[name] = p;
+ } else {
+ delete hostClass._PLUG[name];
+ }
+ }
+ }
+ };
-YUI.add('node', function(Y){}, '3.4.0' ,{skinnable:false, use:['node-base', 'node-style', 'node-screen', 'node-pluginhost', 'node-event-delegate']});
+}, '3.4.0' ,{requires:['pluginhost-base']});
YUI.add('event-delegate', function(Y) {
/**
@@ -18445,919 +14737,534 @@ delegate._applyFilter = function (filter, args, ce) {
container = ce.el, // facadeless events in IE, have no e.currentTarget
target = e.target || e.srcElement,
match = [],
- isContainer = false;
-
- // Resolve text nodes to their containing element
- if (target.nodeType === 3) {
- target = target.parentNode;
- }
-
- // passing target as the first arg rather than leaving well enough alone
- // making 'this' in the filter function refer to the target. This is to
- // support bound filter functions.
- args.unshift(target);
-
- if (isString(filter)) {
- while (target) {
- isContainer = (target === container);
- if (selectorTest(target, filter, (isContainer ?null: container))) {
- match.push(target);
- }
-
- if (isContainer) {
- break;
- }
-
- target = target.parentNode;
- }
- } else {
- // filter functions are implementer code and should receive wrappers
- args[0] = Y.one(target);
- args[1] = new Y.DOMEventFacade(e, container, ce);
-
- while (target) {
- // filter(target, e, extra args...) - this === target
- if (filter.apply(args[0], args)) {
- match.push(target);
- }
-
- if (target === container) {
- break;
- }
-
- target = target.parentNode;
- args[0] = Y.one(target);
- }
- args[1] = e; // restore the raw DOM event
- }
-
- if (match.length <= 1) {
- match = match[0]; // single match or undefined
- }
-
- // remove the target
- args.shift();
-
- return match;
-};
-
-/**
- * Sets up event delegation on a container element. The delegated event
- * will use a supplied filter to test if the callback should be executed.
- * This filter can be either a selector string or a function that returns
- * a Node to use as the currentTarget for the event.
- *
- * The event object for the delegated event is supplied to the callback
- * function. It is modified slightly in order to support all properties
- * that may be needed for event delegation. 'currentTarget' is set to
- * the element that matched the selector string filter or the Node returned
- * from the filter function. 'container' is set to the element that the
- * listener is delegated from (this normally would be the 'currentTarget').
- *
- * Filter functions will be called with the arguments that would be passed to
- * the callback function, including the event object as the first parameter.
- * The function should return false (or a falsey value) if the success criteria
- * aren't met, and the Node to use as the event's currentTarget and 'this'
- * object if they are.
- *
- * @method delegate
- * @param type {string} the event type to delegate
- * @param fn {function} the callback function to execute. This function
- * will be provided the event object for the delegated event.
- * @param el {string|node} the element that is the delegation container
- * @param filter {string|function} a selector that must match the target of the
- * event or a function that returns a Node or false.
- * @param context optional argument that specifies what 'this' refers to.
- * @param args* 0..n additional arguments to pass on to the callback function.
- * These arguments will be added after the event object.
- * @return {EventHandle} the detach handle
- * @for YUI
- */
-Y.delegate = Y.Event.delegate = delegate;
-
-
-}, '3.4.0' ,{requires:['node-base']});
-YUI.add('io-base', function(Y) {
-
- /**
- * Base IO functionality. Provides basic XHR transport support.
- * @module io
- * @submodule io-base
- */
-
- /**
- * The io class is a utility that brokers HTTP requests through a simplified
- * interface. Specifically, it allows JavaScript to make HTTP requests to
- * a resource without a page reload. The underlying transport for making
- * same-domain requests is the XMLHttpRequest object. YUI.io can also use
- * Flash, if specified as a transport, for cross-domain requests.
- *
- * @class io
- */
-
- /**
- * @event io:start
- * @description This event is fired by YUI.io when a transaction is initiated.
- * @type Event Custom
- */
- var E_START = 'io:start',
-
- /**
- * @event io:complete
- * @description This event is fired by YUI.io when a transaction is complete.
- * Response status and data are accessible, if available.
- * @type Event Custom
- */
- E_COMPLETE = 'io:complete',
-
- /**
- * @event io:success
- * @description This event is fired by YUI.io when a transaction is complete, and
- * the HTTP status resolves to HTTP2xx.
- * @type Event Custom
- */
- E_SUCCESS = 'io:success',
-
- /**
- * @event io:failure
- * @description This event is fired by YUI.io when a transaction is complete, and
- * the HTTP status resolves to HTTP4xx, 5xx and above.
- * @type Event Custom
- */
- E_FAILURE = 'io:failure',
-
- /**
- * @event io:end
- * @description This event signifies the end of the transaction lifecycle. The
- * transaction transport is destroyed.
- * @type Event Custom
- */
- E_END = 'io:end',
-
- //--------------------------------------
- // Properties
- //--------------------------------------
- /**
- * @description A transaction counter that increments for each transaction.
- *
- * @property transactionId
- * @private
- * @static
- * @type int
- */
- transactionId = 0,
-
- /**
- * @description Object of default HTTP headers to be initialized and sent
- * for all transactions.
- *
- * @property _headers
- * @private
- * @static
- * @type object
- */
- _headers = {
- 'X-Requested-With' : 'XMLHttpRequest'
- },
-
- /**
- * @description Object that stores timeout values for any transaction with
- * a defined "timeout" configuration property.
- *
- * @property _timeout
- * @private
- * @static
- * @type object
- */
- _timeout = {},
-
- // Window reference
- w = Y.config.win;
-
- //--------------------------------------
- // Methods
- //--------------------------------------
-
- /**
- * @description Method that creates the XMLHttpRequest transport
- *
- * @method _xhr
- * @private
- * @static
- * @return object
- */
- function _xhr() {
- return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
- }
-
- /**
- * @description Method that increments _transactionId for each transaction.
- *
- * @method _id
- * @private
- * @static
- * @return int
- */
- function _id() {
- var id = transactionId;
-
- transactionId++;
+ isContainer = false;
- return id;
+ // Resolve text nodes to their containing element
+ if (target.nodeType === 3) {
+ target = target.parentNode;
}
- /**
- * @description Method that creates a unique transaction object for each
- * request.
- *
- * @method _create
- * @private
- * @static
- * @param {number} c - configuration object subset to determine if
- * the transaction is an XDR or file upload,
- * requiring an alternate transport.
- * @param {number} i - transaction id
- * @return object
- */
- function _create(c, i) {
- var o = {};
- o.id = Y.Lang.isNumber(i) ? i : _id();
- c = c || {};
-
- if (!c.use && !c.upload) {
- o.c = _xhr();
- }
- else if (c.use) {
- if (c.use === 'native') {
- if (w.XDomainRequest) {
- o.c = new XDomainRequest();
- o.t = c.use;
- }
- else {
- o.c = _xhr();
- }
+ // passing target as the first arg rather than leaving well enough alone
+ // making 'this' in the filter function refer to the target. This is to
+ // support bound filter functions.
+ args.unshift(target);
+
+ if (isString(filter)) {
+ while (target) {
+ isContainer = (target === container);
+ if (selectorTest(target, filter, (isContainer ?null: container))) {
+ match.push(target);
}
- else {
- o.c = Y.io._transport[c.use];
- o.t = c.use;
+
+ if (isContainer) {
+ break;
}
- }
- else {
- o.c = {};
- o.t = 'io:iframe';
- }
- return o;
- }
+ target = target.parentNode;
+ }
+ } else {
+ // filter functions are implementer code and should receive wrappers
+ args[0] = Y.one(target);
+ args[1] = new Y.DOMEventFacade(e, container, ce);
+ while (target) {
+ // filter(target, e, extra args...) - this === target
+ if (filter.apply(args[0], args)) {
+ match.push(target);
+ }
- function _destroy(o) {
- if (w) {
- if (o.c && w.XMLHttpRequest) {
- o.c.onreadystatechange = null;
+ if (target === container) {
+ break;
}
- else if (Y.UA.ie === 6 && !o.t) {
- // IE, when using XMLHttpRequest as an ActiveX Object, will throw
- // a "Type Mismatch" error if the event handler is set to "null".
- o.c.abort();
- }
+
+ target = target.parentNode;
+ args[0] = Y.one(target);
}
+ args[1] = e; // restore the raw DOM event
+ }
- o.c = null;
- o = null;
+ if (match.length <= 1) {
+ match = match[0]; // single match or undefined
}
- /**
- * @description Method for creating and subscribing transaction events.
- *
- * @method _tE
- * @private
- * @static
- * @param {string} e - event to be published
- * @param {object} c - configuration data subset for event subscription.
- *
- * @return void
- */
- function _tE(e, c) {
- var eT = new Y.EventTarget().publish('transaction:' + e),
- cT = c.context || Y,
- a = c.arguments;
+ // remove the target
+ args.shift();
- if (a) {
- eT.on(c.on[e], cT, a);
- }
- else {
- eT.on(c.on[e], cT);
- }
+ return match;
+};
- return eT;
- }
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied filter to test if the callback should be executed.
+ * This filter can be either a selector string or a function that returns
+ * a Node to use as the currentTarget for the event.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the selector string filter or the Node returned
+ * from the filter function. 'container' is set to the element that the
+ * listener is delegated from (this normally would be the 'currentTarget').
+ *
+ * Filter functions will be called with the arguments that would be passed to
+ * the callback function, including the event object as the first parameter.
+ * The function should return false (or a falsey value) if the success criteria
+ * aren't met, and the Node to use as the event's currentTarget and 'this'
+ * object if they are.
+ *
+ * @method delegate
+ * @param type {string} the event type to delegate
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param filter {string|function} a selector that must match the target of the
+ * event or a function that returns a Node or false.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.delegate = Y.Event.delegate = delegate;
- /**
- * @description Fires event "io:start" and creates, fires a
- * transaction-specific start event, if config.on.start is
- * defined.
- *
- * @method _ioStart
- * @private
- * @static
- * @param {number} id - transaction id
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioStart(id, c) {
- var a = c.arguments;
- if (a) {
- Y.fire(E_START, id, a);
- }
- else {
- Y.fire(E_START, id);
- }
+}, '3.4.0' ,{requires:['node-base']});
+YUI.add('node-event-delegate', function(Y) {
- if (c.on && c.on.start) {
- _tE('start', c).fire(id);
- }
- }
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
+
+/**
+ *
Sets up a delegation listener for an event occurring inside the Node. + * The delegated event will be verified against a supplied selector or + * filtering function to test if the event references at least one node that + * should trigger the subscription callback.
+ * + *Selector string filters will trigger the callback if the event originated + * from a node that matches it or is contained in a node that matches it. + * Function filters are called for each Node up the parent axis to the + * subscribing container node, and receive at each level the Node and the event + * object. The function should return true (or a truthy value) if that Node + * should trigger the subscription callback. Note, it is possible for filters + * to match multiple Nodes for a single event. In this case, the delegate + * callback will be executed for each matching Node.
+ * + *For each matching Node, the callback will be executed with its 'this'
+ * object set to the Node matched by the filter (unless a specific context was
+ * provided during subscription), and the provided event's
+ * =g.rollup);if(e){break;}}}}if(e){b[k]=true;d=true;this.getRequires(g);}}}}if(!d){break;}}};},"3.4.0",{requires:["loader-base"]});YUI.add("loader-yui3",function(a){YUI.Env[a.version].modules=YUI.Env[a.version].modules||{"align-plugin":{"requires":["node-screen","node-pluginhost"]},"anim":{"use":["anim-base","anim-color","anim-curve","anim-easing","anim-node-plugin","anim-scroll","anim-xy"]},"anim-base":{"requires":["base-base","node-style"]},"anim-color":{"requires":["anim-base"]},"anim-curve":{"requires":["anim-xy"]},"anim-easing":{"requires":["anim-base"]},"anim-node-plugin":{"requires":["node-pluginhost","anim-base"]},"anim-scroll":{"requires":["anim-base"]},"anim-xy":{"requires":["anim-base","node-screen"]},"app":{"use":["controller","model","model-list","view"]},"array-extras":{},"array-invoke":{},"arraylist":{},"arraylist-add":{"requires":["arraylist"]},"arraylist-filter":{"requires":["arraylist"]},"arraysort":{"requires":["yui-base"]},"async-queue":{"requires":["event-custom"]},"attribute":{"use":["attribute-base","attribute-complex"]},"attribute-base":{"requires":["event-custom"]},"attribute-complex":{"requires":["attribute-base"]},"autocomplete":{"use":["autocomplete-base","autocomplete-sources","autocomplete-list","autocomplete-plugin"]},"autocomplete-base":{"optional":["autocomplete-sources"],"requires":["array-extras","base-build","escape","event-valuechange","node-base"]},"autocomplete-filters":{"requires":["array-extras","text-wordbreak"]},"autocomplete-filters-accentfold":{"requires":["array-extras","text-accentfold","text-wordbreak"]},"autocomplete-highlighters":{"requires":["array-extras","highlight-base"]},"autocomplete-highlighters-accentfold":{"requires":["array-extras","highlight-accentfold"]},"autocomplete-list":{"after":["autocomplete-sources"],"lang":["en"],"requires":["autocomplete-base","event-resize","selector-css3","shim-plugin","widget","widget-position","widget-position-align"],"skinnable":true},"autocomplete-list-keys":{"condition":{"name":"autocomplete-list-keys","test":function(b){return !(b.UA.ios||b.UA.android);
-},"trigger":"autocomplete-list"},"requires":["autocomplete-list","base-build"]},"autocomplete-plugin":{"requires":["autocomplete-list","node-pluginhost"]},"autocomplete-sources":{"optional":["io-base","json-parse","jsonp","yql"],"requires":["autocomplete-base"]},"base":{"use":["base-base","base-pluginhost","base-build"]},"base-base":{"after":["attribute-complex"],"requires":["attribute-base"]},"base-build":{"requires":["base-base"]},"base-pluginhost":{"requires":["base-base","pluginhost"]},"cache":{"use":["cache-base","cache-offline","cache-plugin"]},"cache-base":{"requires":["base"]},"cache-offline":{"requires":["cache-base","json"]},"cache-plugin":{"requires":["plugin","cache-base"]},"charts":{"requires":["dom","datatype","event-custom","event-mouseenter","widget","widget-position","widget-stack"]},"classnamemanager":{"requires":["yui-base"]},"clickable-rail":{"requires":["slider-base"]},"collection":{"use":["array-extras","arraylist","arraylist-add","arraylist-filter","array-invoke"]},"compat":{"requires":["event-base","dom","dump","substitute"]},"console":{"lang":["en","es"],"requires":["yui-log","widget","substitute"],"skinnable":true},"console-filters":{"requires":["plugin","console"],"skinnable":true},"controller":{"optional":["querystring-parse"],"requires":["array-extras","base-build","history"]},"cookie":{"requires":["yui-base"]},"createlink-base":{"requires":["editor-base"]},"cssbase":{"after":["cssreset","cssfonts","cssgrids","cssreset-context","cssfonts-context","cssgrids-context"],"type":"css"},"cssbase-context":{"after":["cssreset","cssfonts","cssgrids","cssreset-context","cssfonts-context","cssgrids-context"],"type":"css"},"cssfonts":{"type":"css"},"cssfonts-context":{"type":"css"},"cssgrids":{"optional":["cssreset","cssfonts"],"type":"css"},"cssgrids-context-deprecated":{"optional":["cssreset-context"],"requires":["cssfonts-context"],"type":"css"},"cssgrids-deprecated":{"optional":["cssreset"],"requires":["cssfonts"],"type":"css"},"cssreset":{"type":"css"},"cssreset-context":{"type":"css"},"dataschema":{"use":["dataschema-base","dataschema-json","dataschema-xml","dataschema-array","dataschema-text"]},"dataschema-array":{"requires":["dataschema-base"]},"dataschema-base":{"requires":["base"]},"dataschema-json":{"requires":["dataschema-base","json"]},"dataschema-text":{"requires":["dataschema-base"]},"dataschema-xml":{"requires":["dataschema-base"]},"datasource":{"use":["datasource-local","datasource-io","datasource-get","datasource-function","datasource-cache","datasource-jsonschema","datasource-xmlschema","datasource-arrayschema","datasource-textschema","datasource-polling"]},"datasource-arrayschema":{"requires":["datasource-local","plugin","dataschema-array"]},"datasource-cache":{"requires":["datasource-local","plugin","cache-base"]},"datasource-function":{"requires":["datasource-local"]},"datasource-get":{"requires":["datasource-local","get"]},"datasource-io":{"requires":["datasource-local","io-base"]},"datasource-jsonschema":{"requires":["datasource-local","plugin","dataschema-json"]},"datasource-local":{"requires":["base"]},"datasource-polling":{"requires":["datasource-local"]},"datasource-textschema":{"requires":["datasource-local","plugin","dataschema-text"]},"datasource-xmlschema":{"requires":["datasource-local","plugin","dataschema-xml"]},"datatable":{"use":["datatable-base","datatable-datasource","datatable-sort","datatable-scroll"]},"datatable-base":{"requires":["recordset-base","widget","substitute","event-mouseenter"],"skinnable":true},"datatable-datasource":{"requires":["datatable-base","plugin","datasource-local"]},"datatable-scroll":{"requires":["datatable-base","plugin","stylesheet"]},"datatable-sort":{"lang":["en"],"requires":["datatable-base","plugin","recordset-sort"]},"datatype":{"use":["datatype-number","datatype-date","datatype-xml"]},"datatype-date":{"lang":["ar","ar-JO","ca","ca-ES","da","da-DK","de","de-AT","de-DE","el","el-GR","en","en-AU","en-CA","en-GB","en-IE","en-IN","en-JO","en-MY","en-NZ","en-PH","en-SG","en-US","es","es-AR","es-BO","es-CL","es-CO","es-EC","es-ES","es-MX","es-PE","es-PY","es-US","es-UY","es-VE","fi","fi-FI","fr","fr-BE","fr-CA","fr-FR","hi","hi-IN","id","id-ID","it","it-IT","ja","ja-JP","ko","ko-KR","ms","ms-MY","nb","nb-NO","nl","nl-BE","nl-NL","pl","pl-PL","pt","pt-BR","ro","ro-RO","ru","ru-RU","sv","sv-SE","th","th-TH","tr","tr-TR","vi","vi-VN","zh-Hans","zh-Hans-CN","zh-Hant","zh-Hant-HK","zh-Hant-TW"],"supersedes":["datatype-date-format"],"use2":["datatype-date-parse","datatype-date-format"]},"datatype-date-format":{},"datatype-date-parse":{},"datatype-number":{"use":["datatype-number-parse","datatype-number-format"]},"datatype-number-format":{},"datatype-number-parse":{},"datatype-xml":{"use":["datatype-xml-parse","datatype-xml-format"]},"datatype-xml-format":{},"datatype-xml-parse":{},"dd":{"use":["dd-ddm-base","dd-ddm","dd-ddm-drop","dd-drag","dd-proxy","dd-constrain","dd-drop","dd-scroll","dd-delegate"]},"dd-constrain":{"requires":["dd-drag"]},"dd-ddm":{"requires":["dd-ddm-base","event-resize"]},"dd-ddm-base":{"requires":["node","base","yui-throttle","classnamemanager"]},"dd-ddm-drop":{"requires":["dd-ddm"]},"dd-delegate":{"requires":["dd-drag","dd-drop-plugin","event-mouseenter"]},"dd-drag":{"requires":["dd-ddm-base"]},"dd-drop":{"requires":["dd-drag","dd-ddm-drop"]},"dd-drop-plugin":{"requires":["dd-drop"]},"dd-gestures":{"condition":{"name":"dd-gestures","test":function(b){return(b.config.win&&("ontouchstart" in b.config.win&&!b.UA.chrome));},"trigger":"dd-drag"},"requires":["dd-drag","event-synthetic","event-gestures"]},"dd-plugin":{"optional":["dd-constrain","dd-proxy"],"requires":["dd-drag"]},"dd-proxy":{"requires":["dd-drag"]},"dd-scroll":{"requires":["dd-drag"]},"dial":{"lang":["en","es"],"requires":["widget","dd-drag","substitute","event-mouseenter","event-move","transition","intl"],"skinnable":true},"dom":{"use":["dom-core","dom-base","dom-attrs","dom-create","dom-class","dom-size","dom-screen","dom-style","selector-native","selector"]},"dom-attrs":{"requires":["dom-core"]},"dom-base":{"requires":["dom-core","dom-attrs","dom-create","dom-class","dom-size"]},"dom-class":{"requires":["dom-core"]},"dom-core":{"requires":["oop","features"]},"dom-create":{"requires":["dom-core"]},"dom-deprecated":{"requires":["dom-core"]},"dom-screen":{"requires":["dom-core","dom-style"]},"dom-size":{"requires":["dom-core"]},"dom-style":{"requires":["dom-core"]},"dom-style-ie":{"condition":{"name":"dom-style-ie","test":function(h){var f=h.Features.test,g=h.Features.add,d=h.config.win,e=h.config.doc,b="documentElement",c=false;
-g("style","computedStyle",{test:function(){return d&&"getComputedStyle" in d;}});g("style","opacity",{test:function(){return e&&"opacity" in e[b].style;}});c=(!f("style","opacity")&&!f("style","computedStyle"));return c;},"trigger":"dom-style"},"requires":["dom-style"]},"dump":{},"editor":{"use":["frame","selection","exec-command","editor-base","editor-para","editor-br","editor-bidi","editor-tab","createlink-base"]},"editor-base":{"requires":["base","frame","node","exec-command","selection"]},"editor-bidi":{"requires":["editor-base"]},"editor-br":{"requires":["editor-base"]},"editor-lists":{"requires":["editor-base"]},"editor-para":{"requires":["editor-base"]},"editor-tab":{"requires":["editor-base"]},"escape":{},"event":{"after":["node-base"],"use":["event-base","event-delegate","event-synthetic","event-mousewheel","event-mouseenter","event-key","event-focus","event-resize","event-hover"]},"event-base":{"after":["node-base"],"requires":["event-custom-base"]},"event-base-ie":{"after":["event-base"],"condition":{"name":"event-base-ie","test":function(c){var b=c.config.doc&&c.config.doc.implementation;return(b&&(!b.hasFeature("Events","2.0")));},"trigger":"node-base"},"requires":["node-base"]},"event-custom":{"use":["event-custom-base","event-custom-complex"]},"event-custom-base":{"requires":["oop"]},"event-custom-complex":{"requires":["event-custom-base"]},"event-delegate":{"requires":["node-base"]},"event-flick":{"requires":["node-base","event-touch","event-synthetic"]},"event-focus":{"requires":["event-synthetic"]},"event-gestures":{"use":["event-flick","event-move"]},"event-hover":{"requires":["event-mouseenter"]},"event-key":{"requires":["event-synthetic"]},"event-mouseenter":{"requires":["event-synthetic"]},"event-mousewheel":{"requires":["node-base"]},"event-move":{"requires":["node-base","event-touch","event-synthetic"]},"event-resize":{"requires":["node-base"]},"event-simulate":{"requires":["event-base"]},"event-synthetic":{"requires":["node-base","event-custom-complex"]},"event-touch":{"requires":["node-base"]},"event-valuechange":{"requires":["event-focus","event-synthetic"]},"exec-command":{"requires":["frame"]},"features":{"requires":["yui-base"]},"frame":{"requires":["base","node","selector-css3","substitute","yui-throttle"]},"get":{"requires":["yui-base"]},"highlight":{"use":["highlight-base","highlight-accentfold"]},"highlight-accentfold":{"requires":["highlight-base","text-accentfold"]},"highlight-base":{"requires":["array-extras","escape","text-wordbreak"]},"history":{"use":["history-base","history-hash","history-hash-ie","history-html5"]},"history-base":{"requires":["event-custom-complex"]},"history-hash":{"after":["history-html5"],"requires":["event-synthetic","history-base","yui-later"]},"history-hash-ie":{"condition":{"name":"history-hash-ie","test":function(c){var b=c.config.doc&&c.config.doc.documentMode;return c.UA.ie&&(!("onhashchange" in c.config.win)||!b||b<8);},"trigger":"history-hash"},"requires":["history-hash","node-base"]},"history-html5":{"optional":["json"],"requires":["event-base","history-base","node-base"]},"imageloader":{"requires":["base-base","node-style","node-screen"]},"intl":{"requires":["intl-base","event-custom"]},"intl-base":{"requires":["yui-base"]},"io":{"use":["io-base","io-xdr","io-form","io-upload-iframe","io-queue"]},"io-base":{"requires":["event-custom-base","querystring-stringify-simple"]},"io-form":{"requires":["io-base","node-base"]},"io-queue":{"requires":["io-base","queue-promote"]},"io-upload-iframe":{"requires":["io-base","node-base"]},"io-xdr":{"requires":["io-base","datatype-xml"]},"json":{"use":["json-parse","json-stringify"]},"json-parse":{},"json-stringify":{},"jsonp":{"requires":["get","oop"]},"jsonp-url":{"requires":["jsonp"]},"loader":{"use":["loader-base","loader-rollup","loader-yui3"]},"loader-base":{"requires":["get"]},"loader-rollup":{"requires":["loader-base"]},"loader-yui3":{"requires":["loader-base"]},"model":{"requires":["base-build","escape","json-parse"]},"model-list":{"requires":["array-extras","array-invoke","arraylist","base-build","json-parse","model"]},"node":{"use":["node-base","node-event-delegate","node-pluginhost","node-screen","node-style"]},"node-base":{"requires":["dom-base","selector-css2","event-base"]},"node-deprecated":{"requires":["node-base"]},"node-event-delegate":{"requires":["node-base","event-delegate"]},"node-event-simulate":{"requires":["node-base","event-simulate"]},"node-flick":{"requires":["classnamemanager","transition","event-flick","plugin"],"skinnable":true},"node-focusmanager":{"requires":["attribute","node","plugin","node-event-simulate","event-key","event-focus"]},"node-load":{"requires":["node-base","io-base"]},"node-menunav":{"requires":["node","classnamemanager","plugin","node-focusmanager"],"skinnable":true},"node-pluginhost":{"requires":["node-base","pluginhost"]},"node-screen":{"requires":["dom-screen","node-base"]},"node-style":{"requires":["dom-style","node-base"]},"oop":{"requires":["yui-base"]},"overlay":{"requires":["widget","widget-stdmod","widget-position","widget-position-align","widget-stack","widget-position-constrain"],"skinnable":true},"plugin":{"requires":["base-base"]},"pluginattr":{"requires":["plugin"]},"pluginhost":{"use":["pluginhost-base","pluginhost-config"]},"pluginhost-base":{"requires":["yui-base"]},"pluginhost-config":{"requires":["pluginhost-base"]},"profiler":{"requires":["yui-base"]},"querystring":{"use":["querystring-parse","querystring-stringify"]},"querystring-parse":{"requires":["yui-base","array-extras"]},"querystring-parse-simple":{"requires":["yui-base"]},"querystring-stringify":{"requires":["yui-base"]},"querystring-stringify-simple":{"requires":["yui-base"]},"queue-promote":{"requires":["yui-base"]},"range-slider":{"requires":["slider-base","slider-value-range","clickable-rail"]},"recordset":{"use":["recordset-base","recordset-sort","recordset-filter","recordset-indexer"]},"recordset-base":{"requires":["base","arraylist"]},"recordset-filter":{"requires":["recordset-base","array-extras","plugin"]},"recordset-indexer":{"requires":["recordset-base","plugin"]},"recordset-sort":{"requires":["arraysort","recordset-base","plugin"]},"resize":{"use":["resize-base","resize-proxy","resize-constrain"]},"resize-base":{"requires":["base","widget","substitute","event","oop","dd-drag","dd-delegate","dd-drop"],"skinnable":true},"resize-constrain":{"requires":["plugin","resize-base"]},"resize-plugin":{"optional":["resize-constrain"],"requires":["resize-base","plugin"]},"resize-proxy":{"requires":["plugin","resize-base"]},"rls":{"requires":["get","features"]},"scrollview":{"requires":["scrollview-base","scrollview-scrollbars"]},"scrollview-base":{"requires":["widget","event-gestures","transition"],"skinnable":true},"scrollview-base-ie":{"condition":{"name":"scrollview-base-ie","trigger":"scrollview-base","ua":"ie"},"requires":["scrollview-base"]},"scrollview-paginator":{"requires":["plugin"]},"scrollview-scrollbars":{"requires":["classnamemanager","transition","plugin"],"skinnable":true},"selection":{"requires":["node"]},"selector":{"requires":["selector-native"]},"selector-css2":{"condition":{"name":"selector-css2","test":function(d){var c=d.config.doc,b=c&&!("querySelectorAll" in c);
-return b;},"trigger":"selector"},"requires":["selector-native"]},"selector-css3":{"requires":["selector-native","selector-css2"]},"selector-native":{"requires":["dom-core"]},"shim-plugin":{"requires":["node-style","node-pluginhost"]},"slider":{"use":["slider-base","slider-value-range","clickable-rail","range-slider"]},"slider-base":{"requires":["widget","dd-constrain","substitute"],"skinnable":true},"slider-value-range":{"requires":["slider-base"]},"sortable":{"requires":["dd-delegate","dd-drop-plugin","dd-proxy"]},"sortable-scroll":{"requires":["dd-scroll","sortable"]},"stylesheet":{},"substitute":{"optional":["dump"]},"swf":{"requires":["event-custom","node","swfdetect"]},"swfdetect":{},"tabview":{"requires":["widget","widget-parent","widget-child","tabview-base","node-pluginhost","node-focusmanager"],"skinnable":true},"tabview-base":{"requires":["node-event-delegate","classnamemanager","skin-sam-tabview"]},"tabview-plugin":{"requires":["tabview-base"]},"test":{"requires":["event-simulate","event-custom","substitute","json-stringify"],"skinnable":true},"text":{"use":["text-accentfold","text-wordbreak"]},"text-accentfold":{"requires":["array-extras","text-data-accentfold"]},"text-data-accentfold":{},"text-data-wordbreak":{},"text-wordbreak":{"requires":["array-extras","text-data-wordbreak"]},"transition":{"use":["transition-native","transition-timer"]},"transition-native":{"requires":["node-base"]},"transition-timer":{"requires":["transition-native","node-style"]},"uploader":{"requires":["event-custom","node","base","swf"]},"view":{"requires":["base-build","node-event-delegate"]},"widget":{"skinnable":true,"use":["widget-base","widget-htmlparser","widget-uievents","widget-skin"]},"widget-anim":{"requires":["plugin","anim-base","widget"]},"widget-base":{"requires":["attribute","event-focus","base-base","base-pluginhost","node-base","node-style","classnamemanager"]},"widget-base-ie":{"condition":{"name":"widget-base-ie","trigger":"widget-base","ua":"ie"},"requires":["widget-base"]},"widget-child":{"requires":["base-build","widget"]},"widget-htmlparser":{"requires":["widget-base"]},"widget-locale":{"requires":["widget-base"]},"widget-modality":{"requires":["widget","plugin","gallery-outside-events","base-build"],"skinnable":false},"widget-parent":{"requires":["base-build","arraylist","widget"]},"widget-position":{"requires":["base-build","node-screen","widget"]},"widget-position-align":{"requires":["widget-position"]},"widget-position-constrain":{"requires":["widget-position"]},"widget-skin":{"requires":["widget-base"]},"widget-stack":{"requires":["base-build","widget"],"skinnable":true},"widget-stdmod":{"requires":["base-build","widget"]},"widget-uievents":{"requires":["widget-base","node-event-delegate"]},"yql":{"requires":["jsonp","jsonp-url"]},"yui":{"use":["yui-base","get","features","intl-base","yui-log","yui-later","loader-base","loader-rollup","loader-yui3"]},"yui-base":{},"yui-later":{"requires":["yui-base"]},"yui-log":{"requires":["yui-base"]},"yui-rls":{"use":["yui-base","get","features","intl-base","rls","yui-log","yui-later"]},"yui-throttle":{"requires":["yui-base"]}};YUI.Env[a.version].md5="8deea7d26f0f85ddcacf3aa4da9bfed6";},"3.4.0",{requires:["loader-base"]});YUI.add("yui",function(a){},"3.4.0",{use:["yui-base","get","features","intl-base","yui-log","yui-later","loader-base","loader-rollup","loader-yui3"]});YUI.add("oop",function(h){var d=h.Lang,c=h.Array,b=Object.prototype,a="_~yuim~_",e=b.hasOwnProperty,g=b.toString;function f(l,k,m,i,j){if(l&&l[j]&&l!==h){return l[j].call(l,k,m);}else{switch(c.test(l)){case 1:return c[j](l,k,m);case 2:return c[j](h.Array(l,0,true),k,m);default:return h.Object[j](l,k,m,i);}}}h.augment=function(i,k,r,o,s){var n=i.prototype,m=n&&k,q=k.prototype,v=n||i,j,u,p,l,t;s=s?h.Array(s):[];if(m){u={};p={};l={};j=function(x,w){if(r||!(w in n)){if(g.call(x)==="[object Function]"){l[w]=x;u[w]=p[w]=function(){return t(this,x,arguments);};}else{u[w]=x;}}};t=function(w,y,z){for(var x in l){if(e.call(l,x)&&w[x]===p[x]){w[x]=l[x];}}k.apply(w,s);return y.apply(w,z);};if(o){h.Array.each(o,function(w){if(w in q){j(q[w],w);}});}else{h.Object.each(q,j,null,true);}}h.mix(v,u||q,r,o);if(!m){k.apply(v,s);}return i;};h.aggregate=function(k,j,i,l){return h.mix(k,j,i,l,0,true);};h.extend=function(l,k,i,n){if(!k||!l){h.error("extend failed, verify dependencies");}var m=k.prototype,j=h.Object(m);l.prototype=j;j.constructor=l;l.superclass=m;if(k!=Object&&m.constructor==b.constructor){m.constructor=k;}if(i){h.mix(j,i,true);}if(n){h.mix(l,n,true);}return l;};h.each=function(k,j,l,i){return f(k,j,l,i,"each");};h.some=function(k,j,l,i){return f(k,j,l,i,"some");};h.clone=function(l,m,r,s,k,q){if(!d.isObject(l)){return l;}if(h.instanceOf(l,YUI)){return l;}var n,j=q||{},i,p=h.each;switch(d.type(l)){case"date":return new Date(l);case"regexp":return l;case"function":return l;case"array":n=[];break;default:if(l[a]){return j[l[a]];}i=h.guid();n=(m)?{}:h.Object(l);l[a]=i;j[i]=l;}if(!l.addEventListener&&!l.attachEvent){p(l,function(t,o){if((o||o===0)&&(!r||(r.call(s||this,t,o,this,l)!==false))){if(o!==a){if(o=="prototype"){}else{this[o]=h.clone(t,m,r,s,k||l,j);}}}},n);}if(!q){h.Object.each(j,function(t,o){if(t[a]){try{delete t[a];}catch(u){t[a]=null;}}},this);j=null;}return n;};h.bind=function(i,k){var j=arguments.length>2?h.Array(arguments,2,true):null;return function(){var m=d.isString(i)?k[i]:i,l=(j)?j.concat(h.Array(arguments,0,true)):arguments;return m.apply(k||m,l);};};h.rbind=function(i,k){var j=arguments.length>2?h.Array(arguments,2,true):null;return function(){var m=d.isString(i)?k[i]:i,l=(j)?h.Array(arguments,0,true).concat(j):arguments;return m.apply(k||m,l);};};},"3.4.0",{requires:["yui-base"]});YUI.add("dom-core",function(e){var n="nodeType",c="ownerDocument",b="documentElement",a="defaultView",g="parentWindow",j="tagName",k="parentNode",i="previousSibling",l="nextSibling",h="contains",d="compareDocumentPosition",m=[],f={byId:function(p,o){return f.allById(p,o)[0]||null;
-},ancestor:function(p,q,r){var o=null;if(r){o=(!q||q(p))?p:null;}return o||f.elementByAxis(p,k,q,null);},ancestors:function(q,r,s){var p=f.ancestor.apply(f,arguments),o=(p)?[p]:[];while((p=f.ancestor(p,r))){if(p){o.unshift(p);}}return o;},elementByAxis:function(o,r,q,p){while(o&&(o=o[r])){if((p||o[j])&&(!q||q(o))){return o;}}return null;},contains:function(p,q){var o=false;if(!q||!p||!q[n]||!p[n]){o=false;}else{if(p[h]){if(e.UA.opera||q[n]===1){o=p[h](q);}else{o=f._bruteContains(p,q);}}else{if(p[d]){if(p===q||!!(p[d](q)&16)){o=true;}}}}return o;},inDoc:function(q,r){var p=false,o;if(q&&q.nodeType){(r)||(r=q[c]);o=r[b];if(o&&o.contains&&q.tagName){p=o.contains(q);}else{p=f.contains(o,q);}}return p;},allById:function(t,o){o=o||e.config.doc;var p=[],q=[],r,s;if(o.querySelectorAll){q=o.querySelectorAll('[id="'+t+'"]');}else{if(o.all){p=o.all(t);if(p){if(p.nodeName){if(p.id===t){q.push(p);p=m;}else{p=[p];}}if(p.length){for(r=0;s=p[r++];){if(s.id===t||(s.attributes&&s.attributes.id&&s.attributes.id.value===t)){q.push(s);}}}}}else{q=[f._getDoc(o).getElementById(t)];}}return q;},isWindow:function(o){return !!(o&&o.alert&&o.document);},_removeChildNodes:function(o){while(o.firstChild){o.removeChild(o.firstChild);}},siblings:function(r,q){var o=[],p=r;while((p=p[i])){if(p[j]&&(!q||q(p))){o.unshift(p);}}p=r;while((p=p[l])){if(p[j]&&(!q||q(p))){o.push(p);}}return o;},_bruteContains:function(o,p){while(p){if(o===p){return true;}p=p.parentNode;}return false;},_getRegExp:function(p,o){o=o||"";f._regexCache=f._regexCache||{};if(!f._regexCache[p+o]){f._regexCache[p+o]=new RegExp(p,o);}return f._regexCache[p+o];},_getDoc:function(o){var p=e.config.doc;if(o){p=(o[n]===9)?o:o[c]||o.document||e.config.doc;}return p;},_getWin:function(o){var p=f._getDoc(o);return p[a]||p[g]||e.config.win;},_batch:function(o,w,u,t,s,q){w=(typeof w==="string")?f[w]:w;var x,r=0,p,v;if(w&&o){while((p=o[r++])){x=x=w.call(f,p,u,t,s,q);if(typeof x!=="undefined"){(v)||(v=[]);v.push(x);}}}return(typeof v!=="undefined")?v:o;},wrap:function(r,p){var q=e.DOM.create(p),o=q.getElementsByTagName("*");if(o.length){q=o[o.length-1];}if(r.parentNode){r.parentNode.replaceChild(q,r);}q.appendChild(r);},unwrap:function(r){var p=r.parentNode,q=p.lastChild,o=r,s;if(p){s=p.parentNode;if(s){while(r!==q){o=r.nextSibling;s.insertBefore(r,p);r=o;}s.replaceChild(q,p);}else{p.removeChild(r);}}},generateID:function(o){var p=o.id;if(!p){p=e.stamp(o);o.id=p;}return p;}};e.DOM=f;},"3.4.0",{requires:["oop","features"]});YUI.add("dom-attrs",function(h){var e=h.config.doc.documentElement,b=h.DOM,a="tagName",g="ownerDocument",c="",f=h.Features.add,d=h.Features.test;h.mix(b,{getText:(e.textContent!==undefined)?function(j){var i="";if(j){i=j.textContent;}return i||"";}:function(j){var i="";if(j){i=j.innerText||j.nodeValue;}return i||"";},setText:(e.textContent!==undefined)?function(i,j){if(i){i.textContent=j;}}:function(i,j){if("innerText" in i){i.innerText=j;}else{if("nodeValue" in i){i.nodeValue=j;}}},CUSTOM_ATTRIBUTES:(!e.hasAttribute)?{"for":"htmlFor","class":"className"}:{"htmlFor":"for","className":"class"},setAttribute:function(k,i,l,j){if(k&&i&&k.setAttribute){i=b.CUSTOM_ATTRIBUTES[i]||i;k.setAttribute(i,l,j);}},getAttribute:function(l,i,k){k=(k!==undefined)?k:2;var j="";if(l&&i&&l.getAttribute){i=b.CUSTOM_ATTRIBUTES[i]||i;j=l.getAttribute(i,k);if(j===null){j="";}}return j;},VALUE_SETTERS:{},VALUE_GETTERS:{},getValue:function(k){var j="",i;if(k&&k[a]){i=b.VALUE_GETTERS[k[a].toLowerCase()];if(i){j=i(k);}else{j=k.value;}}if(j===c){j=c;}return(typeof j==="string")?j:"";},setValue:function(i,j){var k;if(i&&i[a]){k=b.VALUE_SETTERS[i[a].toLowerCase()];if(k){k(i,j);}else{i.value=j;}}},creators:{}});f("value-set","select",{test:function(){var i=h.config.doc.createElement("select");i.innerHTML="";i.value="2";return(i.value&&i.value==="2");}});if(!d("value-set","select")){b.VALUE_SETTERS.select=function(m,n){for(var k=0,j=m.getElementsByTagName("option"),l;l=j[k++];){if(b.getValue(l)===n){l.selected=true;break;}}};}h.mix(b.VALUE_GETTERS,{button:function(i){return(i.attributes&&i.attributes.value)?i.attributes.value.value:"";}});h.mix(b.VALUE_SETTERS,{button:function(j,k){var i=j.attributes.value;if(!i){i=j[g].createAttribute("value");j.setAttributeNode(i);}i.value=k;}});h.mix(b.VALUE_GETTERS,{option:function(j){var i=j.attributes;return(i.value&&i.value.specified)?j.value:j.text;},select:function(j){var k=j.value,i=j.options;if(i&&i.length){if(j.multiple){}else{k=b.getValue(i[j.selectedIndex]);}}return k;}});},"3.4.0",{requires:["dom-core"]});YUI.add("dom-create",function(a){var c=/<([a-z]+)/i,d=a.DOM,i=a.Features.add,g=a.Features.test,f={},e=function(m,k){var n=a.config.doc.createElement("div"),l=true;n.innerHTML=m;if(!n.firstChild||n.firstChild.tagName!==k.toUpperCase()){l=false;}return l;},j=/(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s* Execute the supplied method before the specified function. Wrapping
+ * function may optionally return an instance of the following classes to
+ * further alter runtime behavior: Execute the supplied method after the specified function. Wrapping
+ * function may optionally return an instance of the following classes to
+ * further alter runtime behavior: The static properties Execute the wrapped method. All arguments are passed into the wrapping
+ * functions. If any of the before wrappers return an instance of
+ * The return value will be the return value of the wrapped function or one
+ * provided by a wrapper function via an instance of Execute the supplied method before the specified function. Wrapping
- * function may optionally return an instance of the following classes to
- * further alter runtime behavior: Execute the supplied method after the specified function. Wrapping
- * function may optionally return an instance of the following classes to
- * further alter runtime behavior: The static properties Execute the wrapped method. All arguments are passed into the wrapping
- * functions. If any of the before wrappers return an instance of
- * The return value will be the return value of the wrapped function or one
- * provided by a wrapper function via an instance of The callback is executed with a single parameter:
- * the custom object parameter, if provided. The callback is executed with a single parameter:
- * the custom object parameter, if provided.
- * An augmentable class, which provides the augmented class with the ability to host plugins.
- * It adds plug and unplug methods to the augmented class, which can
- * be used to add or remove plugins from instances of the class.
- * Plugins can also be added through the constructor configuration object passed to the host class' constructor using
- * the "plugins" property. Supported values for the "plugins" property are those defined by the plug method.
- *
- * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host):
- * currentTarget
will also be set to the matching Node. The
+ * containing Node from which the subscription was originally made can be
+ * referenced as e.container
.
+ *
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param spec {String|Function} a selector that must match the target of the
+ * event or a function to test target and its parents for a match
+ * @param context {Object} optional argument that specifies what 'this' refers to.
+ * @param args* {any} 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type) {
+ var args = Y.Array(arguments, 0, true),
+ index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2;
- /**
- * @description Fires event "io:complete" and creates, fires a
- * transaction-specific "complete" event, if config.on.complete is
- * defined.
- *
- * @method _ioComplete
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioComplete(o, c) {
- var r = o.e ? { status: 0, statusText: o.e } : o.c,
- a = c.arguments;
+ args.splice(index, 0, this._node);
- if (a) {
- Y.fire(E_COMPLETE, o.id, r, a);
- }
- else {
- Y.fire(E_COMPLETE, o.id, r);
- }
+ return Y.delegate.apply(Y, args);
+};
- if (c.on && c.on.complete) {
- _tE('complete', c).fire(o.id, r);
- }
- }
- /**
- * @description Fires event "io:end" and creates, fires a
- * transaction-specific "end" event, if config.on.end is
- * defined.
- *
- * @method _ioEnd
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioEnd(o, c) {
- var a = c.arguments;
+}, '3.4.0' ,{requires:['node-base', 'event-delegate']});
+YUI.add('node-pluginhost', function(Y) {
- if (a) {
- Y.fire(E_END, o.id, a);
- }
- else {
- Y.fire(E_END, o.id);
- }
+/**
+ * @module node
+ * @submodule node-pluginhost
+ */
- if (c.on && c.on.end) {
- _tE('end', c).fire(o.id);
- }
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method plug
+ * @static
+ * @for Node
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
- _destroy(o);
- }
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
- /**
- * @description Fires event "io:success" and creates, fires a
- * transaction-specific "success" event, if config.on.success is
- * defined.
- *
- * @method _ioSuccess
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioSuccess(o, c) {
- var a = c.arguments;
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
- if (a) {
- Y.fire(E_SUCCESS, o.id, o.c, a);
- }
- else {
- Y.fire(E_SUCCESS, o.id, o.c);
- }
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
- if (c.on && c.on.success) {
- _tE('success', c).fire(o.id, o.c);
- }
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
- _ioEnd(o, c);
- }
- /**
- * @description Fires event "io:failure" and creates, fires a
- * transaction-specific "failure" event, if config.on.failure is
- * defined.
- *
- * @method _ioFailure
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioFailure(o, c) {
- var r = o.e ? { status: 0, statusText: o.e } : o.c,
- a = c.arguments;
+}, '3.4.0' ,{requires:['node-base', 'pluginhost']});
+YUI.add('node-screen', function(Y) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
+
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
- if (a) {
- Y.fire(E_FAILURE, o.id, r, a);
- }
- else {
- Y.fire(E_FAILURE, o.id, r);
- }
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
- if (c.on && c.on.failure) {
- _tE('failure', c).fire(o.id, r);
- }
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
- _ioEnd(o, c);
- }
+ /**
+ * Pixel distance the page has been scrolled horizontally
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
- /**
- * @description Resends an XDR transaction, using the Flash tranport,
- * if the native transport fails.
- *
- * @method _resend
- * @private
- * @static
+ /**
+ * Pixel distance the page has been scrolled vertically
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
- * @param {object} o - Transaction object generated by _create().
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _resend(o, uri, c, d) {
- _destroy(o);
- c.xdr.use = 'flash';
- // If the original request included serialized form data and
- // additional data are defined in the configuration, it must
- // be reset to prevent data duplication.
- c.data = c.form && d ? d : null;
-
- return Y.io(uri, c, o.id);
+ return Y.DOM[name].apply(this, args);
+ }
+ };
}
+);
- /**
- * @description Method that concatenates string data for HTTP GET transactions.
- *
- * @method _concat
- * @private
- * @static
- * @param {string} s - URI or root data.
- * @param {string} d - data to be concatenated onto URI.
- * @return int
- */
- function _concat(s, d) {
- s += (s.indexOf('?') === -1 ? '?' : '&') + d;
- return s;
- }
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
- /**
- * @description Method that stores default client headers for all transactions.
- * If a label is passed with no value argument, the header will be deleted.
- *
- * @method _setHeader
- * @private
- * @static
- * @param {string} l - HTTP header
- * @param {string} v - HTTP header value
- * @return int
- */
- function _setHeader(l, v) {
- if (v) {
- _headers[l] = v;
- }
- else {
- delete _headers[l];
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollLeft for ' + node, 'error', 'Node');
}
}
+};
- /**
- * @description Method that sets all HTTP headers to be sent in a transaction.
- *
- * @method _setHeaders
- * @private
- * @static
- * @param {object} o - XHR instance for the specific transaction.
- * @param {object} h - HTTP headers for the specific transaction, as defined
- * in the configuration object passed to YUI.io().
- * @return void
- */
- function _setHeaders(o, h) {
- var p;
- h = h || {};
-
- for (p in _headers) {
- if (_headers.hasOwnProperty(p)) {
- if (!h[p]) {
- h[p] = _headers[p];
- }
- }
- }
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
- for (p in h) {
- if (h.hasOwnProperty(p)) {
- if (h[p] !== 'disable') {
- o.setRequestHeader(p, h[p]);
- }
- }
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollTop for ' + node, 'error', 'Node');
}
}
+};
- /**
- * @description Terminates a transaction due to an explicit abort or
- * timeout.
- *
- * @method _ioCancel
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {string} s - Identifies timed out or aborted transaction.
- *
- * @return void
- */
- function _ioCancel(o, s) {
- if (o && o.c) {
- o.e = s;
- o.c.abort();
- }
- }
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
- /**
- * @description Starts timeout count if the configuration object
- * has a defined timeout property.
- *
- * @method _startTimeout
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} t - Timeout in milliseconds.
- * @return void
- */
- function _startTimeout(o, t) {
- _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, t);
- }
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
- /**
- * @description Clears the timeout interval started by _startTimeout().
- *
- * @method _clearTimeout
- * @private
- * @static
- * @param {number} id - Transaction id.
- * @return void
- */
- function _clearTimeout(id) {
- w.clearTimeout(_timeout[id]);
- delete _timeout[id];
- }
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
- /**
- * @description Method that determines if a transaction response qualifies
- * as success or failure, based on the response HTTP status code, and
- * fires the appropriate success or failure events.
- *
- * @method _handleResponse
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} c - Configuration object passed to io().
- * @return void
- */
- function _handleResponse(o, c) {
- var status = o.c.status;
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
- // IE reports HTTP 204 as HTTP 1223.
- if (status === 0 && o.c.responseText || status === 1223) {
- status = 200;
- }
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
- if (status >= 200 && status < 300) {
- _ioSuccess(o, c);
- }
- else {
- _ioFailure(o, c);
- }
- }
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY',
- /**
- * @description Event handler bound to onreadystatechange.
- *
- * @method _readyState
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} c - Configuration object passed to YUI.io().
- * @return void
- */
- function _readyState(o, c) {
- if (o.c.readyState === 4) {
- if (c.timeout) {
- _clearTimeout(o.id);
- }
+/**
+ * Swaps the XY position of this node with another node.
+ * @method swapXY
+ * @param {Y.Node || HTMLElement} otherNode The node to swap with.
+ * @chainable
+ */
+ 'swapXY'
+]);
- w.setTimeout(
- function() {
- _ioComplete(o, c);
- _handleResponse(o, c);
- }, 0);
- }
- }
+/**
+ * @module node
+ * @submodule node-screen
+ */
- /**
- * @description Method for requesting a transaction. _io() is implemented as
- * yui.io(). Each transaction may include a configuration object. Its
- * properties are:
- *
- * method: HTTP method verb (e.g., GET or POST). If this property is not
- * not defined, the default value will be GET.
- *
- * data: This is the name-value string that will be sent as the transaction
- * data. If the request is HTTP GET, the data become part of
- * querystring. If HTTP POST, the data are sent in the message body.
- *
- * xdr: Defines the transport to be used for cross-domain requests. By
- * setting this property, the transaction will use the specified
- * transport instead of XMLHttpRequest.
- * The properties are:
- * {
- * use: Specify the transport to be used: 'flash' and 'native'
- * dataType: Set the value to 'XML' if that is the expected
- * response content type.
- * }
- *
- *
- * form: This is a defined object used to process HTML form as data. The
- * properties are:
- * {
- * id: Node object or id of HTML form.
- * useDisabled: Boolean value to allow disabled HTML form field
- * values to be sent as part of the data.
- * }
- *
- * on: This is a defined object used to create and handle specific
- * events during a transaction lifecycle. These events will fire in
- * addition to the global io events. The events are:
- * start - This event is fired when a request is sent to a resource.
- * complete - This event fires when the transaction is complete.
- * success - This event fires when the response status resolves to
- * HTTP 2xx.
- * failure - This event fires when the response status resolves to
- * HTTP 4xx, 5xx; and, for all transaction exceptions,
- * including aborted transactions and transaction timeouts.
- * end - This even is fired at the conclusion of the transaction
- * lifecycle, after a success or failure resolution.
- *
- * The properties are:
- * {
- * start: function(id, arguments){},
- * complete: function(id, responseobject, arguments){},
- * success: function(id, responseobject, arguments){},
- * failure: function(id, responseobject, arguments){},
- * end: function(id, arguments){}
- * }
- * Each property can reference a function or be written as an
- * inline function.
- *
- * sync: To enable synchronous transactions, set the configuration property
- * "sync" to true; the default behavior is false. Synchronous
- * transactions are limited to same-domain requests only.
- *
- * context: Object reference for all defined transaction event handlers
- * when it is implemented as a method of a base object. Defining
- * "context" will set the reference of "this," used in the
- * event handlers, to the context value. In the case where
- * different event handlers all have different contexts,
- * use Y.bind() to set the execution context, bypassing this
- * configuration.
- *
- * headers: This is a defined object of client headers, as many as.
- * desired for the transaction. The object pattern is:
- * { 'header': 'value' }.
- *
- * timeout: This value, defined as milliseconds, is a time threshold for the
- * transaction. When this threshold is reached, and the transaction's
- * Complete event has not yet fired, the transaction will be aborted.
- *
- * arguments: Object, array, string, or number passed to all registered
- * event handlers. This value is available as the second
- * argument in the "start" and "abort" event handlers; and, it is
- * the third argument in the "complete", "success", and "failure"
- * event handlers.
- *
- * @method _io
- * @private
- * @static
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- * @param {number} i - transaction id, if already set.
- * @return object
- */
- function _io(uri, c, i) {
- var f, o, d, m, r, s, oD, a, j, usr, pwd,
- u = uri;
- c = Y.Object(c) || {};
- o = _create(c.xdr || c.form, i);
- usr = c.username || null;
- pwd = c.password || null;
- m = c.method ? c.method = c.method.toUpperCase() : c.method = 'GET';
- s = c.sync;
- oD = c.data;
-
- // Serialize an object into a key-value string using
- // querystring-stringify-simple.
- c.data = (Y.Lang.isObject(c.data) && Y.QueryString) ? Y.QueryString.stringify(c.data) : c.data;
-
- if (c.form) {
- if (c.form.upload) {
- // This is a file upload transaction, calling
- // upload() in io-upload-iframe.
- return Y.io.upload(o, uri, c);
- }
- else {
- // Serialize HTML form data into a key-value string.
- f = Y.io._serialize(c.form, c.data);
- if (m === 'POST' || m === 'PUT') {
- c.data = f;
- }
- else if (m === 'GET') {
- uri = _concat(uri, f);
- }
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = this.getDOMNode(),
+ region;
+
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
}
}
+ if (Y.DOM.isWindow(node)) {
+ region = Y.DOM.viewportRegion(node);
+ } else {
+ region = Y.DOM.region(node);
+ }
+ return region;
+ }
+};
- if (c.data) {
- switch (m) {
- case 'GET':
- case 'HEAD':
- case 'DELETE':
- uri = _concat(uri, c.data);
- c.data = null;
- Y.log('HTTP' + m + ' with data. The querystring is: ' + uri, 'info', 'io');
- break;
- case 'POST':
- case 'PUT':
- // If Content-Type is defined in the configuration object, or
- // or as a default header, it will be used instead of
- // 'application/x-www-form-urlencoded; charset=UTF-8'
- c.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, c.headers);
- break;
- }
- }
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
- if (o.t) {
- // Cross-domain request or custom transport detected.
- return Y.io.xdr(uri, o, c);
- }
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
- if (!s) {
- o.c.onreadystatechange = function() { _readyState(o, c); };
- }
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
- try {
- // Determine if request is to be set as
- // synchronous or asynchronous.
- o.c.open(m, uri, s ? false : true, usr, pwd);
- _setHeaders(o.c, c.headers);
- _ioStart(o.id, c);
-
- // Will work only in browsers that implement the
- // Cross-Origin Resource Sharing draft.
- if (c.xdr && c.xdr.credentials) {
- if (!Y.UA.ie) {
- o.c.withCredentials = true;
- }
- }
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
- // Using "null" with HTTP POST will result in a request
- // with no Content-Length header defined.
- o.c.send(c.data || '');
- if (s) {
- // Create a response object for synchronous transactions,
- // merging ID and arguments fields into a single object.
- d = o.c;
- a = ['status', 'statusText', 'responseText', 'responseXML'];
- r = c.arguments ? { id: o.id, arguments: c.arguments } : { id: o.id };
- r.getAllResponseHeaders = function() { return d.getAllResponseHeaders(); };
- r.getResponseHeader = function(h) { return d.getResponseHeader(h); };
+}, '3.4.0' ,{requires:['node-base', 'dom-screen']});
+YUI.add('node-style', function(Y) {
- for (j = 0; j < 4; j++) {
- r[a[j]] = o.c[a[j]];
- }
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
- _ioComplete(o, c);
- _handleResponse(o, c);
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
- return r;
- }
- }
- catch(e) {
- if (c.xdr && c.xdr.use === 'native') {
- // This exception is usually thrown by browsers
- // that do not support XMLHttpRequest Level 2.
- // Retry the request with the XDR transport set
- // to 'flash'. If the Flash transport is not
- // initialized or available, the transaction
- // will resolve to a transport error.
- return _resend(o, u, c, oD);
- }
- else {
- _ioComplete(o, c);
- _handleResponse(o, c);
- }
- }
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
- // If config.timeout is defined, and the request is standard XHR,
- // initialize timeout polling.
- if (c.timeout) {
- _startTimeout(o, c.timeout);
- Y.log('Configuration timeout set to: ' + c.timeout, 'info', 'io');
- }
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
- return {
- id: o.id,
- abort: function() {
- return o.c ? _ioCancel(o, 'abort') : false;
- },
- isInProgress: function() {
- return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
- }
- };
- }
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
- _io.start = _ioStart;
- _io.complete = _ioComplete;
- _io.success = _ioSuccess;
- _io.failure = _ioFailure;
- _io.end = _ioEnd;
- _io._id = _id;
- _io._timeout = _timeout;
-
- //--------------------------------------
- // Begin public interface definition
- //--------------------------------------
- /**
- * @description Method that stores default client headers for all transactions.
- * If a label is passed with no value argument, the header will be deleted.
- * This is the interface for _setHeader().
- *
- * @method header
- * @public
- * @static
- * @param {string} l - HTTP header
- * @param {string} v - HTTP header value
- * @return int
- */
- _io.header = _setHeader;
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
- /**
- * @description Method for requesting a transaction. This
- * is the interface for _io().
- *
- * @method io
- * @public
- * @static
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- * @return object
- */
- Y.io = _io;
- Y.io.http = _io;
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
-}, '3.4.0' ,{requires:['event-custom-base', 'querystring-stringify-simple']});
+}, '3.4.0' ,{requires:['dom-style', 'node-base']});
YUI.add('querystring-stringify-simple', function(Y) {
/*global Y */
@@ -19419,6 +15326,675 @@ QueryString.stringify = function (obj, c) {
}, '3.4.0' ,{requires:['yui-base']});
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ // Window reference
+ var L = Y.Lang,
+ // List of events that comprise the IO event lifecycle.
+ E = ['start', 'complete', 'end', 'success', 'failure'],
+ // Whitelist of used XHR response object properties.
+ P = ['status', 'statusText', 'responseText', 'responseXML'],
+ aH = 'getAllResponseHeaders',
+ oH = 'getResponseHeader',
+ w = Y.config.win,
+ xhr = w.XMLHttpRequest,
+ xdr = w.XDomainRequest,
+ _i = 0;
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class IO
+ * @constructor
+ * @param {object} c - Object of EventTarget's publish method configurations
+ * used to configure IO's events.
+ */
+ function IO (c) {
+ var io = this;
+
+ io._uid = 'io:' + _i++;
+ io._init(c);
+ Y.io._map[io._uid] = io;
+ }
+
+ IO.prototype = {
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+
+ /**
+ * @description A counter that increments for each transaction.
+ *
+ * @property _id
+ * @private
+ * @type int
+ */
+ _id: 0,
+
+ /**
+ * @description Object of IO HTTP headers sent with each transaction.
+ *
+ * @property _headers
+ * @private
+ * @type object
+ */
+ _headers: {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @type object
+ */
+ _timeout: {},
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+
+ _init: function(c) {
+ var io = this, i;
+
+ io.cfg = c || {};
+
+ Y.augment(io, Y.EventTarget);
+ for (i = 0; i < 5; i++) {
+ // Publish IO global events with configurations, if any.
+ // IO global events are set to broadcast by default.
+ // These events use the "io:" namespace.
+ io.publish('io:' + E[i], Y.merge({ broadcast: 1 }, c));
+ // Publish IO transaction events with configurations, if
+ // any. These events use the "io-trn:" namespace.
+ io.publish('io-trn:' + E[i], c);
+ }
+ },
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ _create: function(c, i) {
+ var io = this,
+ o = { id: L.isNumber(i) ? i : io._id++, uid: io._uid },
+ x = c.xdr,
+ u = x ? x.use : c.form && c.form.upload ? 'iframe' : 'xhr',
+ ie = (x && x.use === 'native' && xdr),
+ t = io._transport;
+
+ switch (u) {
+ case 'native':
+ case 'xhr':
+ o.c = ie ? new xdr() : xhr ? new xhr() : new ActiveXObject('Microsoft.XMLHTTP');
+ o.t = ie ? true : false;
+ break;
+ default:
+ o.c = t ? t[u] : {};
+ o.t = true;
+ }
+
+ return o;
+ },
+
+ _destroy: function(o) {
+ if (w) {
+ if (xhr && o.t === true) {
+ o.c.onreadystatechange = null;
+ }
+ else if (Y.UA.ie) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ o.c.abort();
+ }
+ }
+
+ o.c = null;
+ o = null;
+ },
+
+ /**
+ * @description Method for creating and firing events.
+ *
+ * @method _evt
+ * @private
+ * @param {string} e - event to be published.
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ _evt: function(e, o, c) {
+ var io = this,
+ a = c['arguments'],
+ eF = io.cfg.emitFacade,
+ // Use old-style parameters or use an Event Facade
+ p = eF ? [{ id: o.id, data: o.c, cfg: c, arguments: a }] : [o.id],
+ // IO Global events namespace.
+ gE = "io:" + e,
+ // IO Transaction events namespace.
+ tE = "io-trn:" + e;
+
+ if (!eF) {
+ if (e === E[0] || e === E[2]) {
+ if (a) {
+ p.push(a);
+ }
+ }
+ else {
+ a ? p.push(o.c, a) : p.push(o.c);
+ }
+ }
+
+ p.unshift(gE);
+ io.fire.apply(io, p);
+ if (c.on) {
+ p[0] = tE;
+ io.once(tE, c.on[e], c.context || Y);
+ io.fire.apply(io, p);
+ }
+ },
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method start
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ start: function(o, c) {
+ this._evt(E[0], o, c);
+ },
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method complete
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ complete: function(o, c) {
+ this._evt(E[1], o, c);
+ },
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method end
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ end: function(o, c) {
+ this._evt(E[2], o, c);
+ this._destroy(o);
+ },
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method success
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ success: function(o, c) {
+ this._evt(E[3], o, c);
+ this.end(o, c);
+ },
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method failure
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ failure: function(o, c) {
+ this._evt(E[4], o, c);
+ this.end(o, c);
+ },
+
+ /**
+ * @description Retry an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _retry
+ * @private
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ _retry: function(o, uri, c) {
+ this._destroy(o);
+ c.xdr.use = 'flash';
+ return this.send(uri, c, o.id);
+ },
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ _concat: function(s, d) {
+ s += (s.indexOf('?') === -1 ? '?' : '&') + d;
+ return s;
+ },
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ setHeader: function(l, v) {
+ if (v) {
+ this._headers[l] = v;
+ }
+ else {
+ delete this._headers[l];
+ }
+ },
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ _setHeaders: function(o, h) {
+ h = Y.merge(this._headers, h);
+ Y.Object.each(h, function(v, p) {
+ if (v !== 'disable') {
+ o.setRequestHeader(p, h[p]);
+ }
+ });
+ },
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} t - Timeout in milliseconds.
+ * @return void
+ */
+ _startTimeout: function(o, t) {
+ var io = this;
+ io._timeout[o.id] = w.setTimeout(function() { io._abort(o, 'timeout'); }, t);
+ },
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ _clearTimeout: function(id) {
+ w.clearTimeout(this._timeout[id]);
+ delete this._timeout[id];
+ },
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _result
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ _result: function(o, c) {
+ var s = o.c.status;
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (s >= 200 && s < 300 || s === 1223) {
+ this.success(o, c);
+ }
+ else {
+ this.failure(o, c);
+ }
+ },
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _rS
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ _rS: function(o, c) {
+ var io = this;
+
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ io._clearTimeout(o.id);
+ }
+
+ // Yield in the event of request timeout or abort.
+ w.setTimeout(function() { io.complete(o, c); io._result(o, c); }, 0);
+ }
+ },
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _abort
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ _abort: function(o, s) {
+ if (o && o.c) {
+ o.e = s;
+ o.c.abort();
+ }
+ },
+
+ /**
+ * @description Method for requesting a transaction. send() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest.
+ * The properties are:
+ * {
+ * use: Specify the transport to be used: 'flash' and 'native'
+ * dataType: Set the value to 'XML' if that is the expected
+ * response content type.
+ * }
+ *
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: Node object or id of HTML form.
+ * useDisabled: Boolean value to allow disabled HTML form field
+ * values to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, arguments){},
+ * complete: function(id, responseobject, arguments){},
+ * success: function(id, responseobject, arguments){},
+ * failure: function(id, responseobject, arguments){},
+ * end: function(id, arguments){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * sync: To enable synchronous transactions, set the configuration property
+ * "sync" to true. Synchronous requests are limited to same-domain
+ * requests only.
+ *
+ * context: Object reference for all defined transaction event handlers
+ * when it is implemented as a method of a base object. Defining
+ * "context" will set the reference of "this," used in the
+ * event handlers, to the context value. In the case where
+ * different event handlers all have different contexts,
+ * use Y.bind() to set the execution context, instead.
+ *
+ * headers: This is a defined object of client headers, as many as
+ * desired for this specific transaction. The object pattern is:
+ * { 'header': 'value' }.
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ *
+ * arguments: User-defined data passed to all registered event handlers.
+ * This value is available as the second argument in the "start"
+ * and "end" event handlers. It is the third argument in the
+ * "complete", "success", and "failure" event handlers.
+ *
+ * @method send
+ * @private
+ * @
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set.
+ * @return object
+ */
+ send: function(uri, c, i) {
+ var o, m, r, s, d, io = this,
+ u = uri;
+ c = c ? Y.Object(c) : {};
+ o = io._create(c, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+ s = c.sync;
+ d = c.data;
+
+ // Serialize an object into a key-value string using
+ // querystring-stringify-simple.
+ if (L.isObject(d)) {
+ d = Y.QueryString.stringify(d);
+ }
+
+ if (c.form) {
+ if (c.form.upload) {
+ // This is a file upload transaction, calling
+ // upload() in io-upload-iframe.
+ return io.upload(o, uri, c);
+ }
+ else {
+ // Serialize HTML form data into a key-value string.
+ d = io._serialize(c.form, d);
+ }
+ }
+
+ if (d) {
+ switch (m) {
+ case 'GET':
+ case 'HEAD':
+ case 'DELETE':
+ u = io._concat(u, d);
+ d = '';
+ Y.log('HTTP' + m + ' with data. The querystring is: ' + u, 'info', 'io');
+ break;
+ case 'POST':
+ case 'PUT':
+ // If Content-Type is defined in the configuration object, or
+ // or as a default header, it will be used instead of
+ // 'application/x-www-form-urlencoded; charset=UTF-8'
+ c.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, c.headers);
+ break;
+ }
+ }
+
+ if (o.t) {
+ // Cross-domain request or custom transport configured.
+ return io.xdr(u, o, c);
+ }
+
+ if (!s) {
+ o.c.onreadystatechange = function() { io._rS(o, c); };
+ }
+
+ try {
+ // Determine if request is to be set as
+ // synchronous or asynchronous.
+ o.c.open(m, u, s ? false : true, c.username || null, c.password || null);
+ io._setHeaders(o.c, c.headers || {});
+ io.start(o, c);
+
+ // Will work only in browsers that implement the
+ // Cross-Origin Resource Sharing draft.
+ if (c.xdr && c.xdr.credentials) {
+ if (!Y.UA.ie) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ // Using "null" with HTTP POST will result in a request
+ // with no Content-Length header defined.
+ o.c.send(d);
+
+ if (s) {
+ // Create a response object for synchronous transactions,
+ // mixing id and arguments properties with the xhr
+ // properties whitelist.
+ r = Y.mix({ id: o.id, 'arguments': c['arguments'] }, o.c, false, P);
+ r[aH] = function() { return o.c[aH](); };
+ r[oH] = function(h) { return o.c[oH](h); };
+ io.complete(o, c);
+ io._result(o, c);
+
+ return r;
+ }
+ }
+ catch(e) {
+ if (o.t) {
+ // This exception is usually thrown by browsers
+ // that do not support XMLHttpRequest Level 2.
+ // Retry the request with the XDR transport set
+ // to 'flash'. If the Flash transport is not
+ // initialized or available, the transaction
+ // will resolve to a transport error.
+ return io._retry(o, uri, c);
+ }
+ else {
+ io.complete(o, c);
+ io._result(o, c);
+ }
+ }
+
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ io._startTimeout(o, c.timeout);
+ Y.log('Configuration timeout set to: ' + c.timeout, 'info', 'io');
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? io._abort(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ },
+ io: io
+ };
+ }
+ };
+
+ /**
+ * @description Method for requesting a transaction.
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} u - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = function(u, c) {
+ // Calling IO through the static interface will use and reuse
+ // an instance of IO.
+ var o = Y.io._map['io:0'] || new IO();
+ return o.send.apply(o, [u, c]);
+ };
+
+ Y.IO = IO;
+ // Map of all IO instances created.
+ Y.io._map = {};
+
+
+
+}, '3.4.0' ,{requires:['event-custom-base', 'querystring-stringify-simple']});
YUI.add('json-parse', function(Y) {
/**
@@ -19471,8 +16047,7 @@ function fromGlobal(ref) {
* @private
*/
var _JSON = fromGlobal('JSON'),
- // Create an indirect reference to eval to allow for minification
- _eval = fromGlobal('eval'),
+
Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
useNative = !!Native,
@@ -19604,7 +16179,7 @@ var _JSON = fromGlobal('JSON'),
// Eval the text into a JavaScript data structure, apply any
// reviver function, and return
- return _revive( _eval('(' + s + ')'), reviver );
+ return _revive( eval('(' + s + ')'), reviver );
}
throw new SyntaxError('JSON.parse');
@@ -19649,7 +16224,7 @@ Y.JSON.useNativeParse = useNative;
}, '3.4.0' );
-YUI.add('transition-native', function(Y) {
+YUI.add('transition', function(Y) {
/**
* Provides the transition method for Node.
@@ -19659,19 +16234,32 @@ YUI.add('transition-native', function(Y) {
* @requires node-style
*/
-var TRANSITION = '-webkit-transition',
- TRANSITION_CAMEL = 'WebkitTransition',
- TRANSITION_PROPERTY_CAMEL = 'WebkitTransitionProperty',
- TRANSITION_PROPERTY = '-webkit-transition-property',
- TRANSITION_DURATION = '-webkit-transition-duration',
- TRANSITION_TIMING_FUNCTION = '-webkit-transition-timing-function',
- TRANSITION_DELAY = '-webkit-transition-delay',
- TRANSITION_END = 'webkitTransitionEnd',
- ON_TRANSITION_END = 'onwebkittransitionend',
- TRANSFORM_CAMEL = 'WebkitTransform',
+var CAMEL_VENDOR_PREFIX = '',
+ VENDOR_PREFIX = '',
+ DOCUMENT = Y.config.doc,
+ DOCUMENT_ELEMENT = 'documentElement',
+ TRANSITION = 'transition',
+ TRANSITION_CAMEL = 'Transition',
+ TRANSITION_PROPERTY_CAMEL,
+ TRANSITION_PROPERTY,
+ TRANSITION_DURATION,
+ TRANSITION_TIMING_FUNCTION,
+ TRANSITION_DELAY,
+ TRANSITION_END,
+ ON_TRANSITION_END,
+ TRANSFORM_CAMEL,
EMPTY_OBJ = {},
+ VENDORS = [
+ 'Webkit',
+ 'Moz'
+ ],
+
+ VENDOR_TRANSITION_END = {
+ Webkit: 'webkitTransitionEnd'
+ },
+
/**
* A class for constructing transition instances.
* Adds the "transition" method to Node.
@@ -19683,11 +16271,6 @@ Transition = function() {
this.init.apply(this, arguments);
};
-Transition.fx = {};
-Transition.toggles = {};
-
-Transition._hasEnd = {};
-
Transition._toCamel = function(property) {
property = property.replace(/-([a-z])/gi, function(m0, m1) {
return m1.toUpperCase();
@@ -19698,11 +16281,7 @@ Transition._toCamel = function(property) {
Transition._toHyphen = function(property) {
property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {
- var str = '';
- if (m1) {
- str += '-' + m1.toLowerCase();
- }
- str += m2;
+ var str = ((m1) ? '-' + m1.toLowerCase() : '') + m2;
if (m3) {
str += '-' + m3.toLowerCase();
@@ -19714,15 +16293,42 @@ Transition._toHyphen = function(property) {
return property;
};
-
-Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
+Transition.SHOW_TRANSITION = 'fadeIn';
+Transition.HIDE_TRANSITION = 'fadeOut';
Transition.useNative = false;
-if (TRANSITION in Y.config.doc.documentElement.style) {
- Transition.useNative = true;
- Transition.supported = true; // TODO: remove
-}
+Y.Array.each(VENDORS, function(val) { // then vendor specific
+ var property = val + TRANSITION_CAMEL;
+ if (property in DOCUMENT[DOCUMENT_ELEMENT].style) {
+ CAMEL_VENDOR_PREFIX = val;
+ VENDOR_PREFIX = Transition._toHyphen(val) + '-';
+
+ Transition.useNative = true;
+ Transition.supported = true; // TODO: remove
+ Transition._VENDOR_PREFIX = val;
+ }
+});
+
+TRANSITION_CAMEL = CAMEL_VENDOR_PREFIX + TRANSITION_CAMEL;
+TRANSITION_PROPERTY_CAMEL = CAMEL_VENDOR_PREFIX + 'TransitionProperty';
+TRANSITION_PROPERTY = VENDOR_PREFIX + 'transition-property';
+TRANSITION_DURATION = VENDOR_PREFIX + 'transition-duration';
+TRANSITION_TIMING_FUNCTION = VENDOR_PREFIX + 'transition-timing-function';
+TRANSITION_DELAY = VENDOR_PREFIX + 'transition-delay';
+TRANSITION_END = 'transitionend';
+ON_TRANSITION_END = 'on' + CAMEL_VENDOR_PREFIX.toLowerCase() + 'transitionend';
+
+TRANSITION_END = VENDOR_TRANSITION_END[CAMEL_VENDOR_PREFIX] || TRANSITION_END;
+
+TRANSFORM_CAMEL = CAMEL_VENDOR_PREFIX + 'Transform';
+
+Transition.fx = {};
+Transition.toggles = {};
+
+Transition._hasEnd = {};
+
+Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
Y.Node.DOM_EVENTS[TRANSITION_END] = 1;
@@ -19915,7 +16521,7 @@ Transition.prototype = {
computed = getComputedStyle(node),
attrs = Transition._nodeAttrs[uid],
cssText = '',
- cssTransition = computed[TRANSITION_PROPERTY],
+ cssTransition = computed[Transition._toCamel(TRANSITION_PROPERTY)],
transitionText = TRANSITION_PROPERTY + ': ',
duration = TRANSITION_DURATION + ': ',
@@ -19928,9 +16534,9 @@ Transition.prototype = {
// preserve existing transitions
if (cssTransition !== 'all') {
transitionText += cssTransition + ',';
- duration += computed[TRANSITION_DURATION] + ',';
- easing += computed[TRANSITION_TIMING_FUNCTION] + ',';
- delay += computed[TRANSITION_DELAY] + ',';
+ duration += computed[Transition._toCamel(TRANSITION_DURATION)] + ',';
+ easing += computed[Transition._toCamel(TRANSITION_TIMING_FUNCTION)] + ',';
+ delay += computed[Transition._toCamel(TRANSITION_DELAY)] + ',';
}
@@ -19938,7 +16544,7 @@ Transition.prototype = {
for (name in attrs) {
hyphy = Transition._toHyphen(name);
attr = attrs[name];
- if (attrs.hasOwnProperty(name) && attr.transition === anim) {
+ if ((attr = attrs[name]) && attr.transition === anim) {
if (name in node.style) { // only native styles allowed
duration += anim._prepDur(attr.duration) + ',';
delay += anim._prepDur(attr.delay) + ',';
@@ -19961,7 +16567,7 @@ Transition.prototype = {
if (!Transition._hasEnd[uid]) {
//anim._detach = Y.on(TRANSITION_END, anim._onNativeEnd, node);
//node[ON_TRANSITION_END] = anim._onNativeEnd;
- node.addEventListener(TRANSITION_END, anim._onNativeEnd, false);
+ node.addEventListener(TRANSITION_END, anim._onNativeEnd, '');
Transition._hasEnd[uid] = true;
}
@@ -20011,7 +16617,7 @@ Transition.prototype = {
_endNative: function(name) {
var node = this._node,
- value = node.ownerDocument.defaultView.getComputedStyle(node, '')[TRANSITION_PROPERTY];
+ value = node.ownerDocument.defaultView.getComputedStyle(node, '')[Transition._toCamel(TRANSITION_PROPERTY)];
if (typeof value === 'string') {
value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ',');
@@ -20057,15 +16663,18 @@ Transition.prototype = {
},
destroy: function() {
- var anim = this;
+ var anim = this,
+ node = anim._node;
/*
if (anim._detach) {
anim._detach.detach();
}
*/
//anim._node[ON_TRANSITION_END] = null;
- node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
- anim._node = null;
+ if (node) {
+ node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
+ anim._node = null;
+ }
}
};
@@ -20149,7 +16758,7 @@ Y.Node.prototype.show = function(name, config, callback) {
callback = config;
config = name;
}
- name = this.SHOW_TRANSITION;
+ name = Transition.SHOW_TRANSITION;
}
this.transition(name, config, callback);
}
@@ -20181,7 +16790,7 @@ Y.Node.prototype.hide = function(name, config, callback) {
callback = config;
config = name;
}
- name = this.HIDE_TRANSITION;
+ name = Transition.HIDE_TRANSITION;
}
this.transition(name, config, callback);
} else if (name && !Y.Transition) { Y.log('unable to transition hide; missing transition module', 'warn', 'node'); // end if on nex
@@ -20327,352 +16936,489 @@ Transition.DEFAULT_TOGGLE = 'fade';
-}, '3.4.0' ,{requires:['node-base']});
-YUI.add('transition-timer', function(Y) {
-
-/*
-* The Transition Utility provides an API for creating advanced transitions.
-* @module transition
-*/
+}, '3.4.0' ,{requires:['node-style']});
+YUI.add('selector-css2', function(Y) {
-/*
-* Provides the base Transition class, for animating numeric properties.
-*
-* @module transition
-* @submodule transition-timer
-*/
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
-var Transition = Y.Transition;
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
-Y.mix(Transition.prototype, {
- _start: function() {
- if (Transition.useNative) {
- this._runNative();
- } else {
- this._runTimer();
- }
- },
+ return ret || [];
+ },
- _runTimer: function() {
- var anim = this;
- anim._initAttrs();
+ _re: {
+ attr: /(\[[^\]]*\])/g,
+ esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
+ pseudos: /(\([^\)]*\))/g
+ },
- Transition._running[Y.stamp(anim)] = anim;
- anim._startTime = new Date();
- Transition._startTimer();
- },
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
+ },
- _endTimer: function() {
- var anim = this;
- delete Transition._running[Y.stamp(anim)];
- anim._startTime = null;
- },
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
- _runFrame: function() {
- var t = new Date() - this._startTime;
- this._runAttrs(t);
- },
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
- _runAttrs: function(time) {
- var anim = this,
- node = anim._node,
- config = anim._config,
- uid = Y.stamp(node),
- attrs = Transition._nodeAttrs[uid],
- customAttr = Transition.behaviors,
- done = false,
- allDone = false,
- data,
- name,
- attribute,
- setter,
- elapsed,
- delay,
- d,
- t,
- i;
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ child,
+ id,
+ className,
+ tagName;
- for (name in attrs) {
- attribute = attrs[name];
- if ((attribute && attribute.transition === anim)) {
- d = attribute.duration;
- delay = attribute.delay;
- elapsed = (time - delay) / 1000;
- t = time;
- data = {
- type: 'propertyEnd',
- propertyName: name,
- config: config,
- elapsedTime: elapsed
- };
- setter = (i in customAttr && 'set' in customAttr[i]) ?
- customAttr[i].set : Transition.DEFAULT_SETTER;
+ // if we have an initial ID, set to root when in document
+ /*
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+ */
- done = (t >= d);
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
+ // try ID first, unless no root.all && root not in document
+ // (root.all works off document, but not getElementById)
+ // TODO: move to allById?
+ if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) {
+ nodes = Y.DOM.allById(id, root);
+ // try className
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else { // default to tagName
+ nodes = root.getElementsByTagName(tagName);
+ }
- if (t > d) {
- t = d;
+ } else { // brute getElementsByTagName('*')
+ child = root.firstChild;
+ while (child) {
+ if (child.tagName) { // only collect HTMLElements
+ nodes.push(child);
+ }
+ child = child.nextSilbing || child.firstChild;
+ }
+ }
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
}
+ }
- if (!delay || time >= delay) {
- setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
- attribute.easing, attribute.unit);
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
- if (done) {
- delete attrs[name];
- anim._count--;
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
+ operator.test && !operator.test(value)) || // regex test
+ (!operator.test && // protect against RegExp as function (webkit)
+ typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
- if (config[name] && config[name].on && config[name].on.end) {
- config[name].on.end.call(Y.one(node), data);
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
}
- //node.fire('transition:propertyEnd', data);
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
- if (!allDone && anim._count <= 0) {
- allDone = true;
- anim._end(elapsed);
- anim._endTimer();
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
}
+ break;
}
}
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
}
- }
- },
+ },
- _initAttrs: function() {
- var anim = this,
- customAttr = Transition.behaviors,
- uid = Y.stamp(anim._node),
- attrs = Transition._nodeAttrs[uid],
- attribute,
- duration,
- delay,
- easing,
- val,
- name,
- mTo,
- mFrom,
- unit, begin, end;
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Selector.operators,
+ escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
+ test;
- for (name in attrs) {
- attribute = attrs[name];
- if (attrs.hasOwnProperty(name) && (attribute && attribute.transition === anim)) {
- duration = attribute.duration * 1000;
- delay = attribute.delay * 1000;
- easing = attribute.easing;
- val = attribute.value;
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ Y.config.doc.documentElement.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
- // only allow supported properties
- if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
- begin = (name in customAttr && 'get' in customAttr[name]) ?
- customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
- mFrom = Transition.RE_UNITS.exec(begin);
- mTo = Transition.RE_UNITS.exec(val);
+ match[3] = escVal;
- begin = mFrom ? mFrom[1] : begin;
- end = mTo ? mTo[1] : val;
- unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+ // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
+ token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
- if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
- unit = Transition.DEFAULT_UNIT;
}
- if (typeof easing === 'string') {
- if (easing.indexOf('cubic-bezier') > -1) {
- easing = easing.substring(13, easing.length - 1).split(',');
- } else if (Transition.easings[easing]) {
- easing = Transition.easings[easing];
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
+ test = new RegExp(test.replace('{val}', match[3]));
}
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array and unescape special chars for tests
+ if (match[2]) {
+ match[2] = match[2].replace(/\\/g, '');
+ }
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
}
-
- attribute.from = Number(begin);
- attribute.to = Number(end);
- attribute.unit = unit;
- attribute.easing = easing;
- attribute.duration = duration + delay;
- attribute.delay = delay;
- } else {
- delete attrs[name];
- anim._count--;
}
}
- }
- },
-
- destroy: function() {
- this.detachAll();
- this._node = null;
- }
-}, true);
-
-Y.mix(Y.Transition, {
- _runtimeAttrs: {},
- /*
- * Regex of properties that should use the default unit.
- *
- * @property RE_DEFAULT_UNIT
- * @static
- */
- RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
+ ],
- /*
- * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
- *
- * @property DEFAULT_UNIT
- * @static
- */
- DEFAULT_UNIT: 'px',
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
- /*
- * Time in milliseconds passed to setInterval for frame processing
- *
- * @property intervalTime
- * @default 20
- * @static
- */
- intervalTime: 20,
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
- /*
- * Bucket for custom getters and setters
- *
- * @property behaviors
- * @static
- */
- behaviors: {
- left: {
- get: function(anim, attr) {
- return Y.DOM._getAttrOffset(anim._node, attr);
- }
- }
- },
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
- /*
- * The default setter to use when setting object properties.
- *
- * @property DEFAULT_SETTER
- * @static
- */
- DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
- from = Number(from);
- to = Number(to);
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser.name !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
- var node = anim._node,
- val = Transition.cubicBezier(fn, elapsed / duration);
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
- val = from + val[0] * (to - from);
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
- if (node) {
- if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
- unit = unit || '';
- Y.DOM.setStyle(node, att, val + unit);
- }
- } else {
- anim._end();
- }
- },
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
- /*
- * The default getter to use when getting object properties.
- *
- * @property DEFAULT_GETTER
- * @static
- */
- DEFAULT_GETTER: function(anim, att) {
- var node = anim._node,
- val = '';
+ if (!found || selector.length) { // not fully parsed
+ Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
+ tokens = [];
+ }
+ return tokens;
+ },
- if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
- val = Y.DOM.getComputedStyle(node, att);
- }
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
+ attrs,
+ pseudos,
+ re, i, len;
- return val;
- },
+ if (esc) {
+ selector = selector.replace(Selector._re.esc, '\uE000');
+ }
- _startTimer: function() {
- if (!Transition._timer) {
- Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
- }
- },
+ attrs = selector.match(Selector._re.attr);
+ pseudos = selector.match(Selector._re.pseudos);
- _stopTimer: function() {
- clearInterval(Transition._timer);
- Transition._timer = null;
- },
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '\uE001');
+ }
- /*
- * Called per Interval to handle each animation frame.
- * @method _runFrame
- * @private
- * @static
- */
- _runFrame: function() {
- var done = true,
- anim;
- for (anim in Transition._running) {
- if (Transition._running[anim]._runFrame) {
- done = false;
- Transition._running[anim]._runFrame();
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '\uE002');
}
- }
- if (done) {
- Transition._stopTimer();
- }
- },
- cubicBezier: function(p, t) {
- var x0 = 0,
- y0 = 0,
- x1 = p[0],
- y1 = p[1],
- x2 = p[2],
- y2 = p[3],
- x3 = 1,
- y3 = 0,
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
+ }
+ }
- A = x3 - 3 * x2 + 3 * x1 - x0,
- B = 3 * x2 - 6 * x1 + 3 * x0,
- C = 3 * x1 - 3 * x0,
- D = x0,
- E = y3 - 3 * y2 + 3 * y1 - y0,
- F = 3 * y2 - 6 * y1 + 3 * y0,
- G = 3 * y1 - 3 * y0,
- H = y0,
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace(/\uE001/, attrs[i]);
+ }
+ }
- x = (((A*t) + B)*t + C)*t + D,
- y = (((E*t) + F)*t + G)*t + H;
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace(/\uE002/, pseudos[i]);
+ }
+ }
- return [x, y];
- },
+ selector = selector.replace(/\[/g, '\uE003');
+ selector = selector.replace(/\]/g, '\uE004');
- easings: {
- ease: [0.25, 0, 1, 0.25],
- linear: [0, 0, 1, 1],
- 'ease-in': [0.42, 0, 1, 1],
- 'ease-out': [0, 0, 0.58, 1],
- 'ease-in-out': [0.42, 0, 0.58, 1]
- },
+ selector = selector.replace(/\(/g, '\uE005');
+ selector = selector.replace(/\)/g, '\uE006');
- _running: {},
- _timer: null,
+ if (esc) {
+ for (i = 0, len = esc.length; i < len; ++i) {
+ selector = selector.replace('\uE000', esc[i]);
+ }
+ }
- RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
-}, true);
+ return selector;
+ },
-Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
-Y.Transition = Transition;
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
-}, '3.4.0' ,{requires:['transition-native', 'node-style']});
+// IE wants class with native queries
+if (Y.Selector.useNative && Y.config.doc.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
-YUI.add('transition', function(Y){}, '3.4.0' ,{use:['transition-native', 'transition-timer']});
+}, '3.4.0' ,{requires:['selector-native']});
YUI.add('selector-css3', function(Y) {
/**
@@ -20825,312 +17571,559 @@ Y.Selector.combinators['~'] = {
}, '3.4.0' ,{requires:['selector-native', 'selector-css2']});
-YUI.add('dom-style-ie', function(Y) {
+YUI.add('yui-log', function(Y) {
-(function(Y) {
-var HAS_LAYOUT = 'hasLayout',
- PX = 'px',
- FILTER = 'filter',
- FILTERS = 'filters',
- OPACITY = 'opacity',
- AUTO = 'auto',
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations. This module is a `core` YUI module, it's documentation is located under the YUI class.
+ *
+ * @module yui
+ * @submodule yui-log
+ */
- BORDER_WIDTH = 'borderWidth',
- BORDER_TOP_WIDTH = 'borderTopWidth',
- BORDER_RIGHT_WIDTH = 'borderRightWidth',
- BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
- BORDER_LEFT_WIDTH = 'borderLeftWidth',
- WIDTH = 'width',
- HEIGHT = 'height',
- TRANSPARENT = 'transparent',
- VISIBLE = 'visible',
- GET_COMPUTED_STYLE = 'getComputedStyle',
- UNDEFINED = undefined,
- documentElement = Y.config.doc.documentElement,
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1,
+ info: 1,
+ warn: 1,
+ error: 1 };
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt).
+ * @param {String} src The source of the the message (opt).
+ * @param {boolean} silent If true, the log event won't fire.
+ * @return {YUI} YUI instance.
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var bail, excl, incl, m, f,
+ Y = INSTANCE,
+ c = Y.config,
+ publisher = (Y.fire) ? Y : YUI.Env.globalEvents;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (incl && (src in incl)) {
+ bail = !incl[src];
+ } else if (excl && (src in excl)) {
+ bail = excl[src];
+ }
+ }
+ if (!bail) {
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (Y.Lang.isFunction(c.logFn)) {
+ c.logFn.call(Y, msg, cat, src);
+ } else if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (publisher && !silent) {
+
+ if (publisher == Y && (!publisher.getEvent(LOGEVENT))) {
+ publisher.publish(LOGEVENT, {
+ broadcast: 2
+ });
+ }
+
+ publisher.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt).
+ * @param {String} src The source of the the message (opt).
+ * @param {boolean} silent If true, the log event won't fire.
+ * @return {YUI} YUI instance.
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+
+}, '3.4.0' ,{requires:['yui-base']});
+YUI.add('dump', function(Y) {
+
+/**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * If included, the dump method is added to the YUI instance.
+ *
+ * @module dump
+ */
+
+ var L = Y.Lang,
+ OBJ = '{...}',
+ FUN = 'f(){...}',
+ COMMA = ', ',
+ ARROW = ' => ',
+
+ /**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed.
+ *
+ * @method dump
+ * @param {Object} o The object to dump.
+ * @param {Number} d How deep to recurse child objects, default 3.
+ * @return {String} the dump result.
+ * @for YUI
+ */
+ dump = function(o, d) {
+ var i, len, s = [], type = L.type(o);
+
+ // Cast non-objects to string
+ // Skip dates because the std toString is what we want
+ // Skip HTMLElement-like objects because trying to dump
+ // an element will cause an unhandled exception in FF 2.x
+ if (!L.isObject(o)) {
+ return o + '';
+ } else if (type == 'date') {
+ return o;
+ } else if (o.nodeType && o.tagName) {
+ return o.tagName + '#' + o.id;
+ } else if (o.document && o.navigator) {
+ return 'window';
+ } else if (o.location && o.body) {
+ return 'document';
+ } else if (type == 'function') {
+ return FUN;
+ }
+
+ // dig into child objects the depth specifed. Default 3
+ d = (L.isNumber(d)) ? d : 3;
+
+ // arrays [1, 2, 3]
+ if (type == 'array') {
+ s.push('[');
+ for (i = 0, len = o.length; i < len; i = i + 1) {
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push(']');
+ // regexp /foo/
+ } else if (type == 'regexp') {
+ s.push(o.toString());
+ // objects {k1 => v1, k2 => v2}
+ } else {
+ s.push('{');
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ try {
+ s.push(i + ARROW);
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ } catch (e) {
+ s.push('Error: ' + e.message);
+ }
+ }
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push('}');
+ }
- testFeature = Y.Features.test,
- addFeature = Y.Features.add,
+ return s.join('');
+ };
- // TODO: unit-less lineHeight (e.g. 1.22)
- re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
+ Y.dump = dump;
+ L.dump = dump;
- isIE8 = (Y.UA.ie >= 8),
- _getStyleObj = function(node) {
- return node.currentStyle || node.style;
- },
- ComputedStyle = {
- CUSTOM_STYLES: {},
+}, '3.4.0' );
+YUI.add('transition-timer', function(Y) {
- get: function(el, property) {
- var value = '',
- current;
+/*
+* The Transition Utility provides an API for creating advanced transitions.
+* @module transition
+*/
- if (el) {
- current = _getStyleObj(el)[property];
+/*
+* Provides the base Transition class, for animating numeric properties.
+*
+* @module transition
+* @submodule transition-timer
+*/
- if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
- value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
- } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
- value = current;
- } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
- value = Y.DOM.IE.COMPUTED[property](el, property);
- } else if (re_unit.test(current)) { // convert to pixel
- value = ComputedStyle.getPixel(el, property) + PX;
- } else {
- value = current;
- }
- }
- return value;
- },
+var Transition = Y.Transition;
- sizeOffsets: {
- width: ['Left', 'Right'],
- height: ['Top', 'Bottom'],
- top: ['Top'],
- bottom: ['Bottom']
- },
+Y.mix(Transition.prototype, {
+ _start: function() {
+ if (Transition.useNative) {
+ this._runNative();
+ } else {
+ this._runTimer();
+ }
+ },
- getOffset: function(el, prop) {
- var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
- capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
- offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
- pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
- sizeOffsets = ComputedStyle.sizeOffsets[prop],
- mode = el.ownerDocument.compatMode,
- value = '';
+ _runTimer: function() {
+ var anim = this;
+ anim._initAttrs();
- // IE pixelWidth incorrect for percent
- // manually compute by subtracting padding and border from offset size
- // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
- // reverting to auto from auto causes position stacking issues (old impl)
- if (current === AUTO || current.indexOf('%') > -1) {
- value = el['offset' + capped];
+ Transition._running[Y.stamp(anim)] = anim;
+ anim._startTime = new Date();
+ Transition._startTimer();
+ },
- if (mode !== 'BackCompat') {
- if (sizeOffsets[0]) {
- value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
- value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
- }
+ _endTimer: function() {
+ var anim = this;
+ delete Transition._running[Y.stamp(anim)];
+ anim._startTime = null;
+ },
- if (sizeOffsets[1]) {
- value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
- value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
- }
- }
+ _runFrame: function() {
+ var t = new Date() - this._startTime;
+ this._runAttrs(t);
+ },
- } else { // use style.pixelWidth, etc. to convert to pixels
- // need to map style.width to currentStyle (no currentStyle.pixelWidth)
- if (!el.style[pixel] && !el.style[prop]) {
- el.style[prop] = current;
- }
- value = el.style[pixel];
-
- }
- return value + PX;
- },
+ _runAttrs: function(time) {
+ var anim = this,
+ node = anim._node,
+ config = anim._config,
+ uid = Y.stamp(node),
+ attrs = Transition._nodeAttrs[uid],
+ customAttr = Transition.behaviors,
+ done = false,
+ allDone = false,
+ data,
+ name,
+ attribute,
+ setter,
+ elapsed,
+ delay,
+ d,
+ t,
+ i;
- borderMap: {
- thin: (isIE8) ? '1px' : '2px',
- medium: (isIE8) ? '3px': '4px',
- thick: (isIE8) ? '5px' : '6px'
- },
+ for (name in attrs) {
+ if ((attribute = attrs[name]) && attribute.transition === anim) {
+ d = attribute.duration;
+ delay = attribute.delay;
+ elapsed = (time - delay) / 1000;
+ t = time;
+ data = {
+ type: 'propertyEnd',
+ propertyName: name,
+ config: config,
+ elapsedTime: elapsed
+ };
- getBorderWidth: function(el, property, omitUnit) {
- var unit = omitUnit ? '' : PX,
- current = el.currentStyle[property];
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Transition.DEFAULT_SETTER;
- if (current.indexOf(PX) < 0) { // look up keywords if a border exists
- if (ComputedStyle.borderMap[current] &&
- el.currentStyle.borderStyle !== 'none') {
- current = ComputedStyle.borderMap[current];
- } else { // otherwise no border (default is "medium")
- current = 0;
+ done = (t >= d);
+
+ if (t > d) {
+ t = d;
}
- }
- return (omitUnit) ? parseFloat(current) : current;
- },
- getPixel: function(node, att) {
- // use pixelRight to convert to px
- var val = null,
- style = _getStyleObj(node),
- styleRight = style.right,
- current = style[att];
+ if (!delay || time >= delay) {
+ setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
+ attribute.easing, attribute.unit);
- node.style.right = current;
- val = node.style.pixelRight;
- node.style.right = styleRight; // revert
+ if (done) {
+ delete attrs[name];
+ anim._count--;
- return val;
- },
+ if (config[name] && config[name].on && config[name].on.end) {
+ config[name].on.end.call(Y.one(node), data);
+ }
- getMargin: function(node, att) {
- var val,
- style = _getStyleObj(node);
+ //node.fire('transition:propertyEnd', data);
- if (style[att] == AUTO) {
- val = 0;
- } else {
- val = ComputedStyle.getPixel(node, att);
- }
- return val + PX;
- },
+ if (!allDone && anim._count <= 0) {
+ allDone = true;
+ anim._end(elapsed);
+ anim._endTimer();
+ }
+ }
+ }
- getVisibility: function(node, att) {
- var current;
- while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
- node = node.parentNode;
}
- return (current) ? current[att] : VISIBLE;
- },
+ }
+ },
- getColor: function(node, att) {
- var current = _getStyleObj(node)[att];
+ _initAttrs: function() {
+ var anim = this,
+ customAttr = Transition.behaviors,
+ uid = Y.stamp(anim._node),
+ attrs = Transition._nodeAttrs[uid],
+ attribute,
+ duration,
+ delay,
+ easing,
+ val,
+ name,
+ mTo,
+ mFrom,
+ unit, begin, end;
- if (!current || current === TRANSPARENT) {
- Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
- current = _getStyleObj(parent)[att];
- if (current && current !== TRANSPARENT) {
- node = parent;
- return true;
+ for (name in attrs) {
+ if ((attribute = attrs[name]) && attribute.transition === anim) {
+ duration = attribute.duration * 1000;
+ delay = attribute.delay * 1000;
+ easing = attribute.easing;
+ val = attribute.value;
+
+ // only allow supported properties
+ if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
+
+ mFrom = Transition.RE_UNITS.exec(begin);
+ mTo = Transition.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
+ unit = Transition.DEFAULT_UNIT;
}
- });
- }
- return Y.Color.toRGB(current);
- },
+ if (typeof easing === 'string') {
+ if (easing.indexOf('cubic-bezier') > -1) {
+ easing = easing.substring(13, easing.length - 1).split(',');
+ } else if (Transition.easings[easing]) {
+ easing = Transition.easings[easing];
+ }
+ }
- getBorderColor: function(node, att) {
- var current = _getStyleObj(node),
- val = current[att] || current.color;
- return Y.Color.toRGB(Y.Color.toHex(val));
+ attribute.from = Number(begin);
+ attribute.to = Number(end);
+ attribute.unit = unit;
+ attribute.easing = easing;
+ attribute.duration = duration + delay;
+ attribute.delay = delay;
+ } else {
+ delete attrs[name];
+ anim._count--;
+ }
+ }
}
},
- //fontSize: getPixelFont,
- IEComputed = {};
-
-addFeature('style', 'computedStyle', {
- test: function() {
- return 'getComputedStyle' in Y.config.win;
+ destroy: function() {
+ this.detachAll();
+ this._node = null;
}
-});
+}, true);
-addFeature('style', 'opacity', {
- test: function() {
- return 'opacity' in documentElement.style;
- }
-});
+Y.mix(Y.Transition, {
+ _runtimeAttrs: {},
+ /*
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
-addFeature('style', 'filter', {
- test: function() {
- return 'filters' in documentElement;
- }
-});
+ /*
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ DEFAULT_UNIT: 'px',
-// use alpha filter for IE opacity
-if (!testFeature('style', 'opacity') && testFeature('style', 'filter')) {
- Y.DOM.CUSTOM_STYLES[OPACITY] = {
- get: function(node) {
- var val = 100;
- try { // will error if no DXImageTransform
- val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+ /*
+ * Time in milliseconds passed to setInterval for frame processing
+ *
+ * @property intervalTime
+ * @default 20
+ * @static
+ */
+ intervalTime: 20,
- } catch(e) {
- try { // make sure its in the document
- val = node[FILTERS]('alpha')[OPACITY];
- } catch(err) {
- Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'dom-style');
- }
+ /*
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ behaviors: {
+ left: {
+ get: function(anim, attr) {
+ return Y.DOM._getAttrOffset(anim._node, attr);
}
- return val / 100;
- },
+ }
+ },
- set: function(node, val, style) {
- var current,
- styleObj = _getStyleObj(node),
- currentFilter = styleObj[FILTER];
+ /*
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
+ from = Number(from);
+ to = Number(to);
- style = style || node.style;
- if (val === '') { // normalize inline style behavior
- current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
- val = current;
+ var node = anim._node,
+ val = Transition.cubicBezier(fn, elapsed / duration);
+
+ val = from + val[0] * (to - from);
+
+ if (node) {
+ if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
+ unit = unit || '';
+ Y.DOM.setStyle(node, att, val + unit);
}
+ } else {
+ anim._end();
+ }
+ },
- if (typeof currentFilter == 'string') { // in case not appended
- style[FILTER] = currentFilter.replace(/alpha([^)]*\))/gi, '') +
- ((val < 1) ? 'alpha(' + OPACITY + '=' + val * 100 + ')' : '');
+ /*
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ DEFAULT_GETTER: function(anim, att) {
+ var node = anim._node,
+ val = '';
- if (!style[FILTER]) {
- style.removeAttribute(FILTER);
- }
+ if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
+ val = Y.DOM.getComputedStyle(node, att);
+ }
- if (!styleObj[HAS_LAYOUT]) {
- style.zoom = 1; // needs layout
- }
- }
+ return val;
+ },
+
+ _startTimer: function() {
+ if (!Transition._timer) {
+ Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
}
- };
-}
+ },
-try {
- Y.config.doc.createElement('div').style.height = '-1px';
-} catch(e) { // IE throws error on invalid style set; trap common cases
- Y.DOM.CUSTOM_STYLES.height = {
- set: function(node, val, style) {
- var floatVal = parseFloat(val);
- if (floatVal >= 0 || val === 'auto' || val === '') {
- style.height = val;
- } else {
- Y.log('invalid style value for height: ' + val, 'warn', 'dom-style');
+ _stopTimer: function() {
+ clearInterval(Transition._timer);
+ Transition._timer = null;
+ },
+
+ /*
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ _runFrame: function() {
+ var done = true,
+ anim;
+ for (anim in Transition._running) {
+ if (Transition._running[anim]._runFrame) {
+ done = false;
+ Transition._running[anim]._runFrame();
}
}
- };
- Y.DOM.CUSTOM_STYLES.width = {
- set: function(node, val, style) {
- var floatVal = parseFloat(val);
- if (floatVal >= 0 || val === 'auto' || val === '') {
- style.width = val;
- } else {
- Y.log('invalid style value for width: ' + val, 'warn', 'dom-style');
- }
+ if (done) {
+ Transition._stopTimer();
}
- };
-}
+ },
-if (!testFeature('style', 'computedStyle')) {
- // TODO: top, right, bottom, left
- IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+ cubicBezier: function(p, t) {
+ var x0 = 0,
+ y0 = 0,
+ x1 = p[0],
+ y1 = p[1],
+ x2 = p[2],
+ y2 = p[3],
+ x3 = 1,
+ y3 = 0,
- IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+ A = x3 - 3 * x2 + 3 * x1 - x0,
+ B = 3 * x2 - 6 * x1 + 3 * x0,
+ C = 3 * x1 - 3 * x0,
+ D = x0,
+ E = y3 - 3 * y2 + 3 * y1 - y0,
+ F = 3 * y2 - 6 * y1 + 3 * y0,
+ G = 3 * y1 - 3 * y0,
+ H = y0,
- IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
- IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
- ComputedStyle.getBorderWidth;
+ x = (((A*t) + B)*t + C)*t + D,
+ y = (((E*t) + F)*t + G)*t + H;
- IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
- IEComputed.marginLeft = ComputedStyle.getMargin;
+ return [x, y];
+ },
- IEComputed.visibility = ComputedStyle.getVisibility;
- IEComputed.borderColor = IEComputed.borderTopColor =
- IEComputed.borderRightColor = IEComputed.borderBottomColor =
- IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+ easings: {
+ ease: [0.25, 0, 1, 0.25],
+ linear: [0, 0, 1, 1],
+ 'ease-in': [0.42, 0, 1, 1],
+ 'ease-out': [0, 0, 0.58, 1],
+ 'ease-in-out': [0.42, 0, 0.58, 1]
+ },
- Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+ _running: {},
+ _timer: null,
- Y.namespace('DOM.IE');
- Y.DOM.IE.COMPUTED = IEComputed;
- Y.DOM.IE.ComputedStyle = ComputedStyle;
-}
+ RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
+}, true);
-})(Y);
+Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
+
+Y.Transition = Transition;
-}, '3.4.0' ,{requires:['dom-style']});
+}, '3.4.0' ,{requires:['transition']});
YUI.add('simpleyui', function(Y) {
// empty
diff --git a/build/simpleyui/simpleyui-min.js b/build/simpleyui/simpleyui-min.js
index d78959cd2dd..b4317e9e0af 100644
--- a/build/simpleyui/simpleyui-min.js
+++ b/build/simpleyui/simpleyui-min.js
@@ -5,30 +5,24 @@ http://developer.yahoo.com/yui/license.html
version: 3.4.0
build: nightly
*/
-if(typeof YUI!="undefined"){YUI._YUI=YUI;}var YUI=function(){var c=0,f=this,b=arguments,a=b.length,e=function(h,g){return(h&&h.hasOwnProperty&&(h instanceof g));},d=(typeof YUI_config!=="undefined")&&YUI_config;if(!(e(f,YUI))){f=new YUI();}else{f._init();if(YUI.GlobalConfig){f.applyConfig(YUI.GlobalConfig);}if(d){f.applyConfig(d);}if(!a){f._setup();}}if(a){for(;c-1){q="3.3.0";}p={applyConfig:function(D){D=D||l;var y,A,z=this.config,B=z.modules,x=z.groups,C=z.rls,w=this.Env._loader;for(A in D){if(D.hasOwnProperty(A)){y=D[A];if(B&&A=="modules"){o(B,y);}else{if(x&&A=="groups"){o(x,y);}else{if(C&&A=="rls"){o(C,y);}else{if(A=="win"){z[A]=y.contentWindow||y;z.doc=z[A].document;}else{if(A=="_yuid"){}else{z[A]=y;}}}}}}}if(w){w._config(D);}},_config:function(w){this.applyConfig(w);},_init:function(){var y,z=this,w=YUI.Env,x=z.Env,A;z.version=q;if(!x){z.Env={mods:{},versions:{},base:n,cdn:n+q+"/build/",_idx:0,_used:{},_attached:{},_missed:[],_yidx:0,_uidx:0,_guidp:"y",_loaded:{},_BASE_RE:/(?:\?(?:[^&]*&)*([^&]*))?\b(simpleyui|yui(?:-\w+)?)\/\2(?:-(min|debug))?\.js/,parseBasePath:function(F,D){var B=F.match(D),E,C;if(B){E=RegExp.leftContext||F.slice(0,F.indexOf(B[0]));C=B[3];if(B[1]){E+="?"+B[1];}E={filter:C,path:E};}return E;},getBase:w&&w.getBase||function(F){var D=(v&&v.getElementsByTagName("script"))||[],G=x.cdn,C,E,B,H;for(E=0,B=D.length;Ex&&x in w?w[x]:true;}}return z;};j.indexOf=q.indexOf?function(w,v){return q.indexOf.call(w,v);}:function(y,x){for(var w=0,v=y.length;w","tr");}});i("innerhtml-div","script",{test:function(){return e("', 'script');
+ }
+});
- // the default combo base
- comboBase = self.comboBase;
+if (!testFeature('innerhtml', 'table')) {
+ // TODO: thead/tfoot with nested tbody
+ // IE adds TBODY when creating TABLE elements (which may share this impl)
+ creators.tbody = function(html, doc) {
+ var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
+ tb = frag.children.tags('tbody')[0];
- url = comboBase;
- urls = [];
+ if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
+ tb.parentNode.removeChild(tb); // strip extraneous tbody
+ }
+ return frag;
+ };
+}
- comboSources = {};
+if (!testFeature('innerhtml-div', 'script')) {
+ creators.script = function(html, doc) {
+ var frag = doc.createElement('div');
- for (i = 0; i < len; i++) {
- comboSource = comboBase;
- m = self.getModule(s[i]);
- groupName = m && m.group;
- if (groupName) {
+ frag.innerHTML = '-' + html;
+ frag.removeChild(frag.firstChild);
+ return frag;
+ }
- group = self.groups[groupName];
+ creators.link = creators.style = creators.script;
+}
- if (!group.combine) {
- m.combine = false;
- continue;
- }
- m.combine = true;
- if (group.comboBase) {
- comboSource = group.comboBase;
- }
+if (!testFeature('innerhtml-div', 'tr')) {
+ Y.mix(creators, {
+ option: function(html, doc) {
+ return Y_DOM.create('', doc);
+ },
- if ("root" in group && L.isValue(group.root)) {
- m.root = group.root;
- }
+ tr: function(html, doc) {
+ return Y_DOM.create('' + html + '', doc);
+ },
- }
+ td: function(html, doc) {
+ return Y_DOM.create(' ' + html + ' ', doc);
+ },
- comboSources[comboSource] = comboSources[comboSource] || [];
- comboSources[comboSource].push(m);
- }
+ col: function(html, doc) {
+ return Y_DOM.create('
- *
+ * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
+ * @method viewportRegion
+ * @for DOM
+ * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
*/
- addHTML: function(node, content, where) {
- var nodeParent = node.parentNode,
- i = 0,
- item,
- ret = content,
- newNode;
-
+ viewportRegion: function(node) {
+ node = node || Y.config.doc.documentElement;
+ var ret = false,
+ scrollX,
+ scrollY;
- if (content != undefined) { // not null or undefined (maybe 0)
- if (content.nodeType) { // DOM node, just add it
- newNode = content;
- } else if (typeof content == 'string' || typeof content == 'number') {
- ret = newNode = Y_DOM.create(content);
- } else if (content[0] && content[0].nodeType) { // array or collection
- newNode = Y.config.doc.createDocumentFragment();
- while ((item = content[i++])) {
- newNode.appendChild(item); // append to fragment for insertion
- }
- }
- }
+ if (node) {
+ scrollX = DOM.docScrollX(node);
+ scrollY = DOM.docScrollY(node);
- if (where) {
- if (where.nodeType) { // insert regardless of relationship to node
- where.parentNode.insertBefore(newNode, where);
- } else {
- switch (where) {
- case 'replace':
- while (node.firstChild) {
- node.removeChild(node.firstChild);
- }
- if (newNode) { // allow empty content to clear node
- node.appendChild(newNode);
- }
- break;
- case 'before':
- nodeParent.insertBefore(newNode, node);
- break;
- case 'after':
- if (node.nextSibling) { // IE errors if refNode is null
- nodeParent.insertBefore(newNode, node.nextSibling);
- } else {
- nodeParent.appendChild(newNode);
- }
- break;
- default:
- node.appendChild(newNode);
- }
- }
- } else if (newNode) {
- node.appendChild(newNode);
+ ret = DOM._getRegion(scrollY, // top
+ DOM.winWidth(node) + scrollX, // right
+ scrollY + DOM.winHeight(node), // bottom
+ scrollX); // left
}
return ret;
}
});
+})(Y);
-addFeature('innerhtml', 'table', {
- test: function() {
- var node = Y.config.doc.createElement('table');
- try {
- node.innerHTML = '';
- } catch(e) {
- return false;
- }
- return (node.firstChild && node.firstChild.nodeName === 'TBODY');
- }
-});
-addFeature('innerhtml-div', 'tr', {
- test: function() {
- return createFromDIV('', 'tr');
- }
-});
+}, '3.4.0' ,{requires:['dom-base', 'dom-style']});
+YUI.add('selector-native', function(Y) {
-addFeature('innerhtml-div', 'script', {
- test: function() {
- return createFromDIV('', 'script');
- }
-});
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
-if (!testFeature('innerhtml', 'table')) {
- // TODO: thead/tfoot with nested tbody
- // IE adds TBODY when creating TABLE elements (which may share this impl)
- creators.tbody = function(html, doc) {
- var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
- tb = frag.children.tags('tbody')[0];
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
- if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
- tb.parentNode.removeChild(tb); // strip extraneous tbody
- }
- return frag;
- };
-}
+Y.namespace('Selector'); // allow native module to standalone
-if (!testFeature('innerhtml-div', 'script')) {
- creators.script = function(html, doc) {
- var frag = doc.createElement('div');
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument';
- frag.innerHTML = '-' + html;
- frag.removeChild(frag.firstChild);
- return frag;
- }
+var Selector = {
+ _foundCache: [],
- creators.link = creators.style = creators.script;
-}
+ useNative: true,
-if (!testFeature('innerhtml-div', 'tr')) {
- Y.mix(creators, {
- option: function(html, doc) {
- return Y_DOM.create('', doc);
- },
-
- tr: function(html, doc) {
- return Y_DOM.create('' + html + '', doc);
- },
-
- td: function(html, doc) {
- return Y_DOM.create(' ' + html + ' ', doc);
- },
+ _compare: ('sourceIndex' in Y.config.doc.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
- col: function(html, doc) {
- return Y_DOM.create('
+ *
+ *
+ * @method before
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
+ * when the event fires.
+ * @return {string} handle for the subscription
+ * @static
*/
- getComputedStyle: function(node, att) {
- var val = '',
- doc = node[OWNER_DOCUMENT];
-
- if (node[STYLE] && doc[DEFAULT_VIEW] && doc[DEFAULT_VIEW][GET_COMPUTED_STYLE]) {
- val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
+ before: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
}
- return val;
- }
-});
-// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
-if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
- Y_DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
-} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
- Y_DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
-}
-
-// fix opera computedStyle default color unit (convert to rgb)
-if (Y.UA.opera) {
- Y_DOM[GET_COMPUTED_STYLE] = function(node, att) {
- var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
- val = view[GET_COMPUTED_STYLE](node, '')[att];
+ return this._inject(DO_BEFORE, f, obj, sFn);
+ },
- if (re_color.test(att)) {
- val = Y.Color.toRGB(val);
+ /**
+ * returnValue
. No other wrapping functions will be
+ * executed.
+ *
+ *
+ * returnValue
. No other wrapping functions will be
+ * executed.returnValue
instead of the wrapped
+ * method's original return value. This can be further altered by
+ * other after phase wrappers.Y.Do.originalRetVal
and
+ * Y.Do.currentRetVal
will be populated for reference.before
and after
.
+ *
+ * @method _inject
+ * @param when {string} before or after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @private
+ * @static
+ */
+ _inject: function(when, fn, obj, sFn) {
-// safari converts transparent to rgba(), others use "transparent"
-if (Y.UA.webkit) {
- Y_DOM[GET_COMPUTED_STYLE] = function(node, att) {
- var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
- val = view[GET_COMPUTED_STYLE](node, '')[att];
+ // object id
+ var id = Y.stamp(obj), o, sid;
- if (val === 'rgba(0, 0, 0, 0)') {
- val = TRANSPARENT;
+ if (! this.objs[id]) {
+ // create a map entry for the obj if it doesn't exist
+ this.objs[id] = {};
}
- return val;
- };
-
-}
+ o = this.objs[id];
-Y.DOM._getAttrOffset = function(node, attr) {
- var val = Y.DOM[GET_COMPUTED_STYLE](node, attr),
- offsetParent = node.offsetParent,
- position,
- parentOffset,
- offset;
+ if (! o[sFn]) {
+ // create a map entry for the method if it doesn't exist
+ o[sFn] = new Y.Do.Method(obj, sFn);
- if (val === 'auto') {
- position = Y.DOM.getStyle(node, 'position');
- if (position === 'static' || position === 'relative') {
- val = 0;
- } else if (offsetParent && offsetParent[GET_BOUNDING_CLIENT_RECT]) {
- parentOffset = offsetParent[GET_BOUNDING_CLIENT_RECT]()[attr];
- offset = node[GET_BOUNDING_CLIENT_RECT]()[attr];
- if (attr === 'left' || attr === 'top') {
- val = offset - parentOffset;
- } else {
- val = parentOffset - node[GET_BOUNDING_CLIENT_RECT]()[attr];
- }
+ // re-route the method to our wrapper
+ obj[sFn] =
+ function() {
+ return o[sFn].exec.apply(o[sFn], arguments);
+ };
}
- }
-
- return val;
-};
-
-Y.DOM._getOffset = function(node) {
- var pos,
- xy = null;
-
- if (node) {
- pos = Y_DOM.getStyle(node, 'position');
- xy = [
- parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'left'), 10),
- parseInt(Y_DOM[GET_COMPUTED_STYLE](node, 'top'), 10)
- ];
-
- if ( isNaN(xy[0]) ) { // in case of 'auto'
- xy[0] = parseInt(Y_DOM.getStyle(node, 'left'), 10); // try inline
- if ( isNaN(xy[0]) ) { // default to offset value
- xy[0] = (pos === 'relative') ? 0 : node.offsetLeft || 0;
- }
- }
-
- if ( isNaN(xy[1]) ) { // in case of 'auto'
- xy[1] = parseInt(Y_DOM.getStyle(node, 'top'), 10); // try inline
- if ( isNaN(xy[1]) ) { // default to offset value
- xy[1] = (pos === 'relative') ? 0 : node.offsetTop || 0;
- }
- }
- }
-
- return xy;
-
-};
-Y_DOM.CUSTOM_STYLES.transform = {
- set: function(node, val, style) {
- style[TRANSFORM] = val;
- },
-
- get: function(node, style) {
- return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORM);
- }
-};
+ // subscriber id
+ sid = id + Y.stamp(fn) + sFn;
+ // register the callback
+ o[sFn].register(sid, fn, when);
-})(Y);
-(function(Y) {
-var PARSE_INT = parseInt,
- RE = RegExp;
+ return new Y.EventHandle(o[sFn], sid);
-Y.Color = {
- KEYWORDS: {
- black: '000',
- silver: 'c0c0c0',
- gray: '808080',
- white: 'fff',
- maroon: '800000',
- red: 'f00',
- purple: '800080',
- fuchsia: 'f0f',
- green: '008000',
- lime: '0f0',
- olive: '808000',
- yellow: 'ff0',
- navy: '000080',
- blue: '00f',
- teal: '008080',
- aqua: '0ff'
},
- re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
- re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
- re_hex3: /([0-9A-F])/gi,
+ /**
+ * Detach a before or after subscription.
+ *
+ * @method detach
+ * @param handle {string} the subscription handle
+ * @static
+ */
+ detach: function(handle) {
- toRGB: function(val) {
- if (!Y.Color.re_RGB.test(val)) {
- val = Y.Color.toHex(val);
+ if (handle.detach) {
+ handle.detach();
}
- if(Y.Color.re_hex.exec(val)) {
- val = 'rgb(' + [
- PARSE_INT(RE.$1, 16),
- PARSE_INT(RE.$2, 16),
- PARSE_INT(RE.$3, 16)
- ].join(', ') + ')';
- }
- return val;
},
- toHex: function(val) {
- val = Y.Color.KEYWORDS[val] || val;
- if (Y.Color.re_RGB.exec(val)) {
- val = [
- Number(RE.$1).toString(16),
- Number(RE.$2).toString(16),
- Number(RE.$3).toString(16)
- ];
-
- for (var i = 0; i < val.length; i++) {
- if (val[i].length < 2) {
- val[i] = '0' + val[i];
- }
- }
-
- val = val.join('');
- }
-
- if (val.length < 6) {
- val = val.replace(Y.Color.re_hex3, '$1$1');
- }
-
- if (val !== 'transparent' && val.indexOf('#') < 0) {
- val = '#' + val;
- }
+ _unload: function(e, me) {
- return val.toUpperCase();
}
};
-})(Y);
+Y.Do = DO;
-
-}, '3.4.0' ,{requires:['dom-core']});
-YUI.add('dom-screen', function(Y) {
-
-(function(Y) {
+//////////////////////////////////////////////////////////////////////////
/**
- * Adds position and region management functionality to DOM.
- * @module dom
- * @submodule dom-screen
- * @for DOM
+ * Contains the return value from the wrapped method, accessible
+ * by 'after' event listeners.
+ *
+ * @property Do.originalRetVal
+ * @static
+ * @since 3.2.0
*/
-var DOCUMENT_ELEMENT = 'documentElement',
- COMPAT_MODE = 'compatMode',
- POSITION = 'position',
- FIXED = 'fixed',
- RELATIVE = 'relative',
- LEFT = 'left',
- TOP = 'top',
- _BACK_COMPAT = 'BackCompat',
- MEDIUM = 'medium',
- BORDER_LEFT_WIDTH = 'borderLeftWidth',
- BORDER_TOP_WIDTH = 'borderTopWidth',
- GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
- GET_COMPUTED_STYLE = 'getComputedStyle',
-
- Y_DOM = Y.DOM,
+/**
+ * Contains the current state of the return value, consumable by
+ * 'after' event listeners, and updated if an after subscriber
+ * changes the return value generated by the wrapped function.
+ *
+ * @property Do.currentRetVal
+ * @static
+ * @since 3.2.0
+ */
- // TODO: how about thead/tbody/tfoot/tr?
- // TODO: does caption matter?
- RE_TABLE = /^t(?:able|d|h)$/i,
+//////////////////////////////////////////////////////////////////////////
- SCROLL_NODE;
+/**
+ * Wrapper for a displaced method with aop enabled
+ * @class Do.Method
+ * @constructor
+ * @param obj The object to operate on
+ * @param sFn The name of the method to displace
+ */
+DO.Method = function(obj, sFn) {
+ this.obj = obj;
+ this.methodName = sFn;
+ this.method = obj[sFn];
+ this.before = {};
+ this.after = {};
+};
-if (Y.UA.ie) {
- if (Y.config.doc[COMPAT_MODE] !== 'BackCompat') {
- SCROLL_NODE = DOCUMENT_ELEMENT;
+/**
+ * Register a aop subscriber
+ * @method register
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+DO.Method.prototype.register = function (sid, fn, when) {
+ if (when) {
+ this.after[sid] = fn;
} else {
- SCROLL_NODE = 'body';
+ this.before[sid] = fn;
}
-}
+};
-Y.mix(Y_DOM, {
- /**
- * Returns the inner height of the viewport (exludes scrollbar).
- * @method winHeight
- * @return {Number} The current height of the viewport.
- */
- winHeight: function(node) {
- var h = Y_DOM._getWinSize(node).height;
- return h;
- },
+/**
+ * Unregister a aop subscriber
+ * @method delete
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+DO.Method.prototype._delete = function (sid) {
+ delete this.before[sid];
+ delete this.after[sid];
+};
- /**
- * Returns the inner width of the viewport (exludes scrollbar).
- * @method winWidth
- * @return {Number} The current width of the viewport.
- */
- winWidth: function(node) {
- var w = Y_DOM._getWinSize(node).width;
- return w;
- },
+/**
+ * Y.Do.Halt
or Y.Do.Prevent
, neither the wrapped
+ * function nor any after phase subscribers will be executed.Y.Do.Halt
or
+ * Y.Do.AlterReturn
.
+ *
+ * @method exec
+ * @param arg* {any} Arguments are passed to the wrapping and wrapped functions
+ * @return {any} Return value of wrapped function unless overwritten (see above)
+ */
+DO.Method.prototype.exec = function () {
- /**
- * Document height
- * @method docHeight
- * @return {Number} The current height of the document.
- */
- docHeight: function(node) {
- var h = Y_DOM._getDocSize(node).height;
- return Math.max(h, Y_DOM._getWinSize(node).height);
- },
+ var args = Y.Array(arguments, 0, true),
+ i, ret, newRet,
+ bf = this.before,
+ af = this.after,
+ prevented = false;
- /**
- * Document width
- * @method docWidth
- * @return {Number} The current width of the document.
- */
- docWidth: function(node) {
- var w = Y_DOM._getDocSize(node).width;
- return Math.max(w, Y_DOM._getWinSize(node).width);
- },
+ // execute before
+ for (i in bf) {
+ if (bf.hasOwnProperty(i)) {
+ ret = bf[i].apply(this.obj, args);
+ if (ret) {
+ switch (ret.constructor) {
+ case DO.Halt:
+ return ret.retVal;
+ case DO.AlterArgs:
+ args = ret.newArgs;
+ break;
+ case DO.Prevent:
+ prevented = true;
+ break;
+ default:
+ }
+ }
+ }
+ }
- /**
- * Amount page has been scroll horizontally
- * @method docScrollX
- * @return {Number} The current amount the screen is scrolled horizontally.
- */
- docScrollX: function(node, doc) {
- doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
- var dv = doc.defaultView,
- pageOffset = (dv) ? dv.pageXOffset : 0;
- return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset);
- },
+ // execute method
+ if (!prevented) {
+ ret = this.method.apply(this.obj, args);
+ }
- /**
- * Amount page has been scroll vertically
- * @method docScrollY
- * @return {Number} The current amount the screen is scrolled vertically.
- */
- docScrollY: function(node, doc) {
- doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
- var dv = doc.defaultView,
- pageOffset = (dv) ? dv.pageYOffset : 0;
- return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset);
- },
+ DO.originalRetVal = ret;
+ DO.currentRetVal = ret;
- /**
- * Gets the current position of an element based on page coordinates.
- * Element must be part of the DOM tree to have page coordinates
- * (display:none or elements not appended return false).
- * @method getXY
- * @param element The target element
- * @return {Array} The XY position of the element
+ // execute after methods.
+ for (i in af) {
+ if (af.hasOwnProperty(i)) {
+ newRet = af[i].apply(this.obj, args);
+ // Stop processing if a Halt object is returned
+ if (newRet && newRet.constructor == DO.Halt) {
+ return newRet.retVal;
+ // Check for a new return value
+ } else if (newRet && newRet.constructor == DO.AlterReturn) {
+ ret = newRet.newRetVal;
+ // Update the static retval state
+ DO.currentRetVal = ret;
+ }
+ }
+ }
- TODO: test inDocument/display?
- */
- getXY: function() {
- if (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
- return function(node) {
- var xy = null,
- scrollLeft,
- scrollTop,
- box,
- off1, off2,
- bLeft, bTop,
- mode,
- doc,
- inDoc,
- rootNode;
+ return ret;
+};
- if (node && node.tagName) {
- doc = node.ownerDocument;
- rootNode = doc[DOCUMENT_ELEMENT];
+//////////////////////////////////////////////////////////////////////////
- // inline inDoc check for perf
- if (rootNode.contains) {
- inDoc = rootNode.contains(node);
- } else {
- inDoc = Y.DOM.contains(rootNode, node);
- }
+/**
+ * Return an AlterArgs object when you want to change the arguments that
+ * were passed into the function. Useful for Do.before subscribers. An
+ * example would be a service that scrubs out illegal characters prior to
+ * executing the core business logic.
+ * @class Do.AlterArgs
+ * @constructor
+ * @param msg {String} (optional) Explanation of the altered return value
+ * @param newArgs {Array} Call parameters to be used for the original method
+ * instead of the arguments originally passed in.
+ */
+DO.AlterArgs = function(msg, newArgs) {
+ this.msg = msg;
+ this.newArgs = newArgs;
+};
- if (inDoc) {
- scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc);
- scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc);
- box = node[GET_BOUNDING_CLIENT_RECT]();
- xy = [box.left, box.top];
+/**
+ * Return an AlterReturn object when you want to change the result returned
+ * from the core method to the caller. Useful for Do.after subscribers.
+ * @class Do.AlterReturn
+ * @constructor
+ * @param msg {String} (optional) Explanation of the altered return value
+ * @param newRetVal {any} Return value passed to code that invoked the wrapped
+ * function.
+ */
+DO.AlterReturn = function(msg, newRetVal) {
+ this.msg = msg;
+ this.newRetVal = newRetVal;
+};
- if (Y.UA.ie) {
- off1 = 2;
- off2 = 2;
- mode = doc[COMPAT_MODE];
- bLeft = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
- bTop = Y_DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
+/**
+ * Return a Halt object when you want to terminate the execution
+ * of all subsequent subscribers as well as the wrapped method
+ * if it has not exectued yet. Useful for Do.before subscribers.
+ * @class Do.Halt
+ * @constructor
+ * @param msg {String} (optional) Explanation of why the termination was done
+ * @param retVal {any} Return value passed to code that invoked the wrapped
+ * function.
+ */
+DO.Halt = function(msg, retVal) {
+ this.msg = msg;
+ this.retVal = retVal;
+};
- if (Y.UA.ie === 6) {
- if (mode !== _BACK_COMPAT) {
- off1 = 0;
- off2 = 0;
- }
- }
-
- if ((mode == _BACK_COMPAT)) {
- if (bLeft !== MEDIUM) {
- off1 = parseInt(bLeft, 10);
- }
- if (bTop !== MEDIUM) {
- off2 = parseInt(bTop, 10);
- }
- }
-
- xy[0] -= off1;
- xy[1] -= off2;
+/**
+ * Return a Prevent object when you want to prevent the wrapped function
+ * from executing, but want the remaining listeners to execute. Useful
+ * for Do.before subscribers.
+ * @class Do.Prevent
+ * @constructor
+ * @param msg {String} (optional) Explanation of why the termination was done
+ */
+DO.Prevent = function(msg) {
+ this.msg = msg;
+};
- }
+/**
+ * Return an Error object when you want to terminate the execution
+ * of all subsequent method calls.
+ * @class Do.Error
+ * @constructor
+ * @param msg {String} (optional) Explanation of the altered return value
+ * @param retVal {any} Return value passed to code that invoked the wrapped
+ * function.
+ * @deprecated use Y.Do.Halt or Y.Do.Prevent
+ */
+DO.Error = DO.Halt;
- if ((scrollTop || scrollLeft)) {
- if (!Y.UA.ios || (Y.UA.ios >= 4.2)) {
- xy[0] += scrollLeft;
- xy[1] += scrollTop;
- }
-
- }
- } else {
- xy = Y_DOM._getOffset(node);
- }
- }
- return xy;
- }
- } else {
- return function(node) { // manually calculate by crawling up offsetParents
- //Calculate the Top and Left border sizes (assumes pixels)
- var xy = null,
- doc,
- parentNode,
- bCheck,
- scrollTop,
- scrollLeft;
- if (node) {
- if (Y_DOM.inDoc(node)) {
- xy = [node.offsetLeft, node.offsetTop];
- doc = node.ownerDocument;
- parentNode = node;
- // TODO: refactor with !! or just falsey
- bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
+//////////////////////////////////////////////////////////////////////////
- // TODO: worth refactoring for TOP/LEFT only?
- while ((parentNode = parentNode.offsetParent)) {
- xy[0] += parentNode.offsetLeft;
- xy[1] += parentNode.offsetTop;
- if (bCheck) {
- xy = Y_DOM._calcBorders(parentNode, xy);
- }
- }
+// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
- // account for any scrolled ancestors
- if (Y_DOM.getStyle(node, POSITION) != FIXED) {
- parentNode = node;
- while ((parentNode = parentNode.parentNode)) {
- scrollTop = parentNode.scrollTop;
- scrollLeft = parentNode.scrollLeft;
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
- //Firefox does something funky with borders when overflow is not visible.
- if (Y.UA.gecko && (Y_DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
- xy = Y_DOM._calcBorders(parentNode, xy);
- }
-
- if (scrollTop || scrollLeft) {
- xy[0] -= scrollLeft;
- xy[1] -= scrollTop;
- }
- }
- xy[0] += Y_DOM.docScrollX(node, doc);
- xy[1] += Y_DOM.docScrollY(node, doc);
+// var onsubscribeType = "_event:onsub",
+var AFTER = 'after',
+ CONFIGS = [
+ 'broadcast',
+ 'monitored',
+ 'bubbles',
+ 'context',
+ 'contextFn',
+ 'currentTarget',
+ 'defaultFn',
+ 'defaultTargetOnly',
+ 'details',
+ 'emitFacade',
+ 'fireOnce',
+ 'async',
+ 'host',
+ 'preventable',
+ 'preventedFn',
+ 'queuable',
+ 'silent',
+ 'stoppedFn',
+ 'target',
+ 'type'
+ ],
- } else {
- //Fix FIXED position -- add scrollbars
- xy[0] += Y_DOM.docScrollX(node, doc);
- xy[1] += Y_DOM.docScrollY(node, doc);
- }
- } else {
- xy = Y_DOM._getOffset(node);
- }
- }
+ YUI3_SIGNATURE = 9,
+ YUI_LOG = 'yui:log';
- return xy;
- };
- }
- }(),// NOTE: Executing for loadtime branching
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String} type The type of event, which is passed to the callback
+ * when the event fires.
+ * @param {object} o configuration object.
+ * @class CustomEvent
+ * @constructor
+ */
+Y.CustomEvent = function(type, o) {
+
+ // if (arguments.length > 2) {
+// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
+ // }
+
+ o = o || {};
+
+ this.id = Y.stamp(this);
/**
- * Gets the current X position of an element based on page coordinates.
- * Element must be part of the DOM tree to have page coordinates
- * (display:none or elements not appended return false).
- * @method getX
- * @param element The target element
- * @return {Int} The X position of the element
+ * The type of event, returned to subscribers when the event fires
+ * @property type
+ * @type string
*/
+ this.type = type;
- getX: function(node) {
- return Y_DOM.getXY(node)[0];
- },
+ /**
+ * The context the the event will fire from by default. Defaults to the YUI
+ * instance.
+ * @property context
+ * @type object
+ */
+ this.context = Y;
/**
- * Gets the current Y position of an element based on page coordinates.
- * Element must be part of the DOM tree to have page coordinates
- * (display:none or elements not appended return false).
- * @method getY
- * @param element The target element
- * @return {Int} The Y position of the element
+ * Monitor when an event is attached or detached.
+ *
+ * @property monitored
+ * @type boolean
*/
+ // this.monitored = false;
- getY: function(node) {
- return Y_DOM.getXY(node)[1];
- },
+ this.logSystem = (type == YUI_LOG);
/**
- * Set the position of an html element in page coordinates.
- * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
- * @method setXY
- * @param element The target element
- * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
- * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+ * If 0, this event does not broadcast. If 1, the YUI instance is notified
+ * every time this event fires. If 2, the YUI instance and the YUI global
+ * (if event is enabled on the global) are notified every time this event
+ * fires.
+ * @property broadcast
+ * @type int
*/
- setXY: function(node, xy, noRetry) {
- var setStyle = Y_DOM.setStyle,
- pos,
- delta,
- newXY,
- currentXY;
+ // this.broadcast = 0;
- if (node && xy) {
- pos = Y_DOM.getStyle(node, POSITION);
+ /**
+ * By default all custom events are logged in the debug build, set silent
+ * to true to disable debug outpu for this event.
+ * @property silent
+ * @type boolean
+ */
+ this.silent = this.logSystem;
- delta = Y_DOM._getOffset(node);
- if (pos == 'static') { // default to relative
- pos = RELATIVE;
- setStyle(node, POSITION, pos);
- }
- currentXY = Y_DOM.getXY(node);
+ /**
+ * Specifies whether this event should be queued when the host is actively
+ * processing an event. This will effect exectution order of the callbacks
+ * for the various events.
+ * @property queuable
+ * @type boolean
+ * @default false
+ */
+ // this.queuable = false;
- if (xy[0] !== null) {
- setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
- }
-
- if (xy[1] !== null) {
- setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
- }
+ /**
+ * The subscribers to this event
+ * @property subscribers
+ * @type Subscriber {}
+ */
+ this.subscribers = {};
- if (!noRetry) {
- newXY = Y_DOM.getXY(node);
- if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
- Y_DOM.setXY(node, xy, true);
- }
- }
-
- } else {
- }
- },
+ /**
+ * 'After' subscribers
+ * @property afters
+ * @type Subscriber {}
+ */
+ this.afters = {};
/**
- * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
- * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
- * @method setX
- * @param element The target element
- * @param {Int} x The X values for new position (coordinates are page-based)
+ * This event has fired if true
+ *
+ * @property fired
+ * @type boolean
+ * @default false;
*/
- setX: function(node, x) {
- return Y_DOM.setXY(node, [x, null]);
- },
+ // this.fired = false;
/**
- * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
- * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
- * @method setY
- * @param element The target element
- * @param {Int} y The Y values for new position (coordinates are page-based)
+ * An array containing the arguments the custom event
+ * was last fired with.
+ * @property firedWith
+ * @type Array
*/
- setY: function(node, y) {
- return Y_DOM.setXY(node, [null, y]);
- },
+ // this.firedWith;
/**
- * @method swapXY
- * @description Swap the xy position with another node
- * @param {Node} node The node to swap with
- * @param {Node} otherNode The other node to swap with
- * @return {Node}
+ * This event should only fire one time if true, and if
+ * it has fired, any new subscribers should be notified
+ * immediately.
+ *
+ * @property fireOnce
+ * @type boolean
+ * @default false;
*/
- swapXY: function(node, otherNode) {
- var xy = Y_DOM.getXY(node);
- Y_DOM.setXY(node, Y_DOM.getXY(otherNode));
- Y_DOM.setXY(otherNode, xy);
- },
+ // this.fireOnce = false;
- _calcBorders: function(node, xy2) {
- var t = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
- l = parseInt(Y_DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
- if (Y.UA.gecko) {
- if (RE_TABLE.test(node.tagName)) {
- t = 0;
- l = 0;
- }
- }
- xy2[0] += l;
- xy2[1] += t;
- return xy2;
- },
+ /**
+ * fireOnce listeners will fire syncronously unless async
+ * is set to true
+ * @property async
+ * @type boolean
+ * @default false
+ */
+ //this.async = false;
- _getWinSize: function(node, doc) {
- doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc;
- var win = doc.defaultView || doc.parentWindow,
- mode = doc[COMPAT_MODE],
- h = win.innerHeight,
- w = win.innerWidth,
- root = doc[DOCUMENT_ELEMENT];
+ /**
+ * Flag for stopPropagation that is modified during fire()
+ * 1 means to stop propagation to bubble targets. 2 means
+ * to also stop additional subscribers on this target.
+ * @property stopped
+ * @type int
+ */
+ // this.stopped = 0;
- if ( mode && !Y.UA.opera ) { // IE, Gecko
- if (mode != 'CSS1Compat') { // Quirks
- root = doc.body;
- }
- h = root.clientHeight;
- w = root.clientWidth;
- }
- return { height: h, width: w };
- },
+ /**
+ * Flag for preventDefault that is modified during fire().
+ * if it is not 0, the default behavior for this event
+ * @property prevented
+ * @type int
+ */
+ // this.prevented = 0;
- _getDocSize: function(node) {
- var doc = (node) ? Y_DOM._getDoc(node) : Y.config.doc,
- root = doc[DOCUMENT_ELEMENT];
+ /**
+ * Specifies the host for this custom event. This is used
+ * to enable event bubbling
+ * @property host
+ * @type EventTarget
+ */
+ // this.host = null;
- if (doc[COMPAT_MODE] != 'CSS1Compat') {
- root = doc.body;
- }
+ /**
+ * The default function to execute after event listeners
+ * have fire, but only if the default action was not
+ * prevented.
+ * @property defaultFn
+ * @type Function
+ */
+ // this.defaultFn = null;
- return { height: root.scrollHeight, width: root.scrollWidth };
- }
-});
+ /**
+ * The function to execute if a subscriber calls
+ * stopPropagation or stopImmediatePropagation
+ * @property stoppedFn
+ * @type Function
+ */
+ // this.stoppedFn = null;
-})(Y);
-(function(Y) {
-var TOP = 'top',
- RIGHT = 'right',
- BOTTOM = 'bottom',
- LEFT = 'left',
+ /**
+ * The function to execute if a subscriber calls
+ * preventDefault
+ * @property preventedFn
+ * @type Function
+ */
+ // this.preventedFn = null;
- getOffsets = function(r1, r2) {
- var t = Math.max(r1[TOP], r2[TOP]),
- r = Math.min(r1[RIGHT], r2[RIGHT]),
- b = Math.min(r1[BOTTOM], r2[BOTTOM]),
- l = Math.max(r1[LEFT], r2[LEFT]),
- ret = {};
-
- ret[TOP] = t;
- ret[RIGHT] = r;
- ret[BOTTOM] = b;
- ret[LEFT] = l;
- return ret;
- },
+ /**
+ * Specifies whether or not this event's default function
+ * can be cancelled by a subscriber by executing preventDefault()
+ * on the event facade
+ * @property preventable
+ * @type boolean
+ * @default true
+ */
+ this.preventable = true;
- DOM = Y.DOM;
+ /**
+ * Specifies whether or not a subscriber can stop the event propagation
+ * via stopPropagation(), stopImmediatePropagation(), or halt()
+ *
+ * Events can only bubble if emitFacade is true.
+ *
+ * @property bubbles
+ * @type boolean
+ * @default true
+ */
+ this.bubbles = true;
-Y.mix(DOM, {
/**
- * Returns an Object literal containing the following about this element: (top, right, bottom, left)
- * @for DOM
- * @method region
- * @param {HTMLElement} element The DOM element.
- * @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
+ * Supports multiple options for listener signatures in order to
+ * port YUI 2 apps.
+ * @property signature
+ * @type int
+ * @default 9
*/
- region: function(node) {
- var xy = DOM.getXY(node),
- ret = false;
-
- if (node && xy) {
- ret = DOM._getRegion(
- xy[1], // top
- xy[0] + node.offsetWidth, // right
- xy[1] + node.offsetHeight, // bottom
- xy[0] // left
- );
- }
+ this.signature = YUI3_SIGNATURE;
- return ret;
- },
+ this.subCount = 0;
+ this.afterCount = 0;
+
+ // this.hasSubscribers = false;
+
+ // this.hasAfters = false;
/**
- * Find the intersect information for the passes nodes.
- * @method intersect
- * @for DOM
- * @param {HTMLElement} element The first element
- * @param {HTMLElement | Object} element2 The element or region to check the interect with
- * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
- * @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
+ * If set to true, the custom event will deliver an EventFacade object
+ * that is similar to a DOM event object.
+ * @property emitFacade
+ * @type boolean
+ * @default false
*/
- intersect: function(node, node2, altRegion) {
- var r = altRegion || DOM.region(node), region = {},
- n = node2,
- off;
+ // this.emitFacade = false;
+
+ this.applyConfig(o, true);
+
+ // this.log("Creating " + this.type);
+
+};
+
+Y.CustomEvent.prototype = {
+ constructor: Y.CustomEvent,
- if (n.tagName) {
- region = DOM.region(n);
- } else if (Y.Lang.isObject(node2)) {
- region = node2;
- } else {
- return false;
- }
-
- off = getOffsets(region, r);
- return {
- top: off[TOP],
- right: off[RIGHT],
- bottom: off[BOTTOM],
- left: off[LEFT],
- area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
- yoff: ((off[BOTTOM] - off[TOP])),
- xoff: (off[RIGHT] - off[LEFT]),
- inRegion: DOM.inRegion(node, node2, false, altRegion)
- };
-
- },
/**
- * Check if any part of this node is in the passed region
- * @method inRegion
- * @for DOM
- * @param {Object} node2 The node to get the region from or an Object literal of the region
- * $param {Boolean} all Should all of the node be inside the region
- * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
- * @return {Boolean} True if in region, false if not.
+ * Returns the number of subscribers for this event as the sum of the on()
+ * subscribers and after() subscribers.
+ *
+ * @method hasSubs
+ * @return Number
*/
- inRegion: function(node, node2, all, altRegion) {
- var region = {},
- r = altRegion || DOM.region(node),
- n = node2,
- off;
+ hasSubs: function(when) {
+ var s = this.subCount, a = this.afterCount, sib = this.sibling;
- if (n.tagName) {
- region = DOM.region(n);
- } else if (Y.Lang.isObject(node2)) {
- region = node2;
- } else {
- return false;
+ if (sib) {
+ s += sib.subCount;
+ a += sib.afterCount;
}
-
- if (all) {
- return (
- r[LEFT] >= region[LEFT] &&
- r[RIGHT] <= region[RIGHT] &&
- r[TOP] >= region[TOP] &&
- r[BOTTOM] <= region[BOTTOM] );
- } else {
- off = getOffsets(region, r);
- if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
- return true;
- } else {
- return false;
- }
-
+
+ if (when) {
+ return (when == 'after') ? a : s;
}
+
+ return (s + a);
},
/**
- * Check if any part of this element is in the viewport
- * @method inViewportRegion
- * @for DOM
- * @param {HTMLElement} element The DOM element.
- * @param {Boolean} all Should all of the node be inside the region
- * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
- * @return {Boolean} True if in region, false if not.
+ * Monitor the event state for the subscribed event. The first parameter
+ * is what should be monitored, the rest are the normal parameters when
+ * subscribing to an event.
+ * @method monitor
+ * @param what {string} what to monitor ('detach', 'attach', 'publish').
+ * @return {EventHandle} return value from the monitor event subscription.
*/
- inViewportRegion: function(node, all, altRegion) {
- return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
-
+ monitor: function(what) {
+ this.monitored = true;
+ var type = this.id + '|' + this.type + '_' + what,
+ args = Y.Array(arguments, 0, true);
+ args[0] = type;
+ return this.host.on.apply(this.host, args);
},
- _getRegion: function(t, r, b, l) {
- var region = {};
+ /**
+ * Get all of the subscribers to this event and any sibling event
+ * @method getSubs
+ * @return {Array} first item is the on subscribers, second the after.
+ */
+ getSubs: function() {
+ var s = Y.merge(this.subscribers), a = Y.merge(this.afters), sib = this.sibling;
- region[TOP] = region[1] = t;
- region[LEFT] = region[0] = l;
- region[BOTTOM] = b;
- region[RIGHT] = r;
- region.width = region[RIGHT] - region[LEFT];
- region.height = region[BOTTOM] - region[TOP];
+ if (sib) {
+ Y.mix(s, sib.subscribers);
+ Y.mix(a, sib.afters);
+ }
- return region;
+ return [s, a];
},
/**
- * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
- * @method viewportRegion
- * @for DOM
- * @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
+ * Apply configuration properties. Only applies the CONFIG whitelist
+ * @method applyConfig
+ * @param o hash of properties to apply.
+ * @param force {boolean} if true, properties that exist on the event
+ * will be overwritten.
*/
- viewportRegion: function(node) {
- node = node || Y.config.doc.documentElement;
- var ret = false,
- scrollX,
- scrollY;
-
- if (node) {
- scrollX = DOM.docScrollX(node);
- scrollY = DOM.docScrollY(node);
-
- ret = DOM._getRegion(scrollY, // top
- DOM.winWidth(node) + scrollX, // right
- scrollY + DOM.winHeight(node), // bottom
- scrollX); // left
+ applyConfig: function(o, force) {
+ if (o) {
+ Y.mix(this, o, force, CONFIGS);
}
+ },
- return ret;
- }
-});
-})(Y);
-
-
-}, '3.4.0' ,{requires:['dom-core', 'dom-style']});
-YUI.add('selector-native', function(Y) {
-
-(function(Y) {
-/**
- * The selector-native module provides support for native querySelector
- * @module dom
- * @submodule selector-native
- * @for Selector
- */
-
-/**
- * Provides support for using CSS selectors to query the DOM
- * @class Selector
- * @static
- * @for Selector
- */
-
-Y.namespace('Selector'); // allow native module to standalone
-
-var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
- OWNER_DOCUMENT = 'ownerDocument';
-
-var Selector = {
- _foundCache: [],
-
- useNative: true,
-
- _compare: ('sourceIndex' in Y.config.doc.documentElement) ?
- function(nodeA, nodeB) {
- var a = nodeA.sourceIndex,
- b = nodeB.sourceIndex;
+ /**
+ * Create the Subscription for subscribing function, context, and bound
+ * arguments. If this is a fireOnce event, the subscriber is immediately
+ * notified.
+ *
+ * @method _on
+ * @param fn {Function} Subscription callback
+ * @param [context] {Object} Override `this` in the callback
+ * @param [args] {Array} bound arguments that will be passed to the callback after the arguments generated by fire()
+ * @param [when] {String} "after" to slot into after subscribers
+ * @return {EventHandle}
+ * @protected
+ */
+ _on: function(fn, context, args, when) {
- if (a === b) {
- return 0;
- } else if (a > b) {
- return 1;
- }
+ if (!fn) {
+ this.log('Invalid callback for CE: ' + this.type);
+ }
- return -1;
+ var s = new Y.Subscriber(fn, context, args, when);
- } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ?
- function(nodeA, nodeB) {
- if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
- return -1;
+ if (this.fireOnce && this.fired) {
+ if (this.async) {
+ setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0);
} else {
- return 1;
- }
- } :
- function(nodeA, nodeB) {
- var rangeA, rangeB, compare;
- if (nodeA && nodeB) {
- rangeA = nodeA[OWNER_DOCUMENT].createRange();
- rangeA.setStart(nodeA, 0);
- rangeB = nodeB[OWNER_DOCUMENT].createRange();
- rangeB.setStart(nodeB, 0);
- compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
- }
-
- return compare;
-
- }),
-
- _sort: function(nodes) {
- if (nodes) {
- nodes = Y.Array(nodes, 0, true);
- if (nodes.sort) {
- nodes.sort(Selector._compare);
+ this._notify(s, this.firedWith);
}
}
- return nodes;
- },
-
- _deDupe: function(nodes) {
- var ret = [],
- i, node;
-
- for (i = 0; (node = nodes[i++]);) {
- if (!node._found) {
- ret[ret.length] = node;
- node._found = true;
- }
+ if (when == AFTER) {
+ this.afters[s.id] = s;
+ this.afterCount++;
+ } else {
+ this.subscribers[s.id] = s;
+ this.subCount++;
}
- for (i = 0; (node = ret[i++]);) {
- node._found = null;
- node.removeAttribute('_found');
- }
+ return new Y.EventHandle(this, s);
- return ret;
},
/**
- * Retrieves a set of nodes based on a given CSS selector.
- * @method query
- *
- * @param {string} selector The CSS Selector to test the node against.
- * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
- * @param {Boolean} firstOnly optional Whether or not to return only the first match.
- * @return {Array} An array of nodes that match the given selector.
- * @static
- */
- query: function(selector, root, firstOnly, skipNative) {
- root = root || Y.config.doc;
- var ret = [],
- useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative),
- queries = [[selector, root]],
- query,
- result,
- i,
- fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
-
- if (selector && fn) {
- // split group into seperate queries
- if (!skipNative && // already done if skipping
- (!useNative || root.tagName)) { // split native when element scoping is needed
- queries = Selector._splitQueries(selector, root);
- }
-
- for (i = 0; (query = queries[i++]);) {
- result = fn(query[0], query[1], firstOnly);
- if (!firstOnly) { // coerce DOM Collection to Array
- result = Y.Array(result, 0, true);
- }
- if (result) {
- ret = ret.concat(result);
- }
- }
-
- if (queries.length > 1) { // remove dupes and sort by doc order
- ret = Selector._sort(Selector._deDupe(ret));
- }
- }
-
- return (firstOnly) ? (ret[0] || null) : ret;
-
+ * Listen for this event
+ * @method subscribe
+ * @param {Function} fn The function to execute.
+ * @return {EventHandle} Unsubscribe handle.
+ * @deprecated use on.
+ */
+ subscribe: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
+ return this._on(fn, context, a, true);
},
- // allows element scoped queries to begin with combinator
- // e.g. query('> p', document.body) === query('body > p')
- _splitQueries: function(selector, node) {
- var groups = selector.split(','),
- queries = [],
- prefix = '',
- i, len;
-
- if (node) {
- // enforce for element scoping
- if (node.tagName) {
- node.id = node.id || Y.guid();
- prefix = '[id="' + node.id + '"] ';
- }
-
- for (i = 0, len = groups.length; i < len; ++i) {
- selector = prefix + groups[i];
- queries.push([selector, node]);
- }
+ /**
+ * Listen for this event
+ * @method on
+ * @param {Function} fn The function to execute.
+ * @param {object} context optional execution context.
+ * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
+ * when the event fires.
+ * @return {EventHandle} An object with a detach method to detch the handler(s).
+ */
+ on: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
+ if (this.host) {
+ this.host._monitor('attach', this.type, {
+ args: arguments
+ });
}
+ return this._on(fn, context, a, true);
+ },
- return queries;
+ /**
+ * Listen for this event after the normal subscribers have been notified and
+ * the default behavior has been applied. If a normal subscriber prevents the
+ * default behavior, it also prevents after listeners from firing.
+ * @method after
+ * @param {Function} fn The function to execute.
+ * @param {object} context optional execution context.
+ * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
+ * when the event fires.
+ * @return {EventHandle} handle Unsubscribe handle.
+ */
+ after: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
+ return this._on(fn, context, a, AFTER);
},
- _nativeQuery: function(selector, root, one) {
- if (Y.UA.webkit && selector.indexOf(':checked') > -1 &&
- (Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
- return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
- }
- try {
- return root['querySelector' + (one ? '' : 'All')](selector);
- } catch(e) { // fallback to brute if available
- return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ /**
+ * Detach listeners.
+ * @method detach
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed.
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int} returns the number of subscribers unsubscribed.
+ */
+ detach: function(fn, context) {
+ // unsubscribe handle
+ if (fn && fn.detach) {
+ return fn.detach();
}
- },
- filter: function(nodes, selector) {
- var ret = [],
- i, node;
+ var i, s,
+ found = 0,
+ subs = Y.merge(this.subscribers, this.afters);
- if (nodes && selector) {
- for (i = 0; (node = nodes[i++]);) {
- if (Y.Selector.test(node, selector)) {
- ret[ret.length] = node;
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && (!fn || fn === s.fn)) {
+ this._delete(s);
+ found++;
}
}
- } else {
}
- return ret;
+ return found;
},
- test: function(node, selector, root) {
- var ret = false,
- useFrag = false,
- groups,
- parent,
- item,
- items,
- frag,
- i, j, group;
+ /**
+ * Detach listeners.
+ * @method unsubscribe
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed.
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int|undefined} returns the number of subscribers unsubscribed.
+ * @deprecated use detach.
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
- if (node && node.tagName) { // only test HTMLElements
+ /**
+ * Notify a single subscriber
+ * @method _notify
+ * @param {Subscriber} s the subscriber.
+ * @param {Array} args the arguments array to apply to the listener.
+ * @protected
+ */
+ _notify: function(s, args, ef) {
- if (typeof selector == 'function') { // test with function
- ret = selector.call(node, node);
- } else { // test with query
- // we need a root if off-doc
- groups = selector.split(',');
- if (!root && !Y.DOM.inDoc(node)) {
- parent = node.parentNode;
- if (parent) {
- root = parent;
- } else { // only use frag when no parent to query
- frag = node[OWNER_DOCUMENT].createDocumentFragment();
- frag.appendChild(node);
- root = frag;
- useFrag = true;
- }
- }
- root = root || node[OWNER_DOCUMENT];
+ this.log(this.type + '->' + 'sub: ' + s.id);
- if (!node.id) {
- node.id = Y.guid();
- }
- for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
- group += '[id="' + node.id + '"]';
- items = Y.Selector.query(group, root);
+ var ret;
- for (j = 0; item = items[j++];) {
- if (item === node) {
- ret = true;
- break;
- }
- }
- if (ret) {
- break;
- }
- }
+ ret = s.notify(args, this);
- if (useFrag) { // cleanup
- frag.removeChild(node);
- }
- };
+ if (false === ret || this.stopped > 1) {
+ this.log(this.type + ' cancelled by subscriber');
+ return false;
}
- return ret;
+ return true;
},
/**
- * A convenience function to emulate Y.Node's aNode.ancestor(selector).
- * @param {HTMLElement} element An HTMLElement to start the query from.
- * @param {String} selector The CSS selector to test the node against.
- * @return {HTMLElement} The ancestor node matching the selector, or null.
- * @param {Boolean} testSelf optional Whether or not to include the element in the scan
- * @static
- * @method ancestor
+ * Logger abstraction to centralize the application of the silent flag
+ * @method log
+ * @param {string} msg message to log.
+ * @param {string} cat log category.
*/
- ancestor: function (element, selector, testSelf) {
- return Y.DOM.ancestor(element, function(n) {
- return Y.Selector.test(n, selector);
- }, testSelf);
- }
-};
+ log: function(msg, cat) {
+ if (!this.silent) {
+ }
+ },
-Y.mix(Y.Selector, Selector, true);
+ /**
+ * Notifies the subscribers. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters:
+ *
+ *
+ * @method fire
+ * @param {Object*} arguments an arbitrary set of parameters to pass to
+ * the handler.
+ * @return {boolean} false if one of the subscribers returned false,
+ * true otherwise.
+ *
+ */
+ fire: function() {
+ if (this.fireOnce && this.fired) {
+ this.log('fireOnce event: ' + this.type + ' already fired');
+ return true;
+ } else {
-})(Y);
+ var args = Y.Array(arguments, 0, true);
+ // this doesn't happen if the event isn't published
+ // this.host._monitor('fire', this.type, args);
-}, '3.4.0' ,{requires:['dom-core']});
-YUI.add('selector', function(Y) {
-
-
-
-
-}, '3.4.0' ,{requires:['selector-native']});
-
-
-YUI.add('dom', function(Y){}, '3.4.0' ,{use:['dom-core', 'dom-base', 'dom-attrs', 'dom-create', 'dom-class', 'dom-size', 'dom-style', 'dom-screen', 'selector-native', 'selector']});
-
-YUI.add('event-custom-base', function(Y) {
-
-/**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event-custom
- */
-
-Y.Env.evt = {
- handles: {},
- plugins: {}
-};
-
-
-/**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event-custom
- * @submodule event-custom-base
- */
-
-/**
- * Allows for the insertion of methods that are executed before or after
- * a specified method
- * @class Do
- * @static
- */
-
-var DO_BEFORE = 0,
- DO_AFTER = 1,
-
-DO = {
+ this.fired = true;
+ this.firedWith = args;
- /**
- * Cache of objects touched by the utility
- * @property objs
- * @static
- */
- objs: {},
+ if (this.emitFacade) {
+ return this.fireComplex(args);
+ } else {
+ return this.fireSimple(args);
+ }
+ }
+ },
/**
- *
- *
+ * Set up for notifying subscribers of non-emitFacade events.
*
- * @method before
- * @param fn {Function} the function to execute
- * @param obj the object hosting the method to displace
- * @param sFn {string} the name of the method to displace
- * @param c The execution context for fn
- * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
- * when the event fires.
- * @return {string} handle for the subscription
- * @static
+ * @method fireSimple
+ * @param args {Array} Arguments passed to fire()
+ * @return Boolean false if a subscriber returned false
+ * @protected
*/
- before: function(fn, obj, sFn, c) {
- var f = fn, a;
- if (c) {
- a = [fn, c].concat(Y.Array(arguments, 4, true));
- f = Y.rbind.apply(Y, a);
+ fireSimple: function(args) {
+ this.stopped = 0;
+ this.prevented = 0;
+ if (this.hasSubs()) {
+ // this._procSubs(Y.merge(this.subscribers, this.afters), args);
+ var subs = this.getSubs();
+ this._procSubs(subs[0], args);
+ this._procSubs(subs[1], args);
}
+ this._broadcast(args);
+ return this.stopped ? false : true;
+ },
- return this._inject(DO_BEFORE, f, obj, sFn);
+ // Requires the event-custom-complex module for full funcitonality.
+ fireComplex: function(args) {
+ args[0] = args[0] || {};
+ return this.fireSimple(args);
},
/**
- * returnValue
. No other wrapping functions will be
- * executed.
- *
- *
- * returnValue
. No other wrapping functions will be
- * executed.returnValue
instead of the wrapped
- * method's original return value. This can be further altered by
- * other after phase wrappers.Y.Do.originalRetVal
and
- * Y.Do.currentRetVal
will be populated for reference.before
and after
.
+ * Notifies the YUI instance if the event is configured with broadcast = 1,
+ * and both the YUI instance and Y.Global if configured with broadcast = 2.
*
- * @method _inject
- * @param when {string} before or after
- * @param fn {Function} the function to execute
- * @param obj the object hosting the method to displace
- * @param sFn {string} the name of the method to displace
- * @param c The execution context for fn
- * @return {string} handle for the subscription
+ * @method _broadcast
+ * @param args {Array} Arguments sent to fire()
* @private
- * @static
*/
- _inject: function(when, fn, obj, sFn) {
-
- // object id
- var id = Y.stamp(obj), o, sid;
-
- if (! this.objs[id]) {
- // create a map entry for the obj if it doesn't exist
- this.objs[id] = {};
- }
+ _broadcast: function(args) {
+ if (!this.stopped && this.broadcast) {
- o = this.objs[id];
+ var a = Y.Array(args);
+ a.unshift(this.type);
- if (! o[sFn]) {
- // create a map entry for the method if it doesn't exist
- o[sFn] = new Y.Do.Method(obj, sFn);
+ if (this.host !== Y) {
+ Y.fire.apply(Y, a);
+ }
- // re-route the method to our wrapper
- obj[sFn] =
- function() {
- return o[sFn].exec.apply(o[sFn], arguments);
- };
+ if (this.broadcast == 2) {
+ Y.Global.fire.apply(Y.Global, a);
+ }
}
+ },
- // subscriber id
- sid = id + Y.stamp(fn) + sFn;
-
- // register the callback
- o[sFn].register(sid, fn, when);
-
- return new Y.EventHandle(o[sFn], sid);
+ /**
+ * Removes all listeners
+ * @method unsubscribeAll
+ * @return {int} The number of listeners unsubscribed.
+ * @deprecated use detachAll.
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+ /**
+ * Removes all listeners
+ * @method detachAll
+ * @return {int} The number of listeners unsubscribed.
+ */
+ detachAll: function() {
+ return this.detach();
},
/**
- * Detach a before or after subscription.
+ * Deletes the subscriber from the internal store of on() and after()
+ * subscribers.
*
- * @method detach
- * @param handle {string} the subscription handle
- * @static
+ * @method _delete
+ * @param subscriber object.
+ * @private
*/
- detach: function(handle) {
-
- if (handle.detach) {
- handle.detach();
+ _delete: function(s) {
+ if (s) {
+ if (this.subscribers[s.id]) {
+ delete this.subscribers[s.id];
+ this.subCount--;
+ }
+ if (this.afters[s.id]) {
+ delete this.afters[s.id];
+ this.afterCount--;
+ }
}
- },
-
- _unload: function(e, me) {
+ if (this.host) {
+ this.host._monitor('detach', this.type, {
+ ce: this,
+ sub: s
+ });
+ }
+ if (s) {
+ // delete s.fn;
+ // delete s.context;
+ s.deleted = true;
+ }
}
};
-
-Y.Do = DO;
-
-//////////////////////////////////////////////////////////////////////////
-
/**
- * Contains the return value from the wrapped method, accessible
- * by 'after' event listeners.
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn The wrapped function to execute.
+ * @param {Object} context The value of the keyword 'this' in the listener.
+ * @param {Array} args* 0..n additional arguments to supply the listener.
*
- * @property Do.originalRetVal
- * @static
- * @since 3.2.0
+ * @class Subscriber
+ * @constructor
*/
+Y.Subscriber = function(fn, context, args) {
-/**
- * Contains the current state of the return value, consumable by
- * 'after' event listeners, and updated if an after subscriber
- * changes the return value generated by the wrapped function.
- *
- * @property Do.currentRetVal
- * @static
- * @since 3.2.0
- */
+ /**
+ * The callback that will be execute when the event fires
+ * This is wrapped by Y.rbind if obj was supplied.
+ * @property fn
+ * @type Function
+ */
+ this.fn = fn;
-//////////////////////////////////////////////////////////////////////////
+ /**
+ * Optional 'this' keyword for the listener
+ * @property context
+ * @type Object
+ */
+ this.context = context;
-/**
- * Wrapper for a displaced method with aop enabled
- * @class Do.Method
- * @constructor
- * @param obj The object to operate on
- * @param sFn The name of the method to displace
- */
-DO.Method = function(obj, sFn) {
- this.obj = obj;
- this.methodName = sFn;
- this.method = obj[sFn];
- this.before = {};
- this.after = {};
-};
+ /**
+ * Unique subscriber id
+ * @property id
+ * @type String
+ */
+ this.id = Y.stamp(this);
-/**
- * Register a aop subscriber
- * @method register
- * @param sid {string} the subscriber id
- * @param fn {Function} the function to execute
- * @param when {string} when to execute the function
- */
-DO.Method.prototype.register = function (sid, fn, when) {
- if (when) {
- this.after[sid] = fn;
- } else {
- this.before[sid] = fn;
- }
-};
+ /**
+ * Additional arguments to propagate to the subscriber
+ * @property args
+ * @type Array
+ */
+ this.args = args;
-/**
- * Unregister a aop subscriber
- * @method delete
- * @param sid {string} the subscriber id
- * @param fn {Function} the function to execute
- * @param when {string} when to execute the function
- */
-DO.Method.prototype._delete = function (sid) {
- delete this.before[sid];
- delete this.after[sid];
-};
+ /**
+ * Custom events for a given fire transaction.
+ * @property events
+ * @type {EventTarget}
+ */
+ // this.events = null;
-/**
- * Y.Do.Halt
or Y.Do.Prevent
, neither the wrapped
- * function nor any after phase subscribers will be executed.Y.Do.Halt
or
- * Y.Do.AlterReturn
.
- *
- * @method exec
- * @param arg* {any} Arguments are passed to the wrapping and wrapped functions
- * @return {any} Return value of wrapped function unless overwritten (see above)
- */
-DO.Method.prototype.exec = function () {
+ /**
+ * This listener only reacts to the event once
+ * @property once
+ */
+ // this.once = false;
- var args = Y.Array(arguments, 0, true),
- i, ret, newRet,
- bf = this.before,
- af = this.after,
- prevented = false;
+};
- // execute before
- for (i in bf) {
- if (bf.hasOwnProperty(i)) {
- ret = bf[i].apply(this.obj, args);
- if (ret) {
- switch (ret.constructor) {
- case DO.Halt:
- return ret.retVal;
- case DO.AlterArgs:
- args = ret.newArgs;
- break;
- case DO.Prevent:
- prevented = true;
- break;
- default:
- }
+Y.Subscriber.prototype = {
+ constructor: Y.Subscriber,
+
+ _notify: function(c, args, ce) {
+ if (this.deleted && !this.postponed) {
+ if (this.postponed) {
+ delete this.fn;
+ delete this.context;
+ } else {
+ delete this.postponed;
+ return null;
}
}
- }
+ var a = this.args, ret;
+ switch (ce.signature) {
+ case 0:
+ ret = this.fn.call(c, ce.type, args, c);
+ break;
+ case 1:
+ ret = this.fn.call(c, args[0] || null, c);
+ break;
+ default:
+ if (a || args) {
+ args = args || [];
+ a = (a) ? args.concat(a) : args;
+ ret = this.fn.apply(c, a);
+ } else {
+ ret = this.fn.call(c);
+ }
+ }
- // execute method
- if (!prevented) {
- ret = this.method.apply(this.obj, args);
- }
+ if (this.once) {
+ ce._delete(this);
+ }
- DO.originalRetVal = ret;
- DO.currentRetVal = ret;
+ return ret;
+ },
- // execute after methods.
- for (i in af) {
- if (af.hasOwnProperty(i)) {
- newRet = af[i].apply(this.obj, args);
- // Stop processing if a Halt object is returned
- if (newRet && newRet.constructor == DO.Halt) {
- return newRet.retVal;
- // Check for a new return value
- } else if (newRet && newRet.constructor == DO.AlterReturn) {
- ret = newRet.newRetVal;
- // Update the static retval state
- DO.currentRetVal = ret;
+ /**
+ * Executes the subscriber.
+ * @method notify
+ * @param args {Array} Arguments array for the subscriber.
+ * @param ce {CustomEvent} The custom event that sent the notification.
+ */
+ notify: function(args, ce) {
+ var c = this.context,
+ ret = true;
+
+ if (!c) {
+ c = (ce.contextFn) ? ce.contextFn() : ce.context;
+ }
+
+ // only catch errors if we will not re-throw them.
+ if (Y.config.throwFail) {
+ ret = this._notify(c, args, ce);
+ } else {
+ try {
+ ret = this._notify(c, args, ce);
+ } catch (e) {
+ Y.error(this + ' failed: ' + e.message, e);
}
}
- }
- return ret;
-};
+ return ret;
+ },
-//////////////////////////////////////////////////////////////////////////
+ /**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute.
+ * @param {Object} context optional 'this' keyword for the listener.
+ * @return {boolean} true if the supplied arguments match this
+ * subscriber's signature.
+ */
+ contains: function(fn, context) {
+ if (context) {
+ return ((this.fn == fn) && this.context == context);
+ } else {
+ return (this.fn == fn);
+ }
+ }
-/**
- * Return an AlterArgs object when you want to change the arguments that
- * were passed into the function. Useful for Do.before subscribers. An
- * example would be a service that scrubs out illegal characters prior to
- * executing the core business logic.
- * @class Do.AlterArgs
- * @constructor
- * @param msg {String} (optional) Explanation of the altered return value
- * @param newArgs {Array} Call parameters to be used for the original method
- * instead of the arguments originally passed in.
- */
-DO.AlterArgs = function(msg, newArgs) {
- this.msg = msg;
- this.newArgs = newArgs;
};
-
/**
- * Return an AlterReturn object when you want to change the result returned
- * from the core method to the caller. Useful for Do.after subscribers.
- * @class Do.AlterReturn
+ * Return value from all subscribe operations
+ * @class EventHandle
* @constructor
- * @param msg {String} (optional) Explanation of the altered return value
- * @param newRetVal {any} Return value passed to code that invoked the wrapped
- * function.
+ * @param {CustomEvent} evt the custom event.
+ * @param {Subscriber} sub the subscriber.
*/
-DO.AlterReturn = function(msg, newRetVal) {
- this.msg = msg;
- this.newRetVal = newRetVal;
-};
-
-/**
- * Return a Halt object when you want to terminate the execution
- * of all subsequent subscribers as well as the wrapped method
- * if it has not exectued yet. Useful for Do.before subscribers.
- * @class Do.Halt
- * @constructor
- * @param msg {String} (optional) Explanation of why the termination was done
- * @param retVal {any} Return value passed to code that invoked the wrapped
- * function.
- */
-DO.Halt = function(msg, retVal) {
- this.msg = msg;
- this.retVal = retVal;
-};
-
-/**
- * Return a Prevent object when you want to prevent the wrapped function
- * from executing, but want the remaining listeners to execute. Useful
- * for Do.before subscribers.
- * @class Do.Prevent
- * @constructor
- * @param msg {String} (optional) Explanation of why the termination was done
- */
-DO.Prevent = function(msg) {
- this.msg = msg;
-};
-
-/**
- * Return an Error object when you want to terminate the execution
- * of all subsequent method calls.
- * @class Do.Error
- * @constructor
- * @param msg {String} (optional) Explanation of the altered return value
- * @param retVal {any} Return value passed to code that invoked the wrapped
- * function.
- * @deprecated use Y.Do.Halt or Y.Do.Prevent
- */
-DO.Error = DO.Halt;
-
-
-//////////////////////////////////////////////////////////////////////////
-
-// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
-
-
-/**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event-custom
- * @submodule event-custom-base
- */
-
-
-// var onsubscribeType = "_event:onsub",
-var AFTER = 'after',
- CONFIGS = [
- 'broadcast',
- 'monitored',
- 'bubbles',
- 'context',
- 'contextFn',
- 'currentTarget',
- 'defaultFn',
- 'defaultTargetOnly',
- 'details',
- 'emitFacade',
- 'fireOnce',
- 'async',
- 'host',
- 'preventable',
- 'preventedFn',
- 'queuable',
- 'silent',
- 'stoppedFn',
- 'target',
- 'type'
- ],
-
- YUI3_SIGNATURE = 9,
- YUI_LOG = 'yui:log';
-
-/**
- * Return value from all subscribe operations
- * @class EventHandle
- * @constructor
- * @param {CustomEvent} evt the custom event.
- * @param {Subscriber} sub the subscriber.
- */
-Y.EventHandle = function(evt, sub) {
+Y.EventHandle = function(evt, sub) {
/**
* The custom event
@@ -11907,6204 +8993,5445 @@ Y.EventHandle.prototype = {
};
/**
- * The CustomEvent class lets you define events for your application
- * that can be subscribed to by one or more independent component.
- *
- * @param {String} type The type of event, which is passed to the callback
- * when the event fires.
- * @param {object} o configuration object.
- * @class CustomEvent
- * @constructor
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
*/
-Y.CustomEvent = function(type, o) {
- // if (arguments.length > 2) {
-// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
- // }
+/**
+ * EventTarget provides the implementation for any object to
+ * publish, subscribe and fire to custom events, and also
+ * alows other EventTargets to target the object with events
+ * sourced from the other object.
+ * EventTarget is designed to be used with Y.augment to wrap
+ * EventCustom in an interface that allows events to be listened to
+ * and fired by name. This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ * @class EventTarget
+ * @param opts a configuration object
+ * @config emitFacade {boolean} if true, all events will emit event
+ * facade payloads by default (default false)
+ * @config prefix {string} the prefix to apply to non-prefixed event names
+ * @config chain {boolean} if true, on/after/detach return the host to allow
+ * chaining, otherwise they return an EventHandle (default false)
+ */
- o = o || {};
+var L = Y.Lang,
+ PREFIX_DELIMITER = ':',
+ CATEGORY_DELIMITER = '|',
+ AFTER_PREFIX = '~AFTER~',
+ YArray = Y.Array,
- this.id = Y.stamp(this);
+ _wildType = Y.cached(function(type) {
+ return type.replace(/(.*)(:)(.*)/, "*$2$3");
+ }),
/**
- * The type of event, returned to subscribers when the event fires
- * @property type
- * @type string
+ * If the instance has a prefix attribute and the
+ * event type is not prefixed, the instance prefix is
+ * applied to the supplied type.
+ * @method _getType
+ * @private
*/
- this.type = type;
+ _getType = Y.cached(function(type, pre) {
- /**
- * The context the the event will fire from by default. Defaults to the YUI
- * instance.
- * @property context
- * @type object
- */
- this.context = Y;
+ if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
+ return type;
+ }
+
+ return pre + PREFIX_DELIMITER + type;
+ }),
/**
- * Monitor when an event is attached or detached.
- *
- * @property monitored
- * @type boolean
+ * Returns an array with the detach key (if provided),
+ * and the prefixed event name from _getType
+ * Y.on('detachcategory| menu:click', fn)
+ * @method _parseType
+ * @private
*/
- // this.monitored = false;
+ _parseType = Y.cached(function(type, pre) {
- this.logSystem = (type == YUI_LOG);
+ var t = type, detachcategory, after, i;
- /**
- * If 0, this event does not broadcast. If 1, the YUI instance is notified
- * every time this event fires. If 2, the YUI instance and the YUI global
- * (if event is enabled on the global) are notified every time this event
- * fires.
- * @property broadcast
- * @type int
- */
- // this.broadcast = 0;
+ if (!L.isString(t)) {
+ return t;
+ }
- /**
- * By default all custom events are logged in the debug build, set silent
- * to true to disable debug outpu for this event.
- * @property silent
- * @type boolean
- */
- this.silent = this.logSystem;
+ i = t.indexOf(AFTER_PREFIX);
- /**
- * Specifies whether this event should be queued when the host is actively
- * processing an event. This will effect exectution order of the callbacks
- * for the various events.
- * @property queuable
- * @type boolean
- * @default false
- */
- // this.queuable = false;
+ if (i > -1) {
+ after = true;
+ t = t.substr(AFTER_PREFIX.length);
+ }
- /**
- * The subscribers to this event
- * @property subscribers
- * @type Subscriber {}
- */
- this.subscribers = {};
+ i = t.indexOf(CATEGORY_DELIMITER);
- /**
- * 'After' subscribers
- * @property afters
- * @type Subscriber {}
- */
- this.afters = {};
+ if (i > -1) {
+ detachcategory = t.substr(0, (i));
+ t = t.substr(i+1);
+ if (t == '*') {
+ t = null;
+ }
+ }
- /**
- * This event has fired if true
- *
- * @property fired
- * @type boolean
- * @default false;
- */
- // this.fired = false;
+ // detach category, full type with instance prefix, is this an after listener, short type
+ return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
+ }),
- /**
- * An array containing the arguments the custom event
- * was last fired with.
- * @property firedWith
- * @type Array
- */
- // this.firedWith;
+ ET = function(opts) {
- /**
- * This event should only fire one time if true, and if
- * it has fired, any new subscribers should be notified
- * immediately.
- *
- * @property fireOnce
- * @type boolean
- * @default false;
- */
- // this.fireOnce = false;
- /**
- * fireOnce listeners will fire syncronously unless async
- * is set to true
- * @property async
- * @type boolean
- * @default false
- */
- //this.async = false;
+ var o = (L.isObject(opts)) ? opts : {};
- /**
- * Flag for stopPropagation that is modified during fire()
- * 1 means to stop propagation to bubble targets. 2 means
- * to also stop additional subscribers on this target.
- * @property stopped
- * @type int
- */
- // this.stopped = 0;
+ this._yuievt = this._yuievt || {
- /**
- * Flag for preventDefault that is modified during fire().
- * if it is not 0, the default behavior for this event
- * @property prevented
- * @type int
- */
- // this.prevented = 0;
+ id: Y.guid(),
- /**
- * Specifies the host for this custom event. This is used
- * to enable event bubbling
- * @property host
- * @type EventTarget
- */
- // this.host = null;
+ events: {},
- /**
- * The default function to execute after event listeners
- * have fire, but only if the default action was not
- * prevented.
- * @property defaultFn
- * @type Function
- */
- // this.defaultFn = null;
+ targets: {},
+
+ config: o,
+
+ chain: ('chain' in o) ? o.chain : Y.config.chain,
+
+ bubbling: false,
+
+ defaults: {
+ context: o.context || this,
+ host: this,
+ emitFacade: o.emitFacade,
+ fireOnce: o.fireOnce,
+ queuable: o.queuable,
+ monitored: o.monitored,
+ broadcast: o.broadcast,
+ defaultTargetOnly: o.defaultTargetOnly,
+ bubbles: ('bubbles' in o) ? o.bubbles : true
+ }
+ };
+
+ };
- /**
- * The function to execute if a subscriber calls
- * stopPropagation or stopImmediatePropagation
- * @property stoppedFn
- * @type Function
- */
- // this.stoppedFn = null;
+
+ET.prototype = {
+ constructor: ET,
/**
- * The function to execute if a subscriber calls
- * preventDefault
- * @property preventedFn
- * @type Function
+ * Listen to a custom event hosted by this object one time.
+ * This is the equivalent to on
except the
+ * listener is immediatelly detached when it is executed.
+ * @method once
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @param context {object} optional execution context.
+ * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
+ * @return the event target or a detach handle per 'chain' config
*/
- // this.preventedFn = null;
+ once: function() {
+ var handle = this.on.apply(this, arguments);
+ handle.batch(function(hand) {
+ if (hand.sub) {
+ hand.sub.once = true;
+ }
+ });
+ return handle;
+ },
/**
- * Specifies whether or not this event's default function
- * can be cancelled by a subscriber by executing preventDefault()
- * on the event facade
- * @property preventable
- * @type boolean
- * @default true
+ * Listen to a custom event hosted by this object one time.
+ * This is the equivalent to after
except the
+ * listener is immediatelly detached when it is executed.
+ * @method onceAfter
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @param context {object} optional execution context.
+ * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
+ * @return the event target or a detach handle per 'chain' config
*/
- this.preventable = true;
+ onceAfter: function() {
+ var args = YArray(arguments, 0, true);
+ args[0] = AFTER_PREFIX + args[0];
+
+ return this.once.apply(this, args);
+ },
/**
- * Specifies whether or not a subscriber can stop the event propagation
- * via stopPropagation(), stopImmediatePropagation(), or halt()
- *
- * Events can only bubble if emitFacade is true.
- *
- * @property bubbles
- * @type boolean
- * @default true
+ * Takes the type parameter passed to 'on' and parses out the
+ * various pieces that could be included in the type. If the
+ * event type is passed without a prefix, it will be expanded
+ * to include the prefix one is supplied or the event target
+ * is configured with a default prefix.
+ * @method parseType
+ * @param {string} type the type
+ * @param {string} [pre=this._yuievt.config.prefix] the prefix
+ * @since 3.3.0
+ * @return {Array} an array containing:
+ * * the detach category, if supplied,
+ * * the prefixed event type,
+ * * whether or not this is an after listener,
+ * * the supplied event type
*/
- this.bubbles = true;
+ parseType: function(type, pre) {
+ return _parseType(type, pre || this._yuievt.config.prefix);
+ },
/**
- * Supports multiple options for listener signatures in order to
- * port YUI 2 apps.
- * @property signature
- * @type int
- * @default 9
+ * Subscribe to a custom event hosted by this object
+ * @method on
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @param context {object} optional execution context.
+ * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
+ * @return the event target or a detach handle per 'chain' config
*/
- this.signature = YUI3_SIGNATURE;
+ on: function(type, fn, context) {
- this.subCount = 0;
- this.afterCount = 0;
+ var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
+ detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
+ Node = Y.Node, n, domevent, isArr;
- // this.hasSubscribers = false;
+ // full name, args, detachcategory, after
+ this._monitor('attach', parts[1], {
+ args: arguments,
+ category: parts[0],
+ after: parts[2]
+ });
- // this.hasAfters = false;
+ if (L.isObject(type)) {
- /**
- * If set to true, the custom event will deliver an EventFacade object
- * that is similar to a DOM event object.
- * @property emitFacade
- * @type boolean
- * @default false
- */
- // this.emitFacade = false;
+ if (L.isFunction(type)) {
+ return Y.Do.before.apply(Y.Do, arguments);
+ }
- this.applyConfig(o, true);
+ f = fn;
+ c = context;
+ args = YArray(arguments, 0, true);
+ ret = [];
- // this.log("Creating " + this.type);
+ if (L.isArray(type)) {
+ isArr = true;
+ }
-};
+ after = type._after;
+ delete type._after;
-Y.CustomEvent.prototype = {
+ Y.each(type, function(v, k) {
- hasSubs: function(when) {
- var s = this.subCount, a = this.afterCount, sib = this.sibling;
+ if (L.isObject(v)) {
+ f = v.fn || ((L.isFunction(v)) ? v : f);
+ c = v.context || c;
+ }
- if (sib) {
- s += sib.subCount;
- a += sib.afterCount;
- }
+ var nv = (after) ? AFTER_PREFIX : '';
- if (when) {
- return (when == 'after') ? a : s;
- }
+ args[0] = nv + ((isArr) ? v : k);
+ args[1] = f;
+ args[2] = c;
- return (s + a);
- },
+ ret.push(this.on.apply(this, args));
- /**
- * Monitor the event state for the subscribed event. The first parameter
- * is what should be monitored, the rest are the normal parameters when
- * subscribing to an event.
- * @method monitor
- * @param what {string} what to monitor ('detach', 'attach', 'publish').
- * @return {EventHandle} return value from the monitor event subscription.
- */
- monitor: function(what) {
- this.monitored = true;
- var type = this.id + '|' + this.type + '_' + what,
- args = Y.Array(arguments, 0, true);
- args[0] = type;
- return this.host.on.apply(this.host, args);
- },
+ }, this);
- /**
- * Get all of the subscribers to this event and any sibling event
- * @method getSubs
- * @return {Array} first item is the on subscribers, second the after.
- */
- getSubs: function() {
- var s = Y.merge(this.subscribers), a = Y.merge(this.afters), sib = this.sibling;
+ return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
- if (sib) {
- Y.mix(s, sib.subscribers);
- Y.mix(a, sib.afters);
}
- return [s, a];
- },
-
- /**
- * Apply configuration properties. Only applies the CONFIG whitelist
- * @method applyConfig
- * @param o hash of properties to apply.
- * @param force {boolean} if true, properties that exist on the event
- * will be overwritten.
- */
- applyConfig: function(o, force) {
- if (o) {
- Y.mix(this, o, force, CONFIGS);
- }
- },
+ detachcategory = parts[0];
+ after = parts[2];
+ shorttype = parts[3];
- _on: function(fn, context, args, when) {
-
- if (!fn) {
- this.log('Invalid callback for CE: ' + this.type);
+ // extra redirection so we catch adaptor events too. take a look at this.
+ if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
+ args = YArray(arguments, 0, true);
+ args.splice(2, 0, Node.getDOMNode(this));
+ return Y.on.apply(Y, args);
}
- var s = new Y.Subscriber(fn, context, args, when);
+ type = parts[1];
- if (this.fireOnce && this.fired) {
- if (this.async) {
- setTimeout(Y.bind(this._notify, this, s, this.firedWith), 0);
- } else {
- this._notify(s, this.firedWith);
+ if (Y.instanceOf(this, YUI)) {
+
+ adapt = Y.Env.evt.plugins[type];
+ args = YArray(arguments, 0, true);
+ args[0] = shorttype;
+
+ if (Node) {
+ n = args[2];
+
+ if (Y.instanceOf(n, Y.NodeList)) {
+ n = Y.NodeList.getDOMNodes(n);
+ } else if (Y.instanceOf(n, Node)) {
+ n = Node.getDOMNode(n);
+ }
+
+ domevent = (shorttype in Node.DOM_EVENTS);
+
+ // Captures both DOM events and event plugins.
+ if (domevent) {
+ args[2] = n;
+ }
+ }
+
+ // check for the existance of an event adaptor
+ if (adapt) {
+ handle = adapt.on.apply(Y, args);
+ } else if ((!type) || domevent) {
+ handle = Y.Event._attach(args);
}
+
}
- if (when == AFTER) {
- this.afters[s.id] = s;
- this.afterCount++;
- } else {
- this.subscribers[s.id] = s;
- this.subCount++;
+ if (!handle) {
+ ce = this._yuievt.events[type] || this.publish(type);
+ handle = ce._on(fn, context, (arguments.length > 3) ? YArray(arguments, 3, true) : null, (after) ? 'after' : true);
}
- return new Y.EventHandle(this, s);
+ if (detachcategory) {
+ store[detachcategory] = store[detachcategory] || {};
+ store[detachcategory][type] = store[detachcategory][type] || [];
+ store[detachcategory][type].push(handle);
+ }
+
+ return (this._yuievt.chain) ? this : handle;
},
/**
- * Listen for this event
+ * subscribe to an event
* @method subscribe
- * @param {Function} fn The function to execute.
- * @return {EventHandle} Unsubscribe handle.
- * @deprecated use on.
+ * @deprecated use on
*/
- subscribe: function(fn, context) {
- var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
- return this._on(fn, context, a, true);
+ subscribe: function() {
+ return this.on.apply(this, arguments);
},
/**
- * Listen for this event
- * @method on
- * @param {Function} fn The function to execute.
- * @param {object} context optional execution context.
- * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
- * when the event fires.
- * @return {EventHandle} An object with a detach method to detch the handler(s).
+ * Detach one or more listeners the from the specified event
+ * @method detach
+ * @param type {string|Object} Either the handle to the subscriber or the
+ * type of event. If the type
+ * is not specified, it will attempt to remove
+ * the listener from all hosted events.
+ * @param fn {Function} The subscribed function to unsubscribe, if not
+ * supplied, all subscribers will be removed.
+ * @param context {Object} The custom object passed to subscribe. This is
+ * optional, but if supplied will be used to
+ * disambiguate multiple listeners that are the same
+ * (e.g., you subscribe many object using a function
+ * that lives on the prototype)
+ * @return {EventTarget} the host
*/
- on: function(fn, context) {
- var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
- if (this.host) {
- this.host._monitor('attach', this.type, {
- args: arguments
- });
- }
- return this._on(fn, context, a, true);
- },
+ detach: function(type, fn, context) {
+ var evts = this._yuievt.events, i,
+ Node = Y.Node, isNode = Node && (Y.instanceOf(this, Node));
- /**
- * Listen for this event after the normal subscribers have been notified and
- * the default behavior has been applied. If a normal subscriber prevents the
- * default behavior, it also prevents after listeners from firing.
- * @method after
- * @param {Function} fn The function to execute.
- * @param {object} context optional execution context.
- * @param {mixed} arg* 0..n additional arguments to supply to the subscriber
- * when the event fires.
- * @return {EventHandle} handle Unsubscribe handle.
- */
- after: function(fn, context) {
- var a = (arguments.length > 2) ? Y.Array(arguments, 2, true) : null;
- return this._on(fn, context, a, AFTER);
- },
+ // detachAll disabled on the Y instance.
+ if (!type && (this !== Y)) {
+ for (i in evts) {
+ if (evts.hasOwnProperty(i)) {
+ evts[i].detach(fn, context);
+ }
+ }
+ if (isNode) {
+ Y.Event.purgeElement(Node.getDOMNode(this));
+ }
- /**
- * Detach listeners.
- * @method detach
- * @param {Function} fn The subscribed function to remove, if not supplied
- * all will be removed.
- * @param {Object} context The context object passed to subscribe.
- * @return {int} returns the number of subscribers unsubscribed.
- */
- detach: function(fn, context) {
- // unsubscribe handle
- if (fn && fn.detach) {
- return fn.detach();
+ return this;
}
- var i, s,
- found = 0,
- subs = Y.merge(this.subscribers, this.afters);
+ var parts = _parseType(type, this._yuievt.config.prefix),
+ detachcategory = L.isArray(parts) ? parts[0] : null,
+ shorttype = (parts) ? parts[3] : null,
+ adapt, store = Y.Env.evt.handles, detachhost, cat, args,
+ ce,
- for (i in subs) {
- if (subs.hasOwnProperty(i)) {
- s = subs[i];
- if (s && (!fn || fn === s.fn)) {
- this._delete(s);
- found++;
+ keyDetacher = function(lcat, ltype, host) {
+ var handles = lcat[ltype], ce, i;
+ if (handles) {
+ for (i = handles.length - 1; i >= 0; --i) {
+ ce = handles[i].evt;
+ if (ce.host === host || ce.el === host) {
+ handles[i].detach();
+ }
}
}
- }
+ };
- return found;
- },
+ if (detachcategory) {
- /**
- * Detach listeners.
- * @method unsubscribe
- * @param {Function} fn The subscribed function to remove, if not supplied
- * all will be removed.
- * @param {Object} context The context object passed to subscribe.
- * @return {int|undefined} returns the number of subscribers unsubscribed.
- * @deprecated use detach.
- */
- unsubscribe: function() {
- return this.detach.apply(this, arguments);
- },
+ cat = store[detachcategory];
+ type = parts[1];
+ detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
- /**
- * Notify a single subscriber
- * @method _notify
- * @param {Subscriber} s the subscriber.
- * @param {Array} args the arguments array to apply to the listener.
- * @private
- */
- _notify: function(s, args, ef) {
+ if (cat) {
+ if (type) {
+ keyDetacher(cat, type, detachhost);
+ } else {
+ for (i in cat) {
+ if (cat.hasOwnProperty(i)) {
+ keyDetacher(cat, i, detachhost);
+ }
+ }
+ }
- this.log(this.type + '->' + 'sub: ' + s.id);
+ return this;
+ }
- var ret;
+ // If this is an event handle, use it to detach
+ } else if (L.isObject(type) && type.detach) {
+ type.detach();
+ return this;
+ // extra redirection so we catch adaptor events too. take a look at this.
+ } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
+ args = YArray(arguments, 0, true);
+ args[2] = Node.getDOMNode(this);
+ Y.detach.apply(Y, args);
+ return this;
+ }
- ret = s.notify(args, this);
+ adapt = Y.Env.evt.plugins[shorttype];
- if (false === ret || this.stopped > 1) {
- this.log(this.type + ' cancelled by subscriber');
- return false;
+ // The YUI instance handles DOM events and adaptors
+ if (Y.instanceOf(this, YUI)) {
+ args = YArray(arguments, 0, true);
+ // use the adaptor specific detach code if
+ if (adapt && adapt.detach) {
+ adapt.detach.apply(Y, args);
+ return this;
+ // DOM event fork
+ } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
+ args[0] = type;
+ Y.Event.detach.apply(Y.Event, args);
+ return this;
+ }
}
- return true;
+ // ce = evts[type];
+ ce = evts[parts[1]];
+ if (ce) {
+ ce.detach(fn, context);
+ }
+
+ return this;
},
/**
- * Logger abstraction to centralize the application of the silent flag
- * @method log
- * @param {string} msg message to log.
- * @param {string} cat log category.
+ * detach a listener
+ * @method unsubscribe
+ * @deprecated use detach
*/
- log: function(msg, cat) {
- if (!this.silent) {
- }
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
},
/**
- * Notifies the subscribers. The callback functions will be executed
- * from the context specified when the event was created, and with the
- * following parameters:
- *
- *
- * @method fire
- * @param {Object*} arguments an arbitrary set of parameters to pass to
- * the handler.
- * @return {boolean} false if one of the subscribers returned false,
- * true otherwise.
- *
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method detachAll
+ * @param type {string} The type, or name of the event
*/
- fire: function() {
- if (this.fireOnce && this.fired) {
- this.log('fireOnce event: ' + this.type + ' already fired');
- return true;
- } else {
-
- var args = Y.Array(arguments, 0, true);
-
- // this doesn't happen if the event isn't published
- // this.host._monitor('fire', this.type, args);
-
- this.fired = true;
- this.firedWith = args;
-
- if (this.emitFacade) {
- return this.fireComplex(args);
- } else {
- return this.fireSimple(args);
- }
- }
+ detachAll: function(type) {
+ return this.detach(type);
},
- fireSimple: function(args) {
- this.stopped = 0;
- this.prevented = 0;
- if (this.hasSubs()) {
- // this._procSubs(Y.merge(this.subscribers, this.afters), args);
- var subs = this.getSubs();
- this._procSubs(subs[0], args);
- this._procSubs(subs[1], args);
- }
- this._broadcast(args);
- return this.stopped ? false : true;
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method unsubscribeAll
+ * @param type {string} The type, or name of the event
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
},
- // Requires the event-custom-complex module for full funcitonality.
- fireComplex: function(args) {
- args[0] = args[0] || {};
- return this.fireSimple(args);
- },
+ /**
+ * Creates a new custom event of the specified type. If a custom event
+ * by that name already exists, it will not be re-created. In either
+ * case the custom event is returned.
+ *
+ * @method publish
+ *
+ * @param type {string} the type, or name of the event
+ * @param opts {object} optional config params. Valid properties are:
+ *
+ *
+ *
+ *
+ * @return {CustomEvent} the custom event
+ *
+ */
+ publish: function(type, opts) {
+ var events, ce, ret, defaults,
+ edata = this._yuievt,
+ pre = edata.config.prefix;
- _procSubs: function(subs, args, ef) {
- var s, i;
- for (i in subs) {
- if (subs.hasOwnProperty(i)) {
- s = subs[i];
- if (s && s.fn) {
- if (false === this._notify(s, args, ef)) {
- this.stopped = 2;
- }
- if (this.stopped == 2) {
- return false;
- }
- }
- }
- }
+ type = (pre) ? _getType(type, pre) : type;
- return true;
- },
+ this._monitor('publish', type, {
+ args: arguments
+ });
- _broadcast: function(args) {
- if (!this.stopped && this.broadcast) {
+ if (L.isObject(type)) {
+ ret = {};
+ Y.each(type, function(v, k) {
+ ret[k] = this.publish(k, v || opts);
+ }, this);
- var a = Y.Array(args);
- a.unshift(this.type);
+ return ret;
+ }
- if (this.host !== Y) {
- Y.fire.apply(Y, a);
- }
+ events = edata.events;
+ ce = events[type];
- if (this.broadcast == 2) {
- Y.Global.fire.apply(Y.Global, a);
+ if (ce) {
+// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
+ if (opts) {
+ ce.applyConfig(opts, true);
}
+ } else {
+
+ defaults = edata.defaults;
+
+ // apply defaults
+ ce = new Y.CustomEvent(type,
+ (opts) ? Y.merge(defaults, opts) : defaults);
+ events[type] = ce;
}
- },
- /**
- * Removes all listeners
- * @method unsubscribeAll
- * @return {int} The number of listeners unsubscribed.
- * @deprecated use detachAll.
- */
- unsubscribeAll: function() {
- return this.detachAll.apply(this, arguments);
- },
+ // make sure we turn the broadcast flag off if this
+ // event was published as a result of bubbling
+ // if (opts instanceof Y.CustomEvent) {
+ // events[type].broadcast = false;
+ // }
- /**
- * Removes all listeners
- * @method detachAll
- * @return {int} The number of listeners unsubscribed.
- */
- detachAll: function() {
- return this.detach();
+ return events[type];
},
/**
- * @method _delete
- * @param subscriber object.
+ * This is the entry point for the event monitoring system.
+ * You can monitor 'attach', 'detach', 'fire', and 'publish'.
+ * When configured, these events generate an event. click ->
+ * click_attach, click_detach, click_publish -- these can
+ * be subscribed to like other events to monitor the event
+ * system. Inividual published events can have monitoring
+ * turned on or off (publish can't be turned off before it
+ * it published) by setting the events 'monitor' config.
+ *
+ * @method _monitor
+ * @param what {String} 'attach', 'detach', 'fire', or 'publish'
+ * @param type {String} Name of the event being monitored
+ * @param o {Object} Information about the event interaction, such as
+ * fire() args, subscription category, publish config
* @private
*/
- _delete: function(s) {
- if (s) {
- if (this.subscribers[s.id]) {
- delete this.subscribers[s.id];
- this.subCount--;
- }
- if (this.afters[s.id]) {
- delete this.afters[s.id];
- this.afterCount--;
- }
- }
-
- if (this.host) {
- this.host._monitor('detach', this.type, {
- ce: this,
- sub: s
- });
- }
-
- if (s) {
- // delete s.fn;
- // delete s.context;
- s.deleted = true;
+ _monitor: function(what, type, o) {
+ var monitorevt, ce = this.getEvent(type);
+ if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
+ monitorevt = type + '_' + what;
+ o.monitored = what;
+ this.fire.call(this, monitorevt, o);
}
- }
-};
-
-/////////////////////////////////////////////////////////////////////
+ },
-/**
- * Stores the subscriber information to be used when the event fires.
- * @param {Function} fn The wrapped function to execute.
- * @param {Object} context The value of the keyword 'this' in the listener.
- * @param {Array} args* 0..n additional arguments to supply the listener.
- *
- * @class Subscriber
- * @constructor
- */
-Y.Subscriber = function(fn, context, args) {
-
- /**
- * The callback that will be execute when the event fires
- * This is wrapped by Y.rbind if obj was supplied.
- * @property fn
- * @type Function
+ /**
+ * Fire a custom event by name. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters.
+ *
+ * If the custom event object hasn't been created, then the event hasn't
+ * been published and it has no subscribers. For performance sake, we
+ * immediate exit in this case. This means the event won't bubble, so
+ * if the intention is that a bubble target be notified, the event must
+ * be published on this object first.
+ *
+ * The first argument is the event type, and any additional arguments are
+ * passed to the listeners as parameters. If the first of these is an
+ * object literal, and the event is configured to emit an event facade,
+ * that object is mixed into the event facade and the facade is provided
+ * in place of the original object.
+ *
+ * @method fire
+ * @param type {String|Object} The type of the event, or an object that contains
+ * a 'type' property.
+ * @param arguments {Object*} an arbitrary set of parameters to pass to
+ * the handler. If the first of these is an object literal and the event is
+ * configured to emit an event facade, the event facade will replace that
+ * parameter after the properties the object literal contains are copied to
+ * the event facade.
+ * @return {EventTarget} the event host
+ *
*/
- this.fn = fn;
+ fire: function(type) {
- /**
- * Optional 'this' keyword for the listener
- * @property context
- * @type Object
- */
- this.context = context;
+ var typeIncluded = L.isString(type),
+ t = (typeIncluded) ? type : (type && type.type),
+ ce, ret, pre = this._yuievt.config.prefix, ce2,
+ args = (typeIncluded) ? YArray(arguments, 1, true) : arguments;
- /**
- * Unique subscriber id
- * @property id
- * @type String
- */
- this.id = Y.stamp(this);
+ t = (pre) ? _getType(t, pre) : t;
- /**
- * Additional arguments to propagate to the subscriber
- * @property args
- * @type Array
- */
- this.args = args;
+ this._monitor('fire', t, {
+ args: args
+ });
- /**
- * Custom events for a given fire transaction.
- * @property events
- * @type {EventTarget}
- */
- // this.events = null;
+ ce = this.getEvent(t, true);
+ ce2 = this.getSibling(t, ce);
- /**
- * This listener only reacts to the event once
- * @property once
- */
- // this.once = false;
+ if (ce2 && !ce) {
+ ce = this.publish(t);
+ }
-};
+ // this event has not been published or subscribed to
+ if (!ce) {
+ if (this._yuievt.hasTargets) {
+ return this.bubble({ type: t }, args, this);
+ }
-Y.Subscriber.prototype = {
+ // otherwise there is nothing to be done
+ ret = true;
+ } else {
+ ce.sibling = ce2;
+ ret = ce.fire.apply(ce, args);
+ }
- _notify: function(c, args, ce) {
- if (this.deleted && !this.postponed) {
- if (this.postponed) {
- delete this.fn;
- delete this.context;
- } else {
- delete this.postponed;
- return null;
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ getSibling: function(type, ce) {
+ var ce2;
+ // delegate to *:type events if there are subscribers
+ if (type.indexOf(PREFIX_DELIMITER) > -1) {
+ type = _wildType(type);
+ // console.log(type);
+ ce2 = this.getEvent(type, true);
+ if (ce2) {
+ // console.log("GOT ONE: " + type);
+ ce2.applyConfig(ce);
+ ce2.bubbles = false;
+ ce2.broadcast = 0;
+ // ret = ce2.fire.apply(ce2, a);
}
}
- var a = this.args, ret;
- switch (ce.signature) {
- case 0:
- ret = this.fn.call(c, ce.type, args, c);
- break;
- case 1:
- ret = this.fn.call(c, args[0] || null, c);
- break;
- default:
- if (a || args) {
- args = args || [];
- a = (a) ? args.concat(a) : args;
- ret = this.fn.apply(c, a);
- } else {
- ret = this.fn.call(c);
- }
- }
- if (this.once) {
- ce._delete(this);
- }
+ return ce2;
+ },
- return ret;
+ /**
+ * Returns the custom event of the provided type has been created, a
+ * falsy value otherwise
+ * @method getEvent
+ * @param type {string} the type, or name of the event
+ * @param prefixed {string} if true, the type is prefixed already
+ * @return {CustomEvent} the custom event or null
+ */
+ getEvent: function(type, prefixed) {
+ var pre, e;
+ if (!prefixed) {
+ pre = this._yuievt.config.prefix;
+ type = (pre) ? _getType(type, pre) : type;
+ }
+ e = this._yuievt.events;
+ return e[type] || null;
},
/**
- * Executes the subscriber.
- * @method notify
- * @param args {Array} Arguments array for the subscriber.
- * @param ce {CustomEvent} The custom event that sent the notification.
+ * Subscribe to a custom event hosted by this object. The
+ * supplied callback will execute after any listeners add
+ * via the subscribe method, and after the default function,
+ * if configured for the event, has executed.
+ * @method after
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @param context {object} optional execution context.
+ * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
+ * @return the event target or a detach handle per 'chain' config
*/
- notify: function(args, ce) {
- var c = this.context,
- ret = true;
+ after: function(type, fn) {
- if (!c) {
- c = (ce.contextFn) ? ce.contextFn() : ce.context;
- }
+ var a = YArray(arguments, 0, true);
- // only catch errors if we will not re-throw them.
- if (Y.config.throwFail) {
- ret = this._notify(c, args, ce);
- } else {
- try {
- ret = this._notify(c, args, ce);
- } catch (e) {
- Y.error(this + ' failed: ' + e.message, e);
- }
+ switch (L.type(type)) {
+ case 'function':
+ return Y.Do.after.apply(Y.Do, arguments);
+ case 'array':
+ // YArray.each(a[0], function(v) {
+ // v = AFTER_PREFIX + v;
+ // });
+ // break;
+ case 'object':
+ a[0]._after = true;
+ break;
+ default:
+ a[0] = AFTER_PREFIX + type;
}
- return ret;
+ return this.on.apply(this, a);
+
},
/**
- * Returns true if the fn and obj match this objects properties.
- * Used by the unsubscribe method to match the right subscriber.
+ * Executes the callback before a DOM event, custom event
+ * or method. If the first argument is a function, it
+ * is assumed the target is a method. For DOM and custom
+ * events, this is an alias for Y.on.
*
- * @method contains
- * @param {Function} fn the function to execute.
- * @param {Object} context optional 'this' keyword for the listener.
- * @return {boolean} true if the supplied arguments match this
- * subscriber's signature.
+ * For DOM and custom events:
+ * type, callback, context, 0-n arguments
+ *
+ * For methods:
+ * callback, object (method host), methodName, context, 0-n arguments
+ *
+ * @method before
+ * @return detach handle
*/
- contains: function(fn, context) {
- if (context) {
- return ((this.fn == fn) && this.context == context);
- } else {
- return (this.fn == fn);
- }
+ before: function() {
+ return this.on.apply(this, arguments);
}
};
+Y.EventTarget = ET;
+
+// make Y an event target
+Y.mix(Y, ET.prototype);
+ET.call(Y, { bubbles: false });
+
+YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
+
/**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event-custom
- * @submodule event-custom-base
+ * Hosts YUI page level events. This is where events bubble to
+ * when the broadcast config is set to 2. This property is
+ * only available if the custom event module is loaded.
+ * @property Global
+ * @type EventTarget
+ * @for YUI
*/
+Y.Global = YUI.Env.globalEvents;
+
+// @TODO implement a global namespace function on Y.Global?
/**
- * EventTarget provides the implementation for any object to
- * publish, subscribe and fire to custom events, and also
- * alows other EventTargets to target the object with events
- * sourced from the other object.
- * EventTarget is designed to be used with Y.augment to wrap
- * EventCustom in an interface that allows events to be listened to
- * and fired by name. This makes it possible for implementing code to
- * subscribe to an event that either has not been created yet, or will
- * not be created at all.
- * @class EventTarget
- * @param opts a configuration object
- * @config emitFacade {boolean} if true, all events will emit event
- * facade payloads by default (default false)
- * @config prefix {string} the prefix to apply to non-prefixed event names
- * @config chain {boolean} if true, on/after/detach return the host to allow
- * chaining, otherwise they return an EventHandle (default false)
+ * YUI
's on
method is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events, DOM events, and
+ * function events. detach
is also provided to remove listeners
+ * serviced by this function.
+ *
+ * The signature that on
accepts varies depending on the type
+ * of event being consumed. Refer to the specific methods that will
+ * service a specific request for additional information about subscribing
+ * to that type of event.
+ *
+ *
+ *
+ *
+ * EventTarget
's on
method.
+ *
+ *
+ * Example:
+ * Y.on('drag:drophit', function() { // start work });
+ * Event
's
+ * attach
method.
+ *
+ *
+ * Example:
+ * Y.on('click', function(e) { // something was clicked }, '#someelement');
+ * Event.Do
's
+ * before
method.
+ *
+ *
+ * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');
+ * on
corresponds to the moment before any default behavior of
+ * the event. after
works the same way, but these listeners
+ * execute after the event's default behavior. before
is an
+ * alias for on
.
+ *
+ * @method on
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
*/
-var L = Y.Lang,
- PREFIX_DELIMITER = ':',
- CATEGORY_DELIMITER = '|',
- AFTER_PREFIX = '~AFTER~',
- YArray = Y.Array,
+ /**
+ * Listen for an event one time. Equivalent to on
, except that
+ * the listener is immediately detached when executed.
+ * @see on
+ * @method once
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
- _wildType = Y.cached(function(type) {
- return type.replace(/(.*)(:)(.*)/, "*$2$3");
- }),
+/**
+ * after() is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events,
+ * DOM events, and AOP events. This works the same way as
+ * the on() function, only it operates after any default
+ * behavior for the event has executed. @see on
for more
+ * information.
+ * @method after
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+
+}, '3.4.0' ,{requires:['oop']});
+YUI.add('event-custom-complex', function(Y) {
+
+
+/**
+ * Adds event facades, preventable default behavior, and bubbling.
+ * events.
+ * @module event-custom
+ * @submodule event-custom-complex
+ */
+
+var FACADE,
+ FACADE_KEYS,
+ EMPTY = {},
+ CEProto = Y.CustomEvent.prototype,
+ ETProto = Y.EventTarget.prototype;
+
+/**
+ * Wraps and protects a custom event for use when emitFacade is set to true.
+ * Requires the event-custom-complex module
+ * @class EventFacade
+ * @param e {Event} the custom event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ */
+
+Y.EventFacade = function(e, currentTarget) {
+
+ e = e || EMPTY;
+
+ this._event = e;
/**
- * If the instance has a prefix attribute and the
- * event type is not prefixed, the instance prefix is
- * applied to the supplied type.
- * @method _getType
- * @private
+ * The arguments passed to fire
+ * @property details
+ * @type Array
*/
- _getType = Y.cached(function(type, pre) {
+ this.details = e.details;
- if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
- return type;
- }
+ /**
+ * The event type, this can be overridden by the fire() payload
+ * @property type
+ * @type string
+ */
+ this.type = e.type;
- return pre + PREFIX_DELIMITER + type;
- }),
+ /**
+ * The real event type
+ * @property type
+ * @type string
+ */
+ this._type = e.type;
+
+ //////////////////////////////////////////////////////
/**
- * Returns an array with the detach key (if provided),
- * and the prefixed event name from _getType
- * Y.on('detachcategory| menu:click', fn)
- * @method _parseType
- * @private
+ * Node reference for the targeted eventtarget
+ * @property target
+ * @type Node
*/
- _parseType = Y.cached(function(type, pre) {
+ this.target = e.target;
- var t = type, detachcategory, after, i;
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @property currentTarget
+ * @type Node
+ */
+ this.currentTarget = currentTarget;
- if (!L.isString(t)) {
- return t;
- }
+ /**
+ * Node reference to the relatedTarget
+ * @property relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = e.relatedTarget;
- i = t.indexOf(AFTER_PREFIX);
+};
- if (i > -1) {
- after = true;
- t = t.substr(AFTER_PREFIX.length);
- }
+Y.extend(Y.EventFacade, Object, {
- i = t.indexOf(CATEGORY_DELIMITER);
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ stopPropagation: function() {
+ this._event.stopPropagation();
+ this.stopped = 1;
+ },
- if (i > -1) {
- detachcategory = t.substr(0, (i));
- t = t.substr(i+1);
- if (t == '*') {
- t = null;
- }
- }
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ stopImmediatePropagation: function() {
+ this._event.stopImmediatePropagation();
+ this.stopped = 2;
+ },
- // detach category, full type with instance prefix, is this an after listener, short type
- return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
- }),
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ */
+ preventDefault: function() {
+ this._event.preventDefault();
+ this.prevented = 1;
+ },
- ET = function(opts) {
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ halt: function(immediate) {
+ this._event.halt(immediate);
+ this.prevented = 1;
+ this.stopped = (immediate) ? 2 : 1;
+ }
+});
- var o = (L.isObject(opts)) ? opts : {};
+CEProto.fireComplex = function(args) {
- this._yuievt = this._yuievt || {
+ var es, ef, q, queue, ce, ret, events, subs, postponed,
+ self = this, host = self.host || self, next, oldbubble;
- id: Y.guid(),
+ if (self.stack) {
+ // queue this event if the current item in the queue bubbles
+ if (self.queuable && self.type != self.stack.next.type) {
+ self.log('queue ' + self.type);
+ self.stack.queue.push([self, args]);
+ return true;
+ }
+ }
- events: {},
+ es = self.stack || {
+ // id of the first event in the stack
+ id: self.id,
+ next: self,
+ silent: self.silent,
+ stopped: 0,
+ prevented: 0,
+ bubbling: null,
+ type: self.type,
+ // defaultFnQueue: new Y.Queue(),
+ afterQueue: new Y.Queue(),
+ defaultTargetOnly: self.defaultTargetOnly,
+ queue: []
+ };
- targets: {},
+ subs = self.getSubs();
- config: o,
+ self.stopped = (self.type !== es.type) ? 0 : es.stopped;
+ self.prevented = (self.type !== es.type) ? 0 : es.prevented;
- chain: ('chain' in o) ? o.chain : Y.config.chain,
+ self.target = self.target || host;
- bubbling: false,
+ events = new Y.EventTarget({
+ fireOnce: true,
+ context: host
+ });
- defaults: {
- context: o.context || this,
- host: this,
- emitFacade: o.emitFacade,
- fireOnce: o.fireOnce,
- queuable: o.queuable,
- monitored: o.monitored,
- broadcast: o.broadcast,
- defaultTargetOnly: o.defaultTargetOnly,
- bubbles: ('bubbles' in o) ? o.bubbles : true
- }
- };
+ self.events = events;
- };
+ if (self.stoppedFn) {
+ events.on('stopped', self.stoppedFn);
+ }
+ self.currentTarget = host;
-ET.prototype = {
+ self.details = args.slice(); // original arguments in the details
- /**
- * Listen to a custom event hosted by this object one time.
- * This is the equivalent to on
except the
- * listener is immediatelly detached when it is executed.
- * @method once
- * @param type {string} The type of the event
- * @param fn {Function} The callback
- * @param context {object} optional execution context.
- * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
- * @return the event target or a detach handle per 'chain' config
- */
- once: function() {
- var handle = this.on.apply(this, arguments);
- handle.batch(function(hand) {
- if (hand.sub) {
- hand.sub.once = true;
- }
- });
- return handle;
- },
+ // self.log("Firing " + self + ", " + "args: " + args);
+ self.log("Firing " + self.type);
- /**
- * Listen to a custom event hosted by this object one time.
- * This is the equivalent to after
except the
- * listener is immediatelly detached when it is executed.
- * @method onceAfter
- * @param type {string} The type of the event
- * @param fn {Function} The callback
- * @param context {object} optional execution context.
- * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
- * @return the event target or a detach handle per 'chain' config
- */
- onceAfter: function() {
- var args = YArray(arguments, 0, true);
- args[0] = AFTER_PREFIX + args[0];
+ self._facade = null; // kill facade to eliminate stale properties
- return this.once.apply(this, args);
- },
+ ef = self._getFacade(args);
- /**
- * Takes the type parameter passed to 'on' and parses out the
- * various pieces that could be included in the type. If the
- * event type is passed without a prefix, it will be expanded
- * to include the prefix one is supplied or the event target
- * is configured with a default prefix.
- * @method parseType
- * @param {string} type the type
- * @param {string} [pre=this._yuievt.config.prefix] the prefix
- * @since 3.3.0
- * @return {Array} an array containing:
- * * the detach category, if supplied,
- * * the prefixed event type,
- * * whether or not this is an after listener,
- * * the supplied event type
- */
- parseType: function(type, pre) {
- return _parseType(type, pre || this._yuievt.config.prefix);
- },
+ if (Y.Lang.isObject(args[0])) {
+ args[0] = ef;
+ } else {
+ args.unshift(ef);
+ }
- /**
- * Subscribe to a custom event hosted by this object
- * @method on
- * @param type {string} The type of the event
- * @param fn {Function} The callback
- * @param context {object} optional execution context.
- * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
- * @return the event target or a detach handle per 'chain' config
- */
- on: function(type, fn, context) {
+ // if (subCount) {
+ if (subs[0]) {
+ // self._procSubs(Y.merge(self.subscribers), args, ef);
+ self._procSubs(subs[0], args, ef);
+ }
- var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
- detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
- Node = Y.Node, n, domevent, isArr;
+ // bubble if this is hosted in an event target and propagation has not been stopped
+ if (self.bubbles && host.bubble && !self.stopped) {
- // full name, args, detachcategory, after
- this._monitor('attach', parts[1], {
- args: arguments,
- category: parts[0],
- after: parts[2]
- });
+ oldbubble = es.bubbling;
- if (L.isObject(type)) {
+ // self.bubbling = true;
+ es.bubbling = self.type;
- if (L.isFunction(type)) {
- return Y.Do.before.apply(Y.Do, arguments);
- }
+ // if (host !== ef.target || es.type != self.type) {
+ if (es.type != self.type) {
+ es.stopped = 0;
+ es.prevented = 0;
+ }
- f = fn;
- c = context;
- args = YArray(arguments, 0, true);
- ret = [];
+ ret = host.bubble(self, args, null, es);
- if (L.isArray(type)) {
- isArr = true;
- }
+ self.stopped = Math.max(self.stopped, es.stopped);
+ self.prevented = Math.max(self.prevented, es.prevented);
- after = type._after;
- delete type._after;
+ // self.bubbling = false;
+ es.bubbling = oldbubble;
- Y.each(type, function(v, k) {
+ }
- if (L.isObject(v)) {
- f = v.fn || ((L.isFunction(v)) ? v : f);
- c = v.context || c;
- }
+ if (self.prevented) {
+ if (self.preventedFn) {
+ self.preventedFn.apply(host, args);
+ }
+ } else if (self.defaultFn &&
+ ((!self.defaultTargetOnly && !es.defaultTargetOnly) ||
+ host === ef.target)) {
+ self.defaultFn.apply(host, args);
+ }
- var nv = (after) ? AFTER_PREFIX : '';
+ // broadcast listeners are fired as discreet events on the
+ // YUI instance and potentially the YUI global.
+ self._broadcast(args);
- args[0] = nv + ((isArr) ? v : k);
- args[1] = f;
- args[2] = c;
+ // Queue the after
+ if (subs[1] && !self.prevented && self.stopped < 2) {
+ if (es.id === self.id || self.type != host._yuievt.bubbling) {
+ self._procSubs(subs[1], args, ef);
+ while ((next = es.afterQueue.last())) {
+ next();
+ }
+ } else {
+ postponed = subs[1];
+ if (es.execDefaultCnt) {
+ postponed = Y.merge(postponed);
+ Y.each(postponed, function(s) {
+ s.postponed = true;
+ });
+ }
- ret.push(this.on.apply(this, args));
+ es.afterQueue.add(function() {
+ self._procSubs(postponed, args, ef);
+ });
+ }
+ }
- }, this);
+ self.target = null;
- return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
+ if (es.id === self.id) {
+ queue = es.queue;
+ while (queue.length) {
+ q = queue.pop();
+ ce = q[0];
+ // set up stack to allow the next item to be processed
+ es.next = ce;
+ ce.fire.apply(ce, q[1]);
}
- detachcategory = parts[0];
- after = parts[2];
- shorttype = parts[3];
+ self.stack = null;
+ }
- // extra redirection so we catch adaptor events too. take a look at this.
- if (Node && Y.instanceOf(this, Node) && (shorttype in Node.DOM_EVENTS)) {
- args = YArray(arguments, 0, true);
- args.splice(2, 0, Node.getDOMNode(this));
- return Y.on.apply(Y, args);
- }
+ ret = !(self.stopped);
- type = parts[1];
+ if (self.type != host._yuievt.bubbling) {
+ es.stopped = 0;
+ es.prevented = 0;
+ self.stopped = 0;
+ self.prevented = 0;
+ }
- if (Y.instanceOf(this, YUI)) {
+ return ret;
+};
- adapt = Y.Env.evt.plugins[type];
- args = YArray(arguments, 0, true);
- args[0] = shorttype;
+CEProto._getFacade = function() {
- if (Node) {
- n = args[2];
+ var ef = this._facade, o, o2,
+ args = this.details;
- if (Y.instanceOf(n, Y.NodeList)) {
- n = Y.NodeList.getDOMNodes(n);
- } else if (Y.instanceOf(n, Node)) {
- n = Node.getDOMNode(n);
- }
+ if (!ef) {
+ ef = new Y.EventFacade(this, this.currentTarget);
+ }
- domevent = (shorttype in Node.DOM_EVENTS);
+ // if the first argument is an object literal, apply the
+ // properties to the event facade
+ o = args && args[0];
- // Captures both DOM events and event plugins.
- if (domevent) {
- args[2] = n;
- }
- }
+ if (Y.Lang.isObject(o, true)) {
- // check for the existance of an event adaptor
- if (adapt) {
- handle = adapt.on.apply(Y, args);
- } else if ((!type) || domevent) {
- handle = Y.Event._attach(args);
- }
+ o2 = {};
- }
+ // protect the event facade properties
+ Y.mix(o2, ef, true, FACADE_KEYS);
- if (!handle) {
- ce = this._yuievt.events[type] || this.publish(type);
- handle = ce._on(fn, context, (arguments.length > 3) ? YArray(arguments, 3, true) : null, (after) ? 'after' : true);
- }
+ // mix the data
+ Y.mix(ef, o, true);
- if (detachcategory) {
- store[detachcategory] = store[detachcategory] || {};
- store[detachcategory][type] = store[detachcategory][type] || [];
- store[detachcategory][type].push(handle);
- }
+ // restore ef
+ Y.mix(ef, o2, true, FACADE_KEYS);
- return (this._yuievt.chain) ? this : handle;
+ // Allow the event type to be faked
+ // http://yuilibrary.com/projects/yui3/ticket/2528376
+ ef.type = o.type || ef.type;
+ }
- },
+ // update the details field with the arguments
+ // ef.type = this.type;
+ ef.details = this.details;
- /**
- * subscribe to an event
- * @method subscribe
- * @deprecated use on
- */
- subscribe: function() {
- return this.on.apply(this, arguments);
- },
+ // use the original target when the event bubbled to this target
+ ef.target = this.originalTarget || this.target;
- /**
- * Detach one or more listeners the from the specified event
- * @method detach
- * @param type {string|Object} Either the handle to the subscriber or the
- * type of event. If the type
- * is not specified, it will attempt to remove
- * the listener from all hosted events.
- * @param fn {Function} The subscribed function to unsubscribe, if not
- * supplied, all subscribers will be removed.
- * @param context {Object} The custom object passed to subscribe. This is
- * optional, but if supplied will be used to
- * disambiguate multiple listeners that are the same
- * (e.g., you subscribe many object using a function
- * that lives on the prototype)
- * @return {EventTarget} the host
- */
- detach: function(type, fn, context) {
- var evts = this._yuievt.events, i,
- Node = Y.Node, isNode = Node && (Y.instanceOf(this, Node));
+ ef.currentTarget = this.currentTarget;
+ ef.stopped = 0;
+ ef.prevented = 0;
- // detachAll disabled on the Y instance.
- if (!type && (this !== Y)) {
- for (i in evts) {
- if (evts.hasOwnProperty(i)) {
- evts[i].detach(fn, context);
- }
- }
- if (isNode) {
- Y.Event.purgeElement(Node.getDOMNode(this));
- }
+ this._facade = ef;
- return this;
+ return this._facade;
+};
+
+/**
+ * Stop propagation to bubble targets
+ * @for CustomEvent
+ * @method stopPropagation
+ */
+CEProto.stopPropagation = function() {
+ this.stopped = 1;
+ if (this.stack) {
+ this.stack.stopped = 1;
+ }
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Stops propagation to bubble targets, and prevents any remaining
+ * subscribers on the current target from executing.
+ * @method stopImmediatePropagation
+ */
+CEProto.stopImmediatePropagation = function() {
+ this.stopped = 2;
+ if (this.stack) {
+ this.stack.stopped = 2;
+ }
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Prevents the execution of this event's defaultFn
+ * @method preventDefault
+ */
+CEProto.preventDefault = function() {
+ if (this.preventable) {
+ this.prevented = 1;
+ if (this.stack) {
+ this.stack.prevented = 1;
}
+ }
+};
- var parts = _parseType(type, this._yuievt.config.prefix),
- detachcategory = L.isArray(parts) ? parts[0] : null,
- shorttype = (parts) ? parts[3] : null,
- adapt, store = Y.Env.evt.handles, detachhost, cat, args,
- ce,
+/**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+CEProto.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ this.preventDefault();
+};
- keyDetacher = function(lcat, ltype, host) {
- var handles = lcat[ltype], ce, i;
- if (handles) {
- for (i = handles.length - 1; i >= 0; --i) {
- ce = handles[i].evt;
- if (ce.host === host || ce.el === host) {
- handles[i].detach();
- }
- }
- }
- };
+/**
+ * Registers another EventTarget as a bubble target. Bubble order
+ * is determined by the order registered. Multiple targets can
+ * be specified.
+ *
+ * Events can only bubble if emitFacade is true.
+ *
+ * Included in the event-custom-complex submodule.
+ *
+ * @method addTarget
+ * @param o {EventTarget} the target to add
+ * @for EventTarget
+ */
+ETProto.addTarget = function(o) {
+ this._yuievt.targets[Y.stamp(o)] = o;
+ this._yuievt.hasTargets = true;
+};
- if (detachcategory) {
+/**
+ * Returns an array of bubble targets for this object.
+ * @method getTargets
+ * @return EventTarget[]
+ */
+ETProto.getTargets = function() {
+ return Y.Object.values(this._yuievt.targets);
+};
- cat = store[detachcategory];
- type = parts[1];
- detachhost = (isNode) ? Y.Node.getDOMNode(this) : this;
+/**
+ * Removes a bubble target
+ * @method removeTarget
+ * @param o {EventTarget} the target to remove
+ * @for EventTarget
+ */
+ETProto.removeTarget = function(o) {
+ delete this._yuievt.targets[Y.stamp(o)];
+};
- if (cat) {
- if (type) {
- keyDetacher(cat, type, detachhost);
- } else {
- for (i in cat) {
- if (cat.hasOwnProperty(i)) {
- keyDetacher(cat, i, detachhost);
- }
- }
+/**
+ * Propagate an event. Requires the event-custom-complex module.
+ * @method bubble
+ * @param evt {CustomEvent} the custom event to propagate
+ * @return {boolean} the aggregated return value from Event.Custom.fire
+ * @for EventTarget
+ */
+ETProto.bubble = function(evt, args, target, es) {
+
+ var targs = this._yuievt.targets, ret = true,
+ t, type = evt && evt.type, ce, i, bc, ce2,
+ originalTarget = target || (evt && evt.target) || this,
+ oldbubble;
+
+ if (!evt || ((!evt.stopped) && targs)) {
+
+ for (i in targs) {
+ if (targs.hasOwnProperty(i)) {
+ t = targs[i];
+ ce = t.getEvent(type, true);
+ ce2 = t.getSibling(type, ce);
+
+ if (ce2 && !ce) {
+ ce = t.publish(type);
}
- return this;
- }
+ oldbubble = t._yuievt.bubbling;
+ t._yuievt.bubbling = type;
- // If this is an event handle, use it to detach
- } else if (L.isObject(type) && type.detach) {
- type.detach();
- return this;
- // extra redirection so we catch adaptor events too. take a look at this.
- } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
- args = YArray(arguments, 0, true);
- args[2] = Node.getDOMNode(this);
- Y.detach.apply(Y, args);
- return this;
- }
+ // if this event was not published on the bubble target,
+ // continue propagating the event.
+ if (!ce) {
+ if (t._yuievt.hasTargets) {
+ t.bubble(evt, args, originalTarget, es);
+ }
+ } else {
- adapt = Y.Env.evt.plugins[shorttype];
+ ce.sibling = ce2;
- // The YUI instance handles DOM events and adaptors
- if (Y.instanceOf(this, YUI)) {
- args = YArray(arguments, 0, true);
- // use the adaptor specific detach code if
- if (adapt && adapt.detach) {
- adapt.detach.apply(Y, args);
- return this;
- // DOM event fork
- } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
- args[0] = type;
- Y.Event.detach.apply(Y.Event, args);
- return this;
+ // set the original target to that the target payload on the
+ // facade is correct.
+ ce.target = originalTarget;
+ ce.originalTarget = originalTarget;
+ ce.currentTarget = t;
+ bc = ce.broadcast;
+ ce.broadcast = false;
+
+ // default publish may not have emitFacade true -- that
+ // shouldn't be what the implementer meant to do
+ ce.emitFacade = true;
+
+ ce.stack = es;
+
+ ret = ret && ce.fire.apply(ce, args || evt.details || []);
+ ce.broadcast = bc;
+ ce.originalTarget = null;
+
+
+ // stopPropagation() was called
+ if (ce.stopped) {
+ break;
+ }
+ }
+
+ t._yuievt.bubbling = oldbubble;
}
}
+ }
- // ce = evts[type];
- ce = evts[parts[1]];
- if (ce) {
- ce.detach(fn, context);
- }
+ return ret;
+};
- return this;
- },
+FACADE = new Y.EventFacade();
+FACADE_KEYS = Y.Object.keys(FACADE);
- /**
- * detach a listener
- * @method unsubscribe
- * @deprecated use detach
- */
- unsubscribe: function() {
- return this.detach.apply(this, arguments);
- },
- /**
- * Removes all listeners from the specified event. If the event type
- * is not specified, all listeners from all hosted custom events will
- * be removed.
- * @method detachAll
- * @param type {string} The type, or name of the event
- */
- detachAll: function(type) {
- return this.detach(type);
- },
- /**
- * Removes all listeners from the specified event. If the event type
- * is not specified, all listeners from all hosted custom events will
- * be removed.
- * @method unsubscribeAll
- * @param type {string} The type, or name of the event
- * @deprecated use detachAll
- */
- unsubscribeAll: function() {
- return this.detachAll.apply(this, arguments);
- },
+}, '3.4.0' ,{requires:['event-custom-base']});
+YUI.add('node-core', function(Y) {
- /**
- * Creates a new custom event of the specified type. If a custom event
- * by that name already exists, it will not be re-created. In either
- * case the custom event is returned.
- *
- * @method publish
- *
- * @param type {string} the type, or name of the event
- * @param opts {object} optional config params. Valid properties are:
- *
- *
- *
- *
- * @return {CustomEvent} the custom event
- *
- */
- publish: function(type, opts) {
- var events, ce, ret, defaults,
- edata = this._yuievt,
- pre = edata.config.prefix;
+/**
+ * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
+ * @module node
+ * @submodule node-core
+ */
- type = (pre) ? _getType(type, pre) : type;
+/**
+ * The Node class provides a wrapper for manipulating DOM Nodes.
+ * Node properties can be accessed via the set/get methods.
+ * Use `Y.one()` to retrieve Node instances.
+ *
+ * NOTE: Node properties are accessed using
+ * the set
and get
methods.
+ *
+ * @class Node
+ * @constructor
+ * @param {DOMNode} node the DOM node to be mapped to the Node instance.
+ * @uses EventTarget
+ */
- this._monitor('publish', type, {
- args: arguments
- });
+// "globals"
+var DOT = '.',
+ NODE_NAME = 'nodeName',
+ NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TAG_NAME = 'tagName',
+ UID = '_yuid',
+ EMPTY_OBJ = {},
- if (L.isObject(type)) {
- ret = {};
- Y.each(type, function(v, k) {
- ret[k] = this.publish(k, v || opts);
- }, this);
+ _slice = Array.prototype.slice,
- return ret;
- }
+ Y_DOM = Y.DOM,
- events = edata.events;
- ce = events[type];
+ Y_Node = function(node) {
+ if (!this.getDOMNode) { // support optional "new"
+ return new Y_Node(node);
+ }
- if (ce) {
-// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
- if (opts) {
- ce.applyConfig(opts, true);
+ if (typeof node == 'string') {
+ node = Y_Node._fromString(node);
+ if (!node) {
+ return null; // NOTE: return
}
- } else {
+ }
- defaults = edata.defaults;
+ var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID];
- // apply defaults
- ce = new Y.CustomEvent(type,
- (opts) ? Y.merge(defaults, opts) : defaults);
- events[type] = ce;
+ if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) {
+ node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
}
- // make sure we turn the broadcast flag off if this
- // event was published as a result of bubbling
- // if (opts instanceof Y.CustomEvent) {
- // events[type].broadcast = false;
- // }
+ uid = uid || Y.stamp(node);
+ if (!uid) { // stamp failed; likely IE non-HTMLElement
+ uid = Y.guid();
+ }
- return events[type];
+ this[UID] = uid;
+
+ /**
+ * The underlying DOM node bound to the Y.Node instance
+ * @property _node
+ * @private
+ */
+ this._node = node;
+
+ this._stateProxy = node; // when augmented with Attribute
+
+ if (this._initPlugins) { // when augmented with Plugin.Host
+ this._initPlugins();
+ }
},
- /**
- * This is the entry point for the event monitoring system.
- * You can monitor 'attach', 'detach', 'fire', and 'publish'.
- * When configured, these events generate an event. click ->
- * click_attach, click_detach, click_publish -- these can
- * be subscribed to like other events to monitor the event
- * system. Inividual published events can have monitoring
- * turned on or off (publish can't be turned off before it
- * it published) by setting the events 'monitor' config.
- *
- * @private
- */
- _monitor: function(what, type, o) {
- var monitorevt, ce = this.getEvent(type);
- if ((this._yuievt.config.monitored && (!ce || ce.monitored)) || (ce && ce.monitored)) {
- monitorevt = type + '_' + what;
- o.monitored = what;
- this.fire.call(this, monitorevt, o);
+ // used with previous/next/ancestor tests
+ _wrapFn = function(fn) {
+ var ret = null;
+ if (fn) {
+ ret = (typeof fn == 'string') ?
+ function(n) {
+ return Y.Selector.test(n, fn);
+ } :
+ function(n) {
+ return fn(Y.one(n));
+ };
}
- },
- /**
- * Fire a custom event by name. The callback functions will be executed
- * from the context specified when the event was created, and with the
- * following parameters.
- *
- * If the custom event object hasn't been created, then the event hasn't
- * been published and it has no subscribers. For performance sake, we
- * immediate exit in this case. This means the event won't bubble, so
- * if the intention is that a bubble target be notified, the event must
- * be published on this object first.
- *
- * The first argument is the event type, and any additional arguments are
- * passed to the listeners as parameters. If the first of these is an
- * object literal, and the event is configured to emit an event facade,
- * that object is mixed into the event facade and the facade is provided
- * in place of the original object.
- *
- * @method fire
- * @param type {String|Object} The type of the event, or an object that contains
- * a 'type' property.
- * @param arguments {Object*} an arbitrary set of parameters to pass to
- * the handler. If the first of these is an object literal and the event is
- * configured to emit an event facade, the event facade will replace that
- * parameter after the properties the object literal contains are copied to
- * the event facade.
- * @return {EventTarget} the event host
- *
- */
- fire: function(type) {
+ return ret;
+ };
+// end "globals"
- var typeIncluded = L.isString(type),
- t = (typeIncluded) ? type : (type && type.type),
- ce, ret, pre = this._yuievt.config.prefix, ce2,
- args = (typeIncluded) ? YArray(arguments, 1, true) : arguments;
+Y_Node.ATTRS = {};
+Y_Node.DOM_EVENTS = {};
- t = (pre) ? _getType(t, pre) : t;
+Y_Node._fromString = function(node) {
+ if (node) {
+ if (node.indexOf('doc') === 0) { // doc OR document
+ node = Y.config.doc;
+ } else if (node.indexOf('win') === 0) { // win OR window
+ node = Y.config.win;
+ } else {
+ node = Y.Selector.query(node, null, true);
+ }
+ }
- this._monitor('fire', t, {
- args: args
- });
+ return node || null;
+};
- ce = this.getEvent(t, true);
- ce2 = this.getSibling(t, ce);
+/**
+ * The name of the component
+ * @static
+ * @property NAME
+ */
+Y_Node.NAME = 'node';
- if (ce2 && !ce) {
- ce = this.publish(t);
- }
+/*
+ * The pattern used to identify ARIA attributes
+ */
+Y_Node.re_aria = /^(?:role$|aria-)/;
- // this event has not been published or subscribed to
- if (!ce) {
- if (this._yuievt.hasTargets) {
- return this.bubble({ type: t }, args, this);
- }
+Y_Node.SHOW_TRANSITION = 'fadeIn';
+Y_Node.HIDE_TRANSITION = 'fadeOut';
- // otherwise there is nothing to be done
- ret = true;
- } else {
- ce.sibling = ce2;
- ret = ce.fire.apply(ce, args);
- }
+/**
+ * A list of Node instances that have been created
+ * @private
+ * @property _instances
+ * @static
+ *
+ */
+Y_Node._instances = {};
- return (this._yuievt.chain) ? this : ret;
- },
+/**
+ * Retrieves the DOM node bound to a Node instance
+ * @method getDOMNode
+ * @static
+ *
+ * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
+ * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
+ * as the node argument, it is simply returned.
+ */
+Y_Node.getDOMNode = function(node) {
+ if (node) {
+ return (node.nodeType) ? node : node._node || null;
+ }
+ return null;
+};
- getSibling: function(type, ce) {
- var ce2;
- // delegate to *:type events if there are subscribers
- if (type.indexOf(PREFIX_DELIMITER) > -1) {
- type = _wildType(type);
- // console.log(type);
- ce2 = this.getEvent(type, true);
- if (ce2) {
- // console.log("GOT ONE: " + type);
- ce2.applyConfig(ce);
- ce2.bubbles = false;
- ce2.broadcast = 0;
- // ret = ce2.fire.apply(ce2, a);
+/**
+ * Checks Node return values and wraps DOM Nodes as Y.Node instances
+ * and DOM Collections / Arrays as Y.NodeList instances.
+ * Other return values just pass thru. If undefined is returned (e.g. no return)
+ * then the Node instance is returned for chainability.
+ * @method scrubVal
+ * @static
+ *
+ * @param {any} node The Node instance or an HTMLNode
+ * @return {Y.Node | Y.NodeList | any} Depends on what is returned from the DOM node.
+ */
+Y_Node.scrubVal = function(val, node) {
+ if (val) { // only truthy values are risky
+ if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function
+ if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window
+ val = Y.one(val);
+ } else if ((val.item && !val._nodes) || // dom collection or Node instance
+ (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
+ val = Y.all(val);
}
}
+ } else if (typeof val === 'undefined') {
+ val = node; // for chaining
+ } else if (val === null) {
+ val = null; // IE: DOM null not the same as null
+ }
- return ce2;
- },
+ return val;
+};
- /**
- * Returns the custom event of the provided type has been created, a
- * falsy value otherwise
- * @method getEvent
- * @param type {string} the type, or name of the event
- * @param prefixed {string} if true, the type is prefixed already
- * @return {CustomEvent} the custom event or null
- */
- getEvent: function(type, prefixed) {
- var pre, e;
- if (!prefixed) {
- pre = this._yuievt.config.prefix;
- type = (pre) ? _getType(type, pre) : type;
- }
- e = this._yuievt.events;
- return e[type] || null;
- },
+/**
+ * Adds methods to the Y.Node prototype, routing through scrubVal.
+ * @method addMethod
+ * @static
+ *
+ * @param {String} name The name of the method to add
+ * @param {Function} fn The function that becomes the method
+ * @param {Object} context An optional context to call the method with
+ * (defaults to the Node instance)
+ * @return {any} Depends on what is returned from the DOM node.
+ */
+Y_Node.addMethod = function(name, fn, context) {
+ if (name && fn && typeof fn == 'function') {
+ Y_Node.prototype[name] = function() {
+ var args = _slice.call(arguments),
+ node = this,
+ ret;
- /**
- * Subscribe to a custom event hosted by this object. The
- * supplied callback will execute after any listeners add
- * via the subscribe method, and after the default function,
- * if configured for the event, has executed.
- * @method after
- * @param type {string} The type of the event
- * @param fn {Function} The callback
- * @param context {object} optional execution context.
- * @param arg* {mixed} 0..n additional arguments to supply to the subscriber
- * @return the event target or a detach handle per 'chain' config
- */
- after: function(type, fn) {
+ if (args[0] && Y.instanceOf(args[0], Y_Node)) {
+ args[0] = args[0]._node;
+ }
- var a = YArray(arguments, 0, true);
+ if (args[1] && Y.instanceOf(args[1], Y_Node)) {
+ args[1] = args[1]._node;
+ }
+ args.unshift(node._node);
- switch (L.type(type)) {
- case 'function':
- return Y.Do.after.apply(Y.Do, arguments);
- case 'array':
- // YArray.each(a[0], function(v) {
- // v = AFTER_PREFIX + v;
- // });
- // break;
- case 'object':
- a[0]._after = true;
- break;
- default:
- a[0] = AFTER_PREFIX + type;
- }
+ ret = fn.apply(node, args);
- return this.on.apply(this, a);
+ if (ret) { // scrub truthy
+ ret = Y_Node.scrubVal(ret, node);
+ }
- },
+ (typeof ret != 'undefined') || (ret = node);
+ return ret;
+ };
+ } else {
+ }
+};
- /**
- * Executes the callback before a DOM event, custom event
- * or method. If the first argument is a function, it
- * is assumed the target is a method. For DOM and custom
- * events, this is an alias for Y.on.
- *
- * For DOM and custom events:
- * type, callback, context, 0-n arguments
- *
- * For methods:
- * callback, object (method host), methodName, context, 0-n arguments
- *
- * @method before
- * @return detach handle
- */
- before: function() {
- return this.on.apply(this, arguments);
+/**
+ * Imports utility methods to be added as Y.Node methods.
+ * @method importMethod
+ * @static
+ *
+ * @param {Object} host The object that contains the method to import.
+ * @param {String} name The name of the method to import
+ * @param {String} altName An optional name to use in place of the host name
+ * @param {Object} context An optional context to call the method with
+ */
+Y_Node.importMethod = function(host, name, altName) {
+ if (typeof name == 'string') {
+ altName = altName || name;
+ Y_Node.addMethod(altName, host[name], host);
+ } else {
+ Y.Array.each(name, function(n) {
+ Y_Node.importMethod(host, n);
+ });
}
-
};
-Y.EventTarget = ET;
-
-// make Y an event target
-Y.mix(Y, ET.prototype);
-ET.call(Y, { bubbles: false });
-
-YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
-
/**
- * Hosts YUI page level events. This is where events bubble to
- * when the broadcast config is set to 2. This property is
- * only available if the custom event module is loaded.
- * @property Global
- * @type EventTarget
+ * Retrieves a NodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
* @for YUI
*/
-Y.Global = YUI.Env.globalEvents;
-
-// @TODO implement a global namespace function on Y.Global?
/**
- * YUI
's on
method is a unified interface for subscribing to
- * most events exposed by YUI. This includes custom events, DOM events, and
- * function events. detach
is also provided to remove listeners
- * serviced by this function.
- *
- * The signature that on
accepts varies depending on the type
- * of event being consumed. Refer to the specific methods that will
- * service a specific request for additional information about subscribing
- * to that type of event.
- *
- *
- *
- *
- * EventTarget
's on
method.
- *
- *
- * Example:
- * Y.on('drag:drophit', function() { // start work });
- * Event
's
- * attach
method.
- *
- *
- * Example:
- * Y.on('click', function(e) { // something was clicked }, '#someelement');
- * Event.Do
's
- * before
method.
- *
- *
- * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');
- * on
corresponds to the moment before any default behavior of
- * the event. after
works the same way, but these listeners
- * execute after the event's default behavior. before
is an
- * alias for on
.
- *
- * @method on
- * @param type event type (this parameter does not apply for function events)
- * @param fn the callback
- * @param context optionally change the value of 'this' in the callback
- * @param args* 0..n additional arguments to pass to the callback.
- * @return the event target or a detach handle per 'chain' config
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector. Returns null if no match found.
+ * Note: For chaining purposes you may want to
+ * use Y.all
, which returns a NodeList when no match is found.
+ * @method one
+ * @param {String | HTMLElement} node a node or Selector
+ * @return {Y.Node | null} a Node instance or null if no match found.
* @for YUI
*/
- /**
- * Listen for an event one time. Equivalent to on
, except that
- * the listener is immediately detached when executed.
- * @see on
- * @method once
- * @param type event type (this parameter does not apply for function events)
- * @param fn the callback
- * @param context optionally change the value of 'this' in the callback
- * @param args* 0..n additional arguments to pass to the callback.
- * @return the event target or a detach handle per 'chain' config
- * @for YUI
- */
-
/**
- * after() is a unified interface for subscribing to
- * most events exposed by YUI. This includes custom events,
- * DOM events, and AOP events. This works the same way as
- * the on() function, only it operates after any default
- * behavior for the event has executed. @see on
for more
- * information.
- * @method after
- * @param type event type (this parameter does not apply for function events)
- * @param fn the callback
- * @param context optionally change the value of 'this' in the callback
- * @param args* 0..n additional arguments to pass to the callback.
- * @return the event target or a detach handle per 'chain' config
- * @for YUI
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector. Returns null if no match found.
+ * Note: For chaining purposes you may want to
+ * use Y.all
, which returns a NodeList when no match is found.
+ * @method one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @return {Y.Node | null} a Node instance or null if no match found.
+ * @for Node
*/
+Y_Node.one = function(node) {
+ var instance = null,
+ cachedNode,
+ uid;
+ if (node) {
+ if (typeof node == 'string') {
+ node = Y_Node._fromString(node);
+ if (!node) {
+ return null; // NOTE: return
+ }
+ } else if (node.getDOMNode) {
+ return node; // NOTE: return
+ }
-}, '3.4.0' ,{requires:['oop']});
-(function () {
-var GLOBAL_ENV = YUI.Env;
-
-if (!GLOBAL_ENV._ready) {
- GLOBAL_ENV._ready = function() {
- GLOBAL_ENV.DOMReady = true;
- GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
- };
-
- GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
-}
-})();
-YUI.add('event-base', function(Y) {
+ if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
+ uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
+ instance = Y_Node._instances[uid]; // reuse exising instances
+ cachedNode = instance ? instance._node : null;
+ if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
+ instance = new Y_Node(node);
+ if (node.nodeType != 11) { // dont cache document fragment
+ Y_Node._instances[instance[UID]] = instance; // cache node
+ }
+ }
+ }
+ }
-/*
- * DOM event listener abstraction layer
- * @module event
- * @submodule event-base
- */
+ return instance;
+};
/**
- * The domready event fires at the moment the browser's DOM is
- * usable. In most cases, this is before images are fully
- * downloaded, allowing you to provide a more responsive user
- * interface.
- *
- * In YUI 3, domready subscribers will be notified immediately if
- * that moment has already passed when the subscription is created.
- *
- * One exception is if the yui.js file is dynamically injected into
- * the page. If this is done, you must tell the YUI instance that
- * you did this in order for DOMReady (and window load events) to
- * fire normally. That configuration option is 'injected' -- set
- * it to true if the yui.js script is not included inline.
- *
- * This method is part of the 'event-ready' module, which is a
- * submodule of 'event'.
- *
- * @event domready
- * @for YUI
+ * The default setter for DOM properties
+ * Called with instance context (this === the Node instance)
+ * @method DEFAULT_SETTER
+ * @static
+ * @param {String} name The attribute/property being set
+ * @param {any} val The value to be set
+ * @return {any} The value
*/
-Y.publish('domready', {
- fireOnce: true,
- async: true
-});
+Y_Node.DEFAULT_SETTER = function(name, val) {
+ var node = this._stateProxy,
+ strPath;
-if (YUI.Env.DOMReady) {
- Y.fire('domready');
-} else {
- Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready');
-}
+ if (name.indexOf(DOT) > -1) {
+ strPath = name;
+ name = name.split(DOT);
+ // only allow when defined on node
+ Y.Object.setValue(node, name, val);
+ } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
+ node[name] = val;
+ }
+
+ return val;
+};
/**
- * Custom event engine, DOM event listener abstraction layer, synthetic DOM
- * events.
- * @module event
- * @submodule event-base
+ * The default getter for DOM properties
+ * Called with instance context (this === the Node instance)
+ * @method DEFAULT_GETTER
+ * @static
+ * @param {String} name The attribute/property to look up
+ * @return {any} The current value
*/
+Y_Node.DEFAULT_GETTER = function(name) {
+ var node = this._stateProxy,
+ val;
-/**
- * Wraps a DOM event, properties requiring browser abstraction are
- * fixed here. Provids a security layer when required.
- * @class DOMEventFacade
- * @param ev {Event} the DOM event
- * @param currentTarget {HTMLElement} the element the listener was attached to
- * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
- */
-
- var ua = Y.UA,
-
- EMPTY = {},
+ if (name.indexOf && name.indexOf(DOT) > -1) {
+ val = Y.Object.getValue(node, name.split(DOT));
+ } else if (typeof node[name] != 'undefined') { // pass thru from DOM
+ val = node[name];
+ }
- /**
- * webkit key remapping required for Safari < 3.1
- * @property webkitKeymap
- * @private
- */
- webkitKeymap = {
- 63232: 38, // up
- 63233: 40, // down
- 63234: 37, // left
- 63235: 39, // right
- 63276: 33, // page up
- 63277: 34, // page down
- 25: 9, // SHIFT-TAB (Safari provides a different key code in
- // this case, even though the shiftKey modifier is set)
- 63272: 46, // delete
- 63273: 36, // home
- 63275: 35 // end
- },
+ return val;
+};
+Y.mix(Y_Node.prototype, {
/**
- * Returns a wrapped node. Intended to be used on event targets,
- * so it will return the node's parent if the target is a text
- * node.
- *
- * If accessing a property of the node throws an error, this is
- * probably the anonymous div wrapper Gecko adds inside text
- * nodes. This likely will only occur when attempting to access
- * the relatedTarget. In this case, we now return null because
- * the anonymous div is completely useless and we do not know
- * what the related target was because we can't even get to
- * the element's parent node.
- *
- * @method resolve
- * @private
+ * The method called when outputting Node instances as strings
+ * @method toString
+ * @return {String} A string representation of the Node instance
*/
- resolve = function(n) {
- if (!n) {
- return n;
- }
- try {
- if (n && 3 == n.nodeType) {
- n = n.parentNode;
- }
- } catch(e) {
- return null;
- }
-
- return Y.one(n);
- },
-
- DOMEventFacade = function(ev, currentTarget, wrapper) {
- this._event = ev;
- this._currentTarget = currentTarget;
- this._wrapper = wrapper || EMPTY;
-
- // if not lazy init
- this.init();
- };
-
-Y.extend(DOMEventFacade, Object, {
-
- init: function() {
-
- var e = this._event,
- overrides = this._wrapper.overrides,
- x = e.pageX,
- y = e.pageY,
- c,
- currentTarget = this._currentTarget;
+ toString: function() {
+ var str = this[UID] + ': not bound to a node',
+ node = this._node,
+ attrs, id, className;
- this.altKey = e.altKey;
- this.ctrlKey = e.ctrlKey;
- this.metaKey = e.metaKey;
- this.shiftKey = e.shiftKey;
- this.type = (overrides && overrides.type) || e.type;
- this.clientX = e.clientX;
- this.clientY = e.clientY;
+ if (node) {
+ attrs = node.attributes;
+ id = (attrs && attrs.id) ? node.getAttribute('id') : null;
+ className = (attrs && attrs.className) ? node.getAttribute('className') : null;
+ str = node[NODE_NAME];
- this.pageX = x;
- this.pageY = y;
+ if (id) {
+ str += '#' + id;
+ }
- c = e.keyCode || e.charCode;
+ if (className) {
+ str += '.' + className.replace(' ', '.');
+ }
- if (ua.webkit && (c in webkitKeymap)) {
- c = webkitKeymap[c];
+ // TODO: add yuid?
+ str += ' ' + this[UID];
}
+ return str;
+ },
- this.keyCode = c;
- this.charCode = c;
- this.which = e.which || e.charCode || c;
- // this.button = e.button;
- this.button = this.which;
-
- this.target = resolve(e.target);
- this.currentTarget = resolve(currentTarget);
- this.relatedTarget = resolve(e.relatedTarget);
+ /**
+ * Returns an attribute value on the Node instance.
+ * Unless pre-configured (via `Node.ATTRS`), get hands
+ * off to the underlying DOM node. Only valid
+ * attributes/properties for the node will be queried.
+ * @method get
+ * @param {String} attr The attribute
+ * @return {any} The current value of the attribute
+ */
+ get: function(attr) {
+ var val;
- if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
- this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
+ if (this._getAttr) { // use Attribute imple
+ val = this._getAttr(attr);
+ } else {
+ val = this._get(attr);
}
- if (this._touch) {
- this._touch(e, currentTarget, this._wrapper);
+ if (val) {
+ val = Y_Node.scrubVal(val, this);
+ } else if (val === null) {
+ val = null; // IE: DOM null is not true null (even though they ===)
}
+ return val;
},
- stopPropagation: function() {
- this._event.stopPropagation();
- this._wrapper.stopped = 1;
- this.stopped = 1;
- },
+ /**
+ * Helper method for get.
+ * @method _get
+ * @private
+ * @param {String} attr The attribute
+ * @return {any} The current value of the attribute
+ */
+ _get: function(attr) {
+ var attrConfig = Y_Node.ATTRS[attr],
+ val;
- stopImmediatePropagation: function() {
- var e = this._event;
- if (e.stopImmediatePropagation) {
- e.stopImmediatePropagation();
+ if (attrConfig && attrConfig.getter) {
+ val = attrConfig.getter.call(this);
+ } else if (Y_Node.re_aria.test(attr)) {
+ val = this._node.getAttribute(attr, 2);
} else {
- this.stopPropagation();
+ val = Y_Node.DEFAULT_GETTER.apply(this, arguments);
}
- this._wrapper.stopped = 2;
- this.stopped = 2;
- },
- preventDefault: function(returnValue) {
- var e = this._event;
- e.preventDefault();
- e.returnValue = returnValue || false;
- this._wrapper.prevented = 1;
- this.prevented = 1;
+ return val;
},
- halt: function(immediate) {
- if (immediate) {
- this.stopImmediatePropagation();
- } else {
- this.stopPropagation();
- }
-
- this.preventDefault();
- }
-
-});
-
-DOMEventFacade.resolve = resolve;
-Y.DOM2EventFacade = DOMEventFacade;
-Y.DOMEventFacade = DOMEventFacade;
-
/**
- * The native event
- * @property _event
+ * Sets an attribute on the Node instance.
+ * Unless pre-configured (via Node.ATTRS), set hands
+ * off to the underlying DOM node. Only valid
+ * attributes/properties for the node will be set.
+ * To set custom attributes use setAttribute.
+ * @method set
+ * @param {String} attr The attribute to be set.
+ * @param {any} val The value to set the attribute to.
+ * @chainable
*/
+ set: function(attr, val) {
+ var attrConfig = Y_Node.ATTRS[attr];
- /**
- * The X location of the event on the page (including scroll)
- * @property pageX
- * @type int
- */
+ if (this._setAttr) { // use Attribute imple
+ this._setAttr.apply(this, arguments);
+ } else { // use setters inline
+ if (attrConfig && attrConfig.setter) {
+ attrConfig.setter.call(this, val, attr);
+ } else if (Y_Node.re_aria.test(attr)) { // special case Aria
+ this._node.setAttribute(attr, val);
+ } else {
+ Y_Node.DEFAULT_SETTER.apply(this, arguments);
+ }
+ }
- /**
- * The Y location of the event on the page (including scroll)
- * @property pageY
- * @type int
- */
+ return this;
+ },
/**
- * The keyCode for key events. Uses charCode if keyCode is not available
- * @property keyCode
- * @type int
+ * Sets multiple attributes.
+ * @method setAttrs
+ * @param {Object} attrMap an object of name/value pairs to set
+ * @chainable
*/
+ setAttrs: function(attrMap) {
+ if (this._setAttrs) { // use Attribute imple
+ this._setAttrs(attrMap);
+ } else { // use setters inline
+ Y.Object.each(attrMap, function(v, n) {
+ this.set(n, v);
+ }, this);
+ }
+
+ return this;
+ },
/**
- * The charCode for key events. Same as keyCode
- * @property charCode
- * @type int
+ * Returns an object containing the values for the requested attributes.
+ * @method getAttrs
+ * @param {Array} attrs an array of attributes to get values
+ * @return {Object} An object with attribute name/value pairs.
*/
+ getAttrs: function(attrs) {
+ var ret = {};
+ if (this._getAttrs) { // use Attribute imple
+ this._getAttrs(attrs);
+ } else { // use setters inline
+ Y.Array.each(attrs, function(v, n) {
+ ret[v] = this.get(v);
+ }, this);
+ }
- /**
- * The button that was pushed.
- * @property button
- * @type int
- */
+ return ret;
+ },
/**
- * The button that was pushed. Same as button.
- * @property which
- * @type int
+ * Compares nodes to determine if they match.
+ * Node instances can be compared to each other and/or HTMLElements.
+ * @method compareTo
+ * @param {HTMLElement | Node} refNode The reference node to compare to the node.
+ * @return {Boolean} True if the nodes match, false if they do not.
*/
+ compareTo: function(refNode) {
+ var node = this._node;
+
+ if (Y.instanceOf(refNode, Y_Node)) {
+ refNode = refNode._node;
+ }
+ return node === refNode;
+ },
/**
- * Node reference for the targeted element
- * @propery target
- * @type Node
+ * Determines whether the node is appended to the document.
+ * @method inDoc
+ * @param {Node|HTMLElement} doc optional An optional document to check against.
+ * Defaults to current document.
+ * @return {Boolean} Whether or not this node is appended to the document.
*/
+ inDoc: function(doc) {
+ var node = this._node;
+ doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
+ if (doc.documentElement) {
+ return Y_DOM.contains(doc.documentElement, node);
+ }
+ },
- /**
- * Node reference for the element that the listener was attached to.
- * @propery currentTarget
- * @type Node
+ getById: function(id) {
+ var node = this._node,
+ ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
+ if (ret && Y_DOM.contains(node, ret)) {
+ ret = Y.one(ret);
+ } else {
+ ret = null;
+ }
+ return ret;
+ },
+
+ /**
+ * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+ * @method ancestor
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * @param {Boolean} testSelf optional Whether or not to include the element in the scan
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} The matching Node instance or null if not found
*/
+ ancestor: function(fn, testSelf) {
+ return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf));
+ },
- /**
- * Node reference to the relatedTarget
- * @propery relatedTarget
- * @type Node
+ /**
+ * Returns the ancestors that pass the test applied by supplied boolean method.
+ * @method ancestors
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * @param {Boolean} testSelf optional Whether or not to include the element in the scan
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {NodeList} A NodeList instance containing the matching elements
*/
+ ancestors: function(fn, testSelf) {
+ return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf));
+ },
/**
- * Number representing the direction and velocity of the movement of the mousewheel.
- * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
- * @property wheelDelta
- * @type int
+ * Returns the previous matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method previous
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
*/
+ previous: function(fn, all) {
+ return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
+ },
/**
- * Stops the propagation to the next bubble target
- * @method stopPropagation
+ * Returns the next matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method next
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
*/
+ next: function(fn, all) {
+ return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
+ },
/**
- * Stops the propagation to the next bubble target and
- * prevents any additional listeners from being exectued
- * on the current target.
- * @method stopImmediatePropagation
+ * Returns all matching siblings.
+ * Returns all siblings if no method provided.
+ * @method siblings
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {NodeList} NodeList instance bound to found siblings
*/
+ siblings: function(fn) {
+ return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
+ },
/**
- * Prevents the event's default behavior
- * @method preventDefault
- * @param returnValue {string} sets the returnValue of the event to this value
- * (rather than the default false value). This can be used to add a customized
- * confirmation query to the beforeunload event).
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method one
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
*/
+ one: function(selector) {
+ return Y.one(Y.Selector.query(selector, this._node, true));
+ },
/**
- * Stops the event propagation and prevents the default
- * event behavior.
- * @method halt
- * @param immediate {boolean} if true additional listeners
- * on the current target will not be executed
+ * Retrieves a NodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
*/
-(function() {
-/**
- * DOM event listener abstraction layer
- * @module event
- * @submodule event-base
- */
-
-/**
- * The event utility provides functions to add and remove event listeners,
- * event cleansing. It also tries to automatically remove listeners it
- * registers during the unload event.
- *
- * @class Event
- * @static
- */
-
-Y.Env.evt.dom_wrappers = {};
-Y.Env.evt.dom_map = {};
-
-var _eventenv = Y.Env.evt,
- config = Y.config,
- win = config.win,
- add = YUI.Env.add,
- remove = YUI.Env.remove,
-
- onLoad = function() {
- YUI.Env.windowLoaded = true;
- Y.Event._load();
- remove(win, "load", onLoad);
+ all: function(selector) {
+ var nodelist = Y.all(Y.Selector.query(selector, this._node));
+ nodelist._query = selector;
+ nodelist._queryRoot = this._node;
+ return nodelist;
},
- onUnload = function() {
- Y.Event._unload();
+ // TODO: allow fn test
+ /**
+ * Test if the supplied node matches the supplied selector.
+ * @method test
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {boolean} Whether or not the node matches the selector.
+ */
+ test: function(selector) {
+ return Y.Selector.test(this._node, selector);
},
- EVENT_READY = 'domready',
+ /**
+ * Removes the node from its parent.
+ * Shortcut for myNode.get('parentNode').removeChild(myNode);
+ * @method remove
+ * @param {Boolean} destroy whether or not to call destroy() on the node
+ * after removal.
+ * @chainable
+ *
+ */
+ remove: function(destroy) {
+ var node = this._node;
- COMPAT_ARG = '~yui|2|compat~',
+ if (node && node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
- shouldIterate = function(o) {
- try {
- return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) &&
- !o.tagName && !o.alert);
- } catch(ex) {
- return false;
+ if (destroy) {
+ this.destroy();
}
+ return this;
},
- // aliases to support DOM event subscription clean up when the last
- // subscriber is detached. deleteAndClean overrides the DOM event's wrapper
- // CustomEvent _delete method.
- _ceProtoDelete = Y.CustomEvent.prototype._delete,
- _deleteAndClean = function(s) {
- var ret = _ceProtoDelete.apply(this, arguments);
-
- if (!this.subCount && !this.afterCount) {
- Y.Event._clean(this);
+ /**
+ * Replace the node with the other node. This is a DOM update only
+ * and does not change the node bound to the Node instance.
+ * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
+ * @method replace
+ * @param {Y.Node || HTMLNode} newNode Node to be inserted
+ * @chainable
+ *
+ */
+ replace: function(newNode) {
+ var node = this._node;
+ if (typeof newNode == 'string') {
+ newNode = Y_Node.create(newNode);
}
-
- return ret;
+ node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
+ return this;
},
-Event = function() {
-
/**
- * True after the onload event has fired
- * @property _loadComplete
- * @type boolean
- * @static
- * @private
- */
- var _loadComplete = false,
-
- /**
- * The number of times to poll after window.onload. This number is
- * increased if additional late-bound handlers are requested after
- * the page load.
- * @property _retryCount
- * @static
- * @private
+ * @method replaceChild
+ * @for Node
+ * @param {String | HTMLElement | Node} node Node to be inserted
+ * @param {HTMLElement | Node} refNode Node to be replaced
+ * @return {Node} The replaced node
*/
- _retryCount = 0,
+ replaceChild: function(node, refNode) {
+ if (typeof node == 'string') {
+ node = Y_DOM.create(node);
+ }
- /**
- * onAvailable listeners
- * @property _avail
- * @static
- * @private
- */
- _avail = [],
+ return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
+ },
/**
- * Custom event wrappers for DOM events. Key is
- * 'event:' + Element uid stamp + event type
- * @property _wrappers
- * @type Y.Event.Custom
- * @static
- * @private
+ * Nulls internal node references, removes any plugins and event listeners
+ * @method destroy
+ * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
+ * node's subtree (default is false)
+ *
*/
- _wrappers = _eventenv.dom_wrappers,
+ destroy: function(recursive) {
+ var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid',
+ instance;
- _windowLoadKey = null,
+ this.purge(); // TODO: only remove events add via this Node
- /**
- * Custom event wrapper map DOM events. Key is
- * Element uid stamp. Each item is a hash of custom event
- * wrappers as provided in the _wrappers collection. This
- * provides the infrastructure for getListeners.
- * @property _el_events
- * @static
- * @private
- */
- _el_events = _eventenv.dom_map;
+ if (this.unplug) { // may not be a PluginHost
+ this.unplug();
+ }
- return {
+ this.clearData();
- /**
- * The number of times we should look for elements that are not
- * in the DOM at the time the event is requested after the document
- * has been loaded. The default is 1000@amp;40 ms, so it will poll
- * for 40 seconds or until all outstanding handlers are bound
- * (whichever comes first).
- * @property POLL_RETRYS
- * @type int
- * @static
- * @final
- */
- POLL_RETRYS: 1000,
+ if (recursive) {
+ Y.NodeList.each(this.all('*'), function(node) {
+ instance = Y_Node._instances[node[UID]];
+ if (instance) {
+ instance.destroy();
+ }
+ });
+ }
- /**
- * The poll interval in milliseconds
- * @property POLL_INTERVAL
- * @type int
- * @static
- * @final
- */
- POLL_INTERVAL: 40,
+ this._node = null;
+ this._stateProxy = null;
- /**
- * addListener/removeListener can throw errors in unexpected scenarios.
- * These errors are suppressed, the method returns false, and this property
- * is set
- * @property lastError
- * @static
- * @type Error
- */
- lastError: null,
+ delete Y_Node._instances[this._yuid];
+ },
+ /**
+ * Invokes a method on the Node instance
+ * @method invoke
+ * @param {String} method The name of the method to invoke
+ * @param {Any} a, b, c, etc. Arguments to invoke the method with.
+ * @return Whatever the underly method returns.
+ * DOM Nodes and Collections return values
+ * are converted to Node/NodeList instances.
+ *
+ */
+ invoke: function(method, a, b, c, d, e) {
+ var node = this._node,
+ ret;
- /**
- * poll handle
- * @property _interval
- * @static
- * @private
- */
- _interval: null,
+ if (a && Y.instanceOf(a, Y_Node)) {
+ a = a._node;
+ }
- /**
- * document readystate poll handle
- * @property _dri
- * @static
- * @private
- */
- _dri: null,
+ if (b && Y.instanceOf(b, Y_Node)) {
+ b = b._node;
+ }
- /**
- * True when the document is initially usable
- * @property DOMReady
- * @type boolean
- * @static
- */
- DOMReady: false,
+ ret = node[method](a, b, c, d, e);
+ return Y_Node.scrubVal(ret, this);
+ },
- /**
- * @method startInterval
- * @static
- * @private
- */
- startInterval: function() {
- if (!Event._interval) {
-Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL);
+ /**
+ * @method swap
+ * @description Swap DOM locations with the given node.
+ * This does not change which DOM node each Node instance refers to.
+ * @param {Node} otherNode The node to swap with
+ * @chainable
+ */
+ swap: Y.config.doc.documentElement.swapNode ?
+ function(otherNode) {
+ this._node.swapNode(Y_Node.getDOMNode(otherNode));
+ } :
+ function(otherNode) {
+ otherNode = Y_Node.getDOMNode(otherNode);
+ var node = this._node,
+ parent = otherNode.parentNode,
+ nextSibling = otherNode.nextSibling;
+
+ if (nextSibling === node) {
+ parent.insertBefore(node, otherNode);
+ } else if (otherNode === node.nextSibling) {
+ parent.insertBefore(otherNode, node);
+ } else {
+ node.parentNode.replaceChild(otherNode, node);
+ Y_DOM.addHTML(parent, node, nextSibling);
}
+ return this;
},
- /**
- * Executes the supplied callback when the item with the supplied
- * id is found. This is meant to be used to execute behavior as
- * soon as possible as the page loads. If you use this after the
- * initial page load it will poll for a fixed time for the element.
- * The number of times it will poll and the frequency are
- * configurable. By default it will poll for 10 seconds.
- *
- * lazyEventFacade
is set to true.
- *
- * @method _define
- * @param o {DOMObject} A DOM object to add the property to
- * @param prop {String} The name of the new property
- * @param valueFn {Function} The function that will return the initial, default
- * value for the property.
- * @static
- * @private
- */
-IELazyFacade._define = function (o, prop, valueFn) {
- function val(v) {
- var ret = (arguments.length) ? v : valueFn.call(this);
-
- delete o[prop];
- Object.defineProperty(o, prop, {
- value: ret,
- configurable: true,
- writable: true
- });
- return ret;
- }
- Object.defineProperty(o, prop, {
- get: val,
- set: val,
- configurable: true
- });
-};
-
-if (imp && (!imp.hasFeature('Events', '2.0'))) {
- if (useLazyFacade) {
- // Make sure we can use the lazy facade logic
- try {
- Object.defineProperty(Y.config.doc.createEventObject(), 'z', {});
- } catch (e) {
- useLazyFacade = false;
- }
- }
-
- Y.DOMEventFacade = (useLazyFacade) ? IELazyFacade : IEEventFacade;
-}
-
-
-}, '3.4.0' ,{after:['event-base'], requires:['node-base']});
-YUI.add('pluginhost-base', function(Y) {
-
- /**
- * Provides the augmentable PluginHost interface, which can be added to any class.
- * @module pluginhost
- */
-
- /**
- * Provides the augmentable PluginHost interface, which can be added to any class.
- * @module pluginhost-base
- */
-
- /**
- *
- * Plug.Host's protected _initPlugins and _destroyPlugins - * methods should be invoked by the host class at the appropriate point in the host's lifecyle. - *
- * - * @class Plugin.Host - */ - - var L = Y.Lang; - - function PluginHost() { - this._plugins = {}; - } - - PluginHost.prototype = { - - /** - * Adds a plugin to the host object. This will instantiate the - * plugin and attach it to the configured namespace on the host object. - * - * @method plug - * @chainable - * @param P {Function | Object |Array} Accepts the plugin class, or an - * object with a "fn" property specifying the plugin class and - * a "cfg" property specifying the configuration for the Plugin. - *- * Additionally an Array can also be passed in, with the above function or - * object values, allowing the user to add multiple plugins in a single call. - *
- * @param config (Optional) If the first argument is the plugin class, the second argument - * can be the configuration for the plugin. - * @return {Base} A reference to the host object - */ - plug: function(Plugin, config) { - var i, ln, ns; - - if (L.isArray(Plugin)) { - for (i = 0, ln = Plugin.length; i < ln; i++) { - this.plug(Plugin[i]); - } - } else { - if (Plugin && !L.isFunction(Plugin)) { - config = Plugin.cfg; - Plugin = Plugin.fn; - } - - // Plugin should be fn by now - if (Plugin && Plugin.NS) { - ns = Plugin.NS; - - config = config || {}; - config.host = this; - - if (this.hasPlugin(ns)) { - // Update config - this[ns].setAttrs(config); - } else { - // Create new instance - this[ns] = new Plugin(config); - this._plugins[ns] = Plugin; - } - } - } - return this; - }, - - /** - * Removes a plugin from the host object. This will destroy the - * plugin instance and delete the namepsace from the host object. - * - * @method unplug - * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, - * all registered plugins are unplugged. - * @return {Base} A reference to the host object - * @chainable - */ - unplug: function(plugin) { - var ns = plugin, - plugins = this._plugins; - - if (plugin) { - if (L.isFunction(plugin)) { - ns = plugin.NS; - if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { - ns = null; - } - } - - if (ns) { - if (this[ns]) { - this[ns].destroy(); - delete this[ns]; - } - if (plugins[ns]) { - delete plugins[ns]; - } - } - } else { - for (ns in this._plugins) { - if (this._plugins.hasOwnProperty(ns)) { - this.unplug(ns); - } - } - } - return this; - }, - - /** - * Determines if a plugin has plugged into this host. - * - * @method hasPlugin - * @param {String} ns The plugin's namespace - * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise. - */ - hasPlugin : function(ns) { - return (this._plugins[ns] && this[ns]); - }, - - /** - * Initializes static plugins registered on the host (using the - * Base.plug static method) and any plugins passed to the - * instance through the "plugins" configuration property. - * - * @method _initPlugins - * @param {Config} config The configuration object with property name/value pairs. - * @private - */ - - _initPlugins: function(config) { - this._plugins = this._plugins || {}; - - if (this._initConfigPlugins) { - this._initConfigPlugins(config); - } - }, - - /** - * Unplugs and destroys all plugins on the host - * @method _destroyPlugins - * @private - */ - _destroyPlugins: function() { - this.unplug(); - } - }; - - Y.namespace("Plugin").Host = PluginHost; - - -}, '3.4.0' ,{requires:['yui-base']}); -YUI.add('pluginhost-config', function(Y) { - - /** - * Adds pluginhost constructor configuration and static configuration support - * @submodule pluginhost-config - */ - - /** - * Constructor and static configuration support for plugins - * - * @for Plugin.Host - */ - var PluginHost = Y.Plugin.Host, - L = Y.Lang; - - PluginHost.prototype._initConfigPlugins = function(config) { - - // Class Configuration - var classes = (this._getClasses) ? this._getClasses() : [this.constructor], - plug = [], - unplug = {}, - constructor, i, classPlug, classUnplug, pluginClassName; - - // TODO: Room for optimization. Can we apply statically/unplug in same pass? - for (i = classes.length - 1; i >= 0; i--) { - constructor = classes[i]; - - classUnplug = constructor._UNPLUG; - if (classUnplug) { - // subclasses over-write - Y.mix(unplug, classUnplug, true); - } - - classPlug = constructor._PLUG; - if (classPlug) { - // subclasses over-write - Y.mix(plug, classPlug, true); - } - } - - for (pluginClassName in plug) { - if (plug.hasOwnProperty(pluginClassName)) { - if (!unplug[pluginClassName]) { - this.plug(plug[pluginClassName]); - } - } - } - - // User Configuration - if (config && config.plugins) { - this.plug(config.plugins); - } - }; - - /** - * Registers plugins to be instantiated at the class level (plugins - * which should be plugged into every instance of the class by default). - * - * @method Plugin.Host.plug - * @static - * - * @param {Function} hostClass The host class on which to register the plugins - * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) - * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin - */ - PluginHost.plug = function(hostClass, plugin, config) { - // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ] - var p, i, l, name; - - if (hostClass !== Y.Base) { - hostClass._PLUG = hostClass._PLUG || {}; - - if (!L.isArray(plugin)) { - if (config) { - plugin = {fn:plugin, cfg:config}; - } - plugin = [plugin]; - } - - for (i = 0, l = plugin.length; i < l;i++) { - p = plugin[i]; - name = p.NAME || p.fn.NAME; - hostClass._PLUG[name] = p; - } - } - }; + /** + * Passes through to DOM method. + * @method blur + * @chainable + */ + 'blur', /** - * Unregisters any class level plugins which have been registered by the host class, or any - * other class in the hierarchy. - * - * @method Plugin.Host.unplug - * @static - * - * @param {Function} hostClass The host class from which to unregister the plugins - * @param {Function | Array} plugin The plugin class, or an array of plugin classes - */ - PluginHost.unplug = function(hostClass, plugin) { - var p, i, l, name; - - if (hostClass !== Y.Base) { - hostClass._UNPLUG = hostClass._UNPLUG || {}; - - if (!L.isArray(plugin)) { - plugin = [plugin]; - } - - for (i = 0, l = plugin.length; i < l; i++) { - p = plugin[i]; - name = p.NAME; - if (!hostClass._PLUG[name]) { - hostClass._UNPLUG[name] = p; - } else { - delete hostClass._PLUG[name]; - } - } - } - }; - - -}, '3.4.0' ,{requires:['pluginhost-base']}); - - -YUI.add('pluginhost', function(Y){}, '3.4.0' ,{use:['pluginhost-base', 'pluginhost-config']}); - -YUI.add('node-base', function(Y) { - -/** - * The Node Utility provides a DOM-like interface for interacting with DOM nodes. - * @module node - * @submodule node-base - */ - -/** - * The Node class provides a wrapper for manipulating DOM Nodes. - * Node properties can be accessed via the set/get methods. - * Use Y.one() to retrieve Node instances. - * - * NOTE: Node properties are accessed using - * theset
and get
methods.
- *
- * @class Node
- * @constructor
- * @param {DOMNode} node the DOM node to be mapped to the Node instance.
- * @for Node
- */
-
-// "globals"
-var DOT = '.',
- NODE_NAME = 'nodeName',
- NODE_TYPE = 'nodeType',
- OWNER_DOCUMENT = 'ownerDocument',
- TAG_NAME = 'tagName',
- UID = '_yuid',
- EMPTY_OBJ = {},
-
- _slice = Array.prototype.slice,
-
- Y_DOM = Y.DOM,
-
- Y_Node = function(node) {
- if (!this.getDOMNode) { // support optional "new"
- return new Y_Node(node);
- }
-
- if (typeof node == 'string') {
- node = Y_Node._fromString(node);
- if (!node) {
- return null; // NOTE: return
- }
- }
-
- var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID];
-
- if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) {
- node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
- }
-
- uid = uid || Y.stamp(node);
- if (!uid) { // stamp failed; likely IE non-HTMLElement
- uid = Y.guid();
- }
-
- this[UID] = uid;
-
- /**
- * The underlying DOM node bound to the Y.Node instance
- * @property _node
- * @private
- */
- this._node = node;
-
- this._stateProxy = node; // when augmented with Attribute
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method submit
+ * @chainable
+ */
+ 'submit',
- if (this._initPlugins) { // when augmented with Plugin.Host
- this._initPlugins();
- }
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method reset
+ * @chainable
+ */
+ 'reset',
- this.SHOW_TRANSITION = Y_Node.SHOW_TRANSITION;
- this.HIDE_TRANSITION = Y_Node.HIDE_TRANSITION;
- },
+ /**
+ * Passes through to DOM method.
+ * @method select
+ * @chainable
+ */
+ 'select',
- // used with previous/next/ancestor tests
- _wrapFn = function(fn) {
- var ret = null;
- if (fn) {
- ret = (typeof fn == 'string') ?
- function(n) {
- return Y.Selector.test(n, fn);
- } :
- function(n) {
- return fn(Y.one(n));
- };
- }
+ /**
+ * Passes through to DOM method.
+ * Only valid on TABLE elements
+ * @method createCaption
+ * @chainable
+ */
+ 'createCaption'
+], function(method) {
+ Y.Node.prototype[method] = function(arg1, arg2, arg3) {
+ var ret = this.invoke(method, arg1, arg2, arg3);
return ret;
};
-// end "globals"
-
-Y_Node._fromString = function(node) {
- if (node) {
- if (node.indexOf('doc') === 0) { // doc OR document
- node = Y.config.doc;
- } else if (node.indexOf('win') === 0) { // win OR window
- node = Y.config.win;
- } else {
- node = Y.Selector.query(node, null, true);
- }
- }
-
- return node || null;
-};
-
-/**
- * The name of the component
- * @static
- * @property NAME
- */
-Y_Node.NAME = 'node';
-
-/*
- * The pattern used to identify ARIA attributes
- */
-Y_Node.re_aria = /^(?:role$|aria-)/;
-
-Y_Node.SHOW_TRANSITION = 'fadeIn';
-Y_Node.HIDE_TRANSITION = 'fadeOut';
-
-/**
- * List of events that route to DOM events
- * @static
- * @property DOM_EVENTS
- */
-
-Y_Node.DOM_EVENTS = {
- abort: 1,
- beforeunload: 1,
- blur: 1,
- change: 1,
- click: 1,
- close: 1,
- command: 1,
- contextmenu: 1,
- dblclick: 1,
- DOMMouseScroll: 1,
- drag: 1,
- dragstart: 1,
- dragenter: 1,
- dragover: 1,
- dragleave: 1,
- dragend: 1,
- drop: 1,
- error: 1,
- focus: 1,
- key: 1,
- keydown: 1,
- keypress: 1,
- keyup: 1,
- load: 1,
- message: 1,
- mousedown: 1,
- mouseenter: 1,
- mouseleave: 1,
- mousemove: 1,
- mousemultiwheel: 1,
- mouseout: 1,
- mouseover: 1,
- mouseup: 1,
- mousewheel: 1,
- orientationchange: 1,
- reset: 1,
- resize: 1,
- select: 1,
- selectstart: 1,
- submit: 1,
- scroll: 1,
- textInput: 1,
- unload: 1
-};
-
-// Add custom event adaptors to this list. This will make it so
-// that delegate, key, available, contentready, etc all will
-// be available through Node.on
-Y.mix(Y_Node.DOM_EVENTS, Y.Env.evt.plugins);
-
-/**
- * A list of Node instances that have been created
- * @private
- * @property _instances
- * @static
- *
- */
-Y_Node._instances = {};
-
-/**
- * Retrieves the DOM node bound to a Node instance
- * @method getDOMNode
- * @static
- *
- * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
- * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
- * as the node argument, it is simply returned.
- */
-Y_Node.getDOMNode = function(node) {
- if (node) {
- return (node.nodeType) ? node : node._node || null;
- }
- return null;
-};
-
-/**
- * Checks Node return values and wraps DOM Nodes as Y.Node instances
- * and DOM Collections / Arrays as Y.NodeList instances.
- * Other return values just pass thru. If undefined is returned (e.g. no return)
- * then the Node instance is returned for chainability.
- * @method scrubVal
- * @static
- *
- * @param {any} node The Node instance or an HTMLNode
- * @return {Y.Node | Y.NodeList | any} Depends on what is returned from the DOM node.
- */
-Y_Node.scrubVal = function(val, node) {
- if (val) { // only truthy values are risky
- if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function
- if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window
- val = Y.one(val);
- } else if ((val.item && !val._nodes) || // dom collection or Node instance
- (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
- val = Y.all(val);
- }
- }
- } else if (typeof val === 'undefined') {
- val = node; // for chaining
- } else if (val === null) {
- val = null; // IE: DOM null not the same as null
- }
-
- return val;
-};
-
-/**
- * Adds methods to the Y.Node prototype, routing through scrubVal.
- * @method addMethod
- * @static
- *
- * @param {String} name The name of the method to add
- * @param {Function} fn The function that becomes the method
- * @param {Object} context An optional context to call the method with
- * (defaults to the Node instance)
- * @return {any} Depends on what is returned from the DOM node.
- */
-Y_Node.addMethod = function(name, fn, context) {
- if (name && fn && typeof fn == 'function') {
- Y_Node.prototype[name] = function() {
- var args = _slice.call(arguments),
- node = this,
- ret;
-
- if (args[0] && Y.instanceOf(args[0], Y_Node)) {
- args[0] = args[0]._node;
- }
+});
- if (args[1] && Y.instanceOf(args[1], Y_Node)) {
- args[1] = args[1]._node;
- }
- args.unshift(node._node);
+Y.Node.importMethod(Y.DOM, [
+ /**
+ * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
+ * @method contains
+ * @param {Node | HTMLElement} needle The possible node or descendent
+ * @return {Boolean} Whether or not this node is the needle its ancestor
+ */
+ 'contains',
+ /**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @for Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+ 'setAttribute',
+ /**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @for Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+ 'getAttribute',
- ret = fn.apply(node, args);
+ /**
+ * Wraps the given HTML around the node.
+ * @method wrap
+ * @param {String} html The markup to wrap around the node.
+ * @chainable
+ * @for Node
+ */
+ 'wrap',
- if (ret) { // scrub truthy
- ret = Y_Node.scrubVal(ret, node);
- }
+ /**
+ * Removes the node's parent node.
+ * @method unwrap
+ * @chainable
+ */
+ 'unwrap',
- (typeof ret != 'undefined') || (ret = node);
- return ret;
- };
- } else {
- }
-};
+ /**
+ * Applies a unique ID to the node if none exists
+ * @method generateID
+ * @return {String} The existing or generated ID
+ */
+ 'generateID'
+]);
+Y.NodeList.importMethod(Y.Node.prototype, [
/**
- * Imports utility methods to be added as Y.Node methods.
- * @method importMethod
- * @static
- *
- * @param {Object} host The object that contains the method to import.
- * @param {String} name The name of the method to import
- * @param {String} altName An optional name to use in place of the host name
- * @param {Object} context An optional context to call the method with
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
*/
-Y_Node.importMethod = function(host, name, altName) {
- if (typeof name == 'string') {
- altName = altName || name;
- Y_Node.addMethod(altName, host[name], host);
- } else {
- Y.Array.each(name, function(n) {
- Y_Node.importMethod(host, n);
- });
- }
-};
+ 'getAttribute',
/**
- * Returns a single Node instance bound to the node or the
- * first element matching the given selector. Returns null if no match found.
- * Note: For chaining purposes you may want to
- * use Y.all
, which returns a NodeList when no match is found.
- * @method Y.one
- * @static
- * @param {String | HTMLElement} node a node or Selector
- * @return {Y.Node | null} a Node instance or null if no match found.
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @see Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
*/
-Y_Node.one = function(node) {
- var instance = null,
- cachedNode,
- uid;
-
- if (node) {
- if (typeof node == 'string') {
- node = Y_Node._fromString(node);
- if (!node) {
- return null; // NOTE: return
- }
- } else if (node.getDOMNode) {
- return node; // NOTE: return
- }
-
- if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
- uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
- instance = Y_Node._instances[uid]; // reuse exising instances
- cachedNode = instance ? instance._node : null;
- if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
- instance = new Y_Node(node);
- Y_Node._instances[instance[UID]] = instance; // cache node
- }
- }
- }
-
- return instance;
-};
+ 'setAttribute',
/**
- * Returns a new dom node using the provided markup string.
- * @method create
- * @static
- * @param {String} html The markup used to create the element
- * @param {HTMLDocument} doc An optional document context
- * @return {Node} A Node instance bound to a DOM node or fragment
+ * Allows for removing attributes on DOM nodes.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method removeAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute to remove
*/
-Y_Node.create = function(html, doc) {
- if (doc && doc._node) {
- doc = doc._node;
- }
- return Y.one(Y_DOM.create(html, doc));
-};
+ 'removeAttribute',
+/**
+ * Removes the parent node from node in the list.
+ * @method unwrap
+ * @chainable
+ */
+ 'unwrap',
+/**
+ * Wraps the given HTML around each node.
+ * @method wrap
+ * @param {String} html The markup to wrap around the node.
+ * @chainable
+ */
+ 'wrap',
/**
- * Static collection of configuration attributes for special handling
- * @property ATTRS
- * @static
- * @type object
+ * Applies a unique ID to each node if none exists
+ * @method generateID
+ * @return {String} The existing or generated ID
*/
-Y_Node.ATTRS = {
- /**
- * Allows for getting and setting the text of an element.
- * Formatting is preserved and special characters are treated literally.
- * @config text
- * @type String
- */
- text: {
- getter: function() {
- return Y_DOM.getText(this._node);
- },
+ 'generateID'
+]);
- setter: function(content) {
- Y_DOM.setText(this._node, content);
- return content;
- }
- },
- /**
- * Allows for getting and setting the text of an element.
- * Formatting is preserved and special characters are treated literally.
- * @config text
- * @type String
- */
- 'for': {
- getter: function() {
- return Y_DOM.getAttribute(this._node, 'for');
- },
+}, '3.4.0' ,{requires:['dom-core', 'selector']});
+YUI.add('node-base', function(Y) {
- setter: function(val) {
- Y_DOM.setAttribute(this._node, 'for', val);
- return val;
- }
- },
+/**
+ * @module node
+ * @submodule node-base
+ */
- 'options': {
- getter: function() {
- return this._node.getElementsByTagName('option');
- }
- },
+var methods = [
+/**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @for Node
+ * @param {String} className the class name to search for
+ * @return {Boolean} Whether or not the element has the specified class
+ */
+ 'hasClass',
- /**
- * Returns a NodeList instance of all HTMLElement children.
- * @readOnly
- * @config children
- * @type NodeList
- */
- 'children': {
- getter: function() {
- var node = this._node,
- children = node.children,
- childNodes, i, len;
+/**
+ * Adds a class name to each node.
+ * @method addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+ 'addClass',
- if (!children) {
- childNodes = node.childNodes;
- children = [];
+/**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+ 'removeClass',
- for (i = 0, len = childNodes.length; i < len; ++i) {
- if (childNodes[i][TAG_NAME]) {
- children[children.length] = childNodes[i];
- }
- }
- }
- return Y.all(children);
- }
- },
+/**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+ 'replaceClass',
- value: {
- getter: function() {
- return Y_DOM.getValue(this._node);
- },
+/**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {String} className the class name to be toggled
+ * @param {Boolean} force Option to force adding or removing the class.
+ * @chainable
+ */
+ 'toggleClass'
+];
- setter: function(val) {
- Y_DOM.setValue(this._node, val);
- return val;
- }
- }
-};
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @see Node.hasClass
+ * @for NodeList
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
/**
- * The default setter for DOM properties
- * Called with instance context (this === the Node instance)
- * @method DEFAULT_SETTER
- * @static
- * @param {String} name The attribute/property being set
- * @param {any} val The value to be set
- * @return {any} The value
+ * Adds a class name to each node.
+ * @method addClass
+ * @see Node.addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
*/
-Y_Node.DEFAULT_SETTER = function(name, val) {
- var node = this._stateProxy,
- strPath;
- if (name.indexOf(DOT) > -1) {
- strPath = name;
- name = name.split(DOT);
- // only allow when defined on node
- Y.Object.setValue(node, name, val);
- } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
- node[name] = val;
- }
+/**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @see Node.removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
- return val;
-};
+/**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @see Node.replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
/**
- * The default getter for DOM properties
- * Called with instance context (this === the Node instance)
- * @method DEFAULT_GETTER
- * @static
- * @param {String} name The attribute/property to look up
- * @return {any} The current value
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @see Node.toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
*/
-Y_Node.DEFAULT_GETTER = function(name) {
- var node = this._stateProxy,
- val;
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+/**
+ * @module node
+ * @submodule node-base
+ */
+
+var Y_Node = Y.Node,
+ Y_DOM = Y.DOM;
- if (name.indexOf && name.indexOf(DOT) > -1) {
- val = Y.Object.getValue(node, name.split(DOT));
- } else if (typeof node[name] != 'undefined') { // pass thru from DOM
- val = node[name];
+/**
+ * Returns a new dom node using the provided markup string.
+ * @method create
+ * @static
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ * @for Node
+ */
+Y_Node.create = function(html, doc) {
+ if (doc && doc._node) {
+ doc = doc._node;
}
-
- return val;
+ return Y.one(Y_DOM.create(html, doc));
};
-Y.augment(Y_Node, Y.EventTarget);
-
Y.mix(Y_Node.prototype, {
/**
- * The method called when outputting Node instances as strings
- * @method toString
- * @return {String} A string representation of the Node instance
+ * Creates a new Node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
*/
- toString: function() {
- var str = this[UID] + ': not bound to a node',
- node = this._node,
- attrs, id, className;
+ create: Y_Node.create,
- if (node) {
- attrs = node.attributes;
- id = (attrs && attrs.id) ? node.getAttribute('id') : null;
- className = (attrs && attrs.className) ? node.getAttribute('className') : null;
- str = node[NODE_NAME];
+ /**
+ * Inserts the content before the reference node.
+ * @method insert
+ * @param {String | Y.Node | HTMLElement | Y.NodeList | HTMLCollection} content The content to insert
+ * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
+ * Possible "where" arguments
+ * The callback is executed with a single parameter: + * the custom object parameter, if provided.
+ * + * @method onAvailable + * + * @param {string||string[]} id the id of the element, or an array + * of ids to look for. + * @param {function} fn what to execute when the element is found. + * @param {object} p_obj an optional object to be passed back as + * a parameter to fn. + * @param {boolean|object} p_override If set to true, fn will execute + * in the context of p_obj, if set to an object it + * will execute in the context of that object + * @param checkContent {boolean} check child node readiness (onContentReady) + * @static + * @deprecated Use Y.on("available") + */ + // @TODO fix arguments + onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) { - toString: function() { - var str = '', - errorMsg = this[UID] + ': not bound to any nodes', - nodes = this._nodes, - node; + var a = Y.Array(id), i, availHandle; - if (nodes && nodes[0]) { - node = nodes[0]; - str += node[NODE_NAME]; - if (node.id) { - str += '#' + node.id; - } - if (node.className) { - str += '.' + node.className.replace(' ', '.'); + for (i=0; iThe callback is executed with a single parameter: + * the custom object parameter, if provided.
+ * + * @method onContentReady + * + * @param {string} id the id of the element to look for. + * @param {function} fn what to execute when the element is ready. + * @param {object} obj an optional object to be passed back as + * a parameter to fn. + * @param {boolean|object} override If set to true, fn will execute + * in the context of p_obj. If an object, fn will + * exectute in the context of that object + * + * @static + * @deprecated Use Y.on("contentready") + */ + // @TODO fix arguments + onContentReady: function(id, fn, obj, override, compat) { + return Event.onAvailable(id, fn, obj, override, true, compat); + }, - /** Called on each Node instance - * @method prepend - * @see Node.prepend - */ - 'prepend', + /** + * Adds an event listener + * + * @method attach + * + * @param {String} type The type of event to append + * @param {Function} fn The method the event invokes + * @param {String|HTMLElement|Array|NodeList} el An id, an element + * reference, or a collection of ids and/or elements to assign the + * listener to. + * @param {Object} context optional context object + * @param {Boolean|object} args 0..n arguments to pass to the callback + * @return {EventHandle} an object to that can be used to detach the listener + * + * @static + */ - /** Called on each Node instance - * @method remove - * @see Node.remove - */ - 'remove', + attach: function(type, fn, el, context) { + return Event._attach(Y.Array(arguments, 0, true)); + }, - /** Called on each Node instance - * @method set - * @see Node.set - */ - 'set', + _createWrapper: function (el, type, capture, compat, facade) { - /** Called on each Node instance - * @method setContent - * @see Node.setContent - */ - 'setContent', + var cewrapper, + ek = Y.stamp(el), + key = 'event:' + ek + type; - /** - * Makes each node visible. - * If the "transition" module is loaded, show optionally - * animates the showing of the node using either the default - * transition effect ('fadeIn'), or the given named effect. - * @method show - * @param {String} name A named Transition effect to use as the show effect. - * @param {Object} config Options to use with the transition. - * @param {Function} callback An optional function to run after the transition completes. - * @chainable - */ - 'show', + if (false === facade) { + key += 'native'; + } + if (capture) { + key += 'capture'; + } - /** - * Hides each node. - * If the "transition" module is loaded, hide optionally - * animates the hiding of the node using either the default - * transition effect ('fadeOut'), or the given named effect. - * @method hide - * @param {String} name A named Transition effect to use as the show effect. - * @param {Object} config Options to use with the transition. - * @param {Function} callback An optional function to run after the transition completes. - * @chainable - */ - 'hide', - 'toggleView' -]); + cewrapper = _wrappers[key]; -// one-off implementation to convert array of Nodes to NodeList -// e.g. Y.all('input').get('parentNode'); -/** Called on each Node instance - * @method get - * @see Node - */ -NodeList.prototype.get = function(attr) { - var ret = [], - nodes = this._nodes, - isNodeList = false, - getTemp = NodeList._getTempNode, - instance, - val; + if (!cewrapper) { + // create CE wrapper + cewrapper = Y.publish(key, { + silent: true, + bubbles: false, + contextFn: function() { + if (compat) { + return cewrapper.el; + } else { + cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el); + return cewrapper.nodeRef; + } + } + }); - if (nodes[0]) { - instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]); - val = instance._get(attr); - if (val && val.nodeType) { - isNodeList = true; - } - } + cewrapper.overrides = {}; - Y.Array.each(nodes, function(node) { - instance = Y.Node._instances[node._yuid]; + // for later removeListener calls + cewrapper.el = el; + cewrapper.key = key; + cewrapper.domkey = ek; + cewrapper.type = type; + cewrapper.fn = function(e) { + cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade)))); + }; + cewrapper.capture = capture; - if (!instance) { - instance = getTemp(node); - } + if (el == win && type == "load") { + // window load happens once + cewrapper.fireOnce = true; + _windowLoadKey = key; + } + cewrapper._delete = _deleteAndClean; - val = instance._get(attr); - if (!isNodeList) { // convert array of Nodes to NodeList - val = Y.Node.scrubVal(val, instance); - } + _wrappers[key] = cewrapper; + _el_events[ek] = _el_events[ek] || {}; + _el_events[ek][key] = cewrapper; - ret.push(val); - }); + add(el, type, cewrapper.fn, capture); + } - return (isNodeList) ? Y.all(ret) : ret; -}; + return cewrapper; -Y.NodeList = NodeList; + }, -Y.all = function(nodes) { - return new NodeList(nodes); -}; + _attach: function(args, conf) { -Y.Node.all = Y.all; -Y.Array.each([ - /** - * Passes through to DOM method. - * @for Node - * @method removeChild - * @param {HTMLElement | Node} node Node to be removed - * @return {Node} The removed node - */ - 'removeChild', + var compat, + handles, oEl, cewrapper, context, + fireNow = false, ret, + type = args[0], + fn = args[1], + el = args[2] || win, + facade = conf && conf.facade, + capture = conf && conf.capture, + overrides = conf && conf.overrides; - /** - * Passes through to DOM method. - * @method hasChildNodes - * @return {Boolean} Whether or not the node has any childNodes - */ - 'hasChildNodes', + if (args[args.length-1] === COMPAT_ARG) { + compat = true; + } - /** - * Passes through to DOM method. - * @method cloneNode - * @param {Boolean} deep Whether or not to perform a deep clone, which includes - * subtree and attributes - * @return {Node} The clone - */ - 'cloneNode', + if (!fn || !fn.call) { +// throw new TypeError(type + " attach call failed, callback undefined"); + return false; + } - /** - * Passes through to DOM method. - * @method hasAttribute - * @param {String} attribute The attribute to test for - * @return {Boolean} Whether or not the attribute is present - */ - 'hasAttribute', + // The el argument can be an array of elements or element ids. + if (shouldIterate(el)) { - /** - * Passes through to DOM method. - * @method removeAttribute - * @param {String} attribute The attribute to be removed - * @chainable - */ - 'removeAttribute', + handles=[]; - /** - * Passes through to DOM method. - * @method scrollIntoView - * @chainable - */ - 'scrollIntoView', + Y.each(el, function(v, k) { + args[2] = v; + handles.push(Event._attach(args.slice(), conf)); + }); - /** - * Passes through to DOM method. - * @method getElementsByTagName - * @param {String} tagName The tagName to collect - * @return {NodeList} A NodeList representing the HTMLCollection - */ - 'getElementsByTagName', + // return (handles.length === 1) ? handles[0] : handles; + return new Y.EventHandle(handles); - /** - * Passes through to DOM method. - * @method focus - * @chainable - */ - 'focus', + // If the el argument is a string, we assume it is + // actually the id of the element. If the page is loaded + // we convert el to the actual element, otherwise we + // defer attaching the event until the element is + // ready + } else if (Y.Lang.isString(el)) { - /** - * Passes through to DOM method. - * @method blur - * @chainable - */ - 'blur', + // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el); - /** - * Passes through to DOM method. - * Only valid on FORM elements - * @method submit - * @chainable - */ - 'submit', + if (compat) { + oEl = Y.DOM.byId(el); + } else { - /** - * Passes through to DOM method. - * Only valid on FORM elements - * @method reset - * @chainable - */ - 'reset', + oEl = Y.Selector.query(el); - /** - * Passes through to DOM method. - * @method select - * @chainable - */ - 'select', + switch (oEl.length) { + case 0: + oEl = null; + break; + case 1: + oEl = oEl[0]; + break; + default: + args[2] = oEl; + return Event._attach(args, conf); + } + } - /** - * Passes through to DOM method. - * Only valid on TABLE elements - * @method createCaption - * @chainable - */ - 'createCaption' + if (oEl) { -], function(method) { - Y.Node.prototype[method] = function(arg1, arg2, arg3) { - var ret = this.invoke(method, arg1, arg2, arg3); - return ret; - }; -}); + el = oEl; -Y.Node.importMethod(Y.DOM, [ - /** - * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy. - * @method contains - * @param {Node | HTMLElement} needle The possible node or descendent - * @return {Boolean} Whether or not this node is the needle its ancestor - */ - 'contains', - /** - * Allows setting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method setAttribute - * @for Node - * @for NodeList - * @chainable - * @param {string} name The attribute name - * @param {string} value The value to set - */ - 'setAttribute', - /** - * Allows getting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method getAttribute - * @for Node - * @for NodeList - * @param {string} name The attribute name - * @return {string} The attribute value - */ - 'getAttribute', + // Not found = defer adding the event until the element is available + } else { - /** - * Wraps the given HTML around the node. - * @method wrap - * @param {String} html The markup to wrap around the node. - * @chainable - */ - 'wrap', + ret = Event.onAvailable(el, function() { - /** - * Removes the node's parent node. - * @method unwrap - * @chainable - */ - 'unwrap', + ret.handle = Event._attach(args, conf); - /** - * Applies a unique ID to the node if none exists - * @method generateID - * @return {String} The existing or generated ID - */ - 'generateID' -]); + }, Event, true, false, compat); -Y.NodeList.importMethod(Y.Node.prototype, [ -/** - * Allows getting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method getAttribute - * @see Node - * @for NodeList - * @param {string} name The attribute name - * @return {string} The attribute value - */ + return ret; - 'getAttribute', -/** - * Allows setting attributes on DOM nodes, normalizing in some cases. - * This passes through to the DOM node, allowing for custom attributes. - * @method setAttribute - * @see Node - * @for NodeList - * @chainable - * @param {string} name The attribute name - * @param {string} value The value to set - */ - 'setAttribute', - -/** - * Allows for removing attributes on DOM nodes. - * This passes through to the DOM node, allowing for custom attributes. - * @method removeAttribute - * @see Node - * @for NodeList - * @param {string} name The attribute to remove - */ - 'removeAttribute', -/** - * Removes the parent node from node in the list. - * @method unwrap - * @chainable - */ - 'unwrap', -/** - * Wraps the given HTML around each node. - * @method wrap - * @param {String} html The markup to wrap around the node. - * @chainable - */ - 'wrap', + } + } -/** - * Applies a unique ID to each node if none exists - * @method generateID - * @return {String} The existing or generated ID - */ - 'generateID' -]); -(function(Y) { - var methods = [ - /** - * Determines whether each node has the given className. - * @method hasClass - * @for Node - * @param {String} className the class name to search for - * @return {Boolean} Whether or not the element has the specified class - */ - 'hasClass', + // Element should be an html element or node + if (!el) { + return false; + } + + if (Y.Node && Y.instanceOf(el, Y.Node)) { + el = Y.Node.getDOMNode(el); + } - /** - * Adds a class name to each node. - * @method addClass - * @param {String} className the class name to add to the node's class attribute - * @chainable - */ - 'addClass', + cewrapper = Event._createWrapper(el, type, capture, compat, facade); + if (overrides) { + Y.mix(cewrapper.overrides, overrides); + } - /** - * Removes a class name from each node. - * @method removeClass - * @param {String} className the class name to remove from the node's class attribute - * @chainable - */ - 'removeClass', + if (el == win && type == "load") { - /** - * Replace a class with another class for each node. - * If no oldClassName is present, the newClassName is simply added. - * @method replaceClass - * @param {String} oldClassName the class name to be replaced - * @param {String} newClassName the class name that will be replacing the old class name - * @chainable - */ - 'replaceClass', + // if the load is complete, fire immediately. + // all subscribers, including the current one + // will be notified. + if (YUI.Env.windowLoaded) { + fireNow = true; + } + } - /** - * If the className exists on the node it is removed, if it doesn't exist it is added. - * @method toggleClass - * @param {String} className the class name to be toggled - * @param {Boolean} force Option to force adding or removing the class. - * @chainable - */ - 'toggleClass' - ]; + if (compat) { + args.pop(); + } - Y.Node.importMethod(Y.DOM, methods); - /** - * Determines whether each node has the given className. - * @method hasClass - * @see Node.hasClass - * @for NodeList - * @param {String} className the class name to search for - * @return {Array} An array of booleans for each node bound to the NodeList. - */ + context = args[3]; - /** - * Adds a class name to each node. - * @method addClass - * @see Node.addClass - * @param {String} className the class name to add to the node's class attribute - * @chainable - */ + // set context to the Node if not specified + // ret = cewrapper.on.apply(cewrapper, trimmedArgs); + ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null); - /** - * Removes a class name from each node. - * @method removeClass - * @see Node.removeClass - * @param {String} className the class name to remove from the node's class attribute - * @chainable - */ + if (fireNow) { + cewrapper.fire(); + } - /** - * Replace a class with another class for each node. - * If no oldClassName is present, the newClassName is simply added. - * @method replaceClass - * @see Node.replaceClass - * @param {String} oldClassName the class name to be replaced - * @param {String} newClassName the class name that will be replacing the old class name - * @chainable - */ + return ret; - /** - * If the className exists on the node it is removed, if it doesn't exist it is added. - * @method toggleClass - * @see Node.toggleClass - * @param {String} className the class name to be toggled - * @chainable - */ - Y.NodeList.importMethod(Y.Node.prototype, methods); -})(Y); + }, -if (!Y.config.doc.documentElement.hasAttribute) { // IE < 8 - Y.Node.prototype.hasAttribute = function(attr) { - if (attr === 'value') { - if (this.get('value') !== "") { // IE < 8 fails to populate specified when set in HTML - return true; + /** + * Removes an event listener. Supports the signature the event was bound + * with, but the preferred way to remove listeners is using the handle + * that is returned when using Y.on + * + * @method detach + * + * @param {String} type the type of event to remove. + * @param {Function} fn the method the event invokes. If fn is + * undefined, then all event handlers for the type of event are + * removed. + * @param {String|HTMLElement|Array|NodeList|EventHandle} el An + * event handle, an id, an element reference, or a collection + * of ids and/or elements to remove the listener from. + * @return {boolean} true if the unbind was successful, false otherwise. + * @static + */ + detach: function(type, fn, el, obj) { + + var args=Y.Array(arguments, 0, true), compat, l, ok, i, + id, ce; + + if (args[args.length-1] === COMPAT_ARG) { + compat = true; + // args.pop(); } - } - return !!(this._node.attributes[attr] && - this._node.attributes[attr].specified); - }; -} -// IE throws an error when calling focus() on an element that's invisible, not -// displayed, or disabled. -Y.Node.prototype.focus = function () { - try { - this._node.focus(); - } catch (e) { - } + if (type && type.detach) { + return type.detach(); + } - return this; -}; + // The el argument can be a string + if (typeof el == "string") { -// IE throws error when setting input.type = 'hidden', -// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden' -Y.Node.ATTRS.type = { - setter: function(val) { - if (val === 'hidden') { - try { - this._node.type = 'hidden'; - } catch(e) { - this.setStyle('display', 'none'); - this._inputType = 'hidden'; + // el = (compat) ? Y.DOM.byId(el) : Y.all(el); + if (compat) { + el = Y.DOM.byId(el); + } else { + el = Y.Selector.query(el); + l = el.length; + if (l < 1) { + el = null; + } else if (l == 1) { + el = el[0]; + } + } + // return Event.detach.apply(Event, args); } - } else { - try { // IE errors when changing the type from "hidden' - this._node.type = val; - } catch (e) { + + if (!el) { + return false; } - } - return val; - }, - getter: function() { - return this._inputType || this._node.type; - }, + if (el.detach) { + args.splice(2, 1); + return el.detach.apply(el, args); + // The el argument can be an array of elements or element ids. + } else if (shouldIterate(el)) { + ok = true; + for (i=0, l=el.length; i+ * An augmentable class, which provides the augmented class with the ability to host plugins. + * It adds plug and unplug methods to the augmented class, which can + * be used to add or remove plugins from instances of the class. + *
+ * + *Plugins can also be added through the constructor configuration object passed to the host class' constructor using + * the "plugins" property. Supported values for the "plugins" property are those defined by the plug method. + * + * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host): + *
+ * Plug.Host's protected _initPlugins and _destroyPlugins + * methods should be invoked by the host class at the appropriate point in the host's lifecyle. + *
+ * + * @class Plugin.Host + */ + + var L = Y.Lang; + + function PluginHost() { + this._plugins = {}; } - return Y.DOM.inRegion(node1, node2, all, altRegion); -}; + PluginHost.prototype = { + + /** + * Adds a plugin to the host object. This will instantiate the + * plugin and attach it to the configured namespace on the host object. + * + * @method plug + * @chainable + * @param P {Function | Object |Array} Accepts the plugin class, or an + * object with a "fn" property specifying the plugin class and + * a "cfg" property specifying the configuration for the Plugin. + *+ * Additionally an Array can also be passed in, with the above function or + * object values, allowing the user to add multiple plugins in a single call. + *
+ * @param config (Optional) If the first argument is the plugin class, the second argument + * can be the configuration for the plugin. + * @return {Base} A reference to the host object + */ + plug: function(Plugin, config) { + var i, ln, ns; + + if (L.isArray(Plugin)) { + for (i = 0, ln = Plugin.length; i < ln; i++) { + this.plug(Plugin[i]); + } + } else { + if (Plugin && !L.isFunction(Plugin)) { + config = Plugin.cfg; + Plugin = Plugin.fn; + } + + // Plugin should be fn by now + if (Plugin && Plugin.NS) { + ns = Plugin.NS; + + config = config || {}; + config.host = this; + + if (this.hasPlugin(ns)) { + // Update config + this[ns].setAttrs(config); + } else { + // Create new instance + this[ns] = new Plugin(config); + this._plugins[ns] = Plugin; + } + } + } + return this; + }, + + /** + * Removes a plugin from the host object. This will destroy the + * plugin instance and delete the namepsace from the host object. + * + * @method unplug + * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, + * all registered plugins are unplugged. + * @return {Base} A reference to the host object + * @chainable + */ + unplug: function(plugin) { + var ns = plugin, + plugins = this._plugins; + + if (plugin) { + if (L.isFunction(plugin)) { + ns = plugin.NS; + if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { + ns = null; + } + } + + if (ns) { + if (this[ns]) { + this[ns].destroy(); + delete this[ns]; + } + if (plugins[ns]) { + delete plugins[ns]; + } + } + } else { + for (ns in this._plugins) { + if (this._plugins.hasOwnProperty(ns)) { + this.unplug(ns); + } + } + } + return this; + }, + + /** + * Determines if a plugin has plugged into this host. + * + * @method hasPlugin + * @param {String} ns The plugin's namespace + * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise. + */ + hasPlugin : function(ns) { + return (this._plugins[ns] && this[ns]); + }, -}, '3.4.0' ,{requires:['node-base', 'dom-screen']}); -YUI.add('node-pluginhost', function(Y) { + /** + * Initializes static plugins registered on the host (using the + * Base.plug static method) and any plugins passed to the + * instance through the "plugins" configuration property. + * + * @method _initPlugins + * @param {Config} config The configuration object with property name/value pairs. + * @private + */ + + _initPlugins: function(config) { + this._plugins = this._plugins || {}; -/** - * Registers plugins to be instantiated at the class level (plugins - * which should be plugged into every instance of Node by default). - * - * @method Node.plug - * @static - * - * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) - * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin - */ -Y.Node.plug = function() { - var args = Y.Array(arguments); - args.unshift(Y.Node); - Y.Plugin.Host.plug.apply(Y.Base, args); - return Y.Node; -}; + if (this._initConfigPlugins) { + this._initConfigPlugins(config); + } + }, -/** - * Unregisters any class level plugins which have been registered by the Node - * - * @method Node.unplug - * @static - * - * @param {Function | Array} plugin The plugin class, or an array of plugin classes - */ -Y.Node.unplug = function() { - var args = Y.Array(arguments); - args.unshift(Y.Node); - Y.Plugin.Host.unplug.apply(Y.Base, args); - return Y.Node; -}; + /** + * Unplugs and destroys all plugins on the host + * @method _destroyPlugins + * @private + */ + _destroyPlugins: function() { + this.unplug(); + } + }; -Y.mix(Y.Node, Y.Plugin.Host, false, null, 1); + Y.namespace("Plugin").Host = PluginHost; -// allow batching of plug/unplug via NodeList -// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode) -Y.NodeList.prototype.plug = function() { - var args = arguments; - Y.NodeList.each(this, function(node) { - Y.Node.prototype.plug.apply(Y.one(node), args); - }); -}; -Y.NodeList.prototype.unplug = function() { - var args = arguments; - Y.NodeList.each(this, function(node) { - Y.Node.prototype.unplug.apply(Y.one(node), args); - }); -}; +}, '3.4.0' ,{requires:['yui-base']}); +YUI.add('pluginhost-config', function(Y) { + /** + * Adds pluginhost constructor configuration and static configuration support + * @submodule pluginhost-config + */ -}, '3.4.0' ,{requires:['node-base', 'pluginhost']}); -YUI.add('node-event-delegate', function(Y) { + var PluginHost = Y.Plugin.Host, + L = Y.Lang; -/** - * Functionality to make the node a delegated event container - * @module node - * @submodule node-event-delegate - */ + /** + * A protected initialization method, used by the host class to initialize + * plugin configurations passed the constructor, through the config object. + * + * Host objects should invoke this method at the appropriate time in their + * construction lifecycle. + * + * @method _initConfigPlugins + * @param {Object} config The configuration object passed to the constructor + * @protected + * @for Plugin.Host + */ + PluginHost.prototype._initConfigPlugins = function(config) { -/** - *Sets up a delegation listener for an event occurring inside the Node. - * The delegated event will be verified against a supplied selector or - * filtering function to test if the event references at least one node that - * should trigger the subscription callback.
- * - *Selector string filters will trigger the callback if the event originated - * from a node that matches it or is contained in a node that matches it. - * Function filters are called for each Node up the parent axis to the - * subscribing container node, and receive at each level the Node and the event - * object. The function should return true (or a truthy value) if that Node - * should trigger the subscription callback. Note, it is possible for filters - * to match multiple Nodes for a single event. In this case, the delegate - * callback will be executed for each matching Node.
- * - *For each matching Node, the callback will be executed with its 'this'
- * object set to the Node matched by the filter (unless a specific context was
- * provided during subscription), and the provided event's
- * currentTarget
will also be set to the matching Node. The
- * containing Node from which the subscription was originally made can be
- * referenced as e.container
.
- *
- * @method delegate
- * @param type {String} the event type to delegate
- * @param fn {Function} the callback function to execute. This function
- * will be provided the event object for the delegated event.
- * @param spec {String|Function} a selector that must match the target of the
- * event or a function to test target and its parents for a match
- * @param context {Object} optional argument that specifies what 'this' refers to.
- * @param args* {any} 0..n additional arguments to pass on to the callback function.
- * These arguments will be added after the event object.
- * @return {EventHandle} the detach handle
- * @for Node
- */
-Y.Node.prototype.delegate = function(type) {
+ // Class Configuration
+ var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
+ plug = [],
+ unplug = {},
+ constructor, i, classPlug, classUnplug, pluginClassName;
- var args = Y.Array(arguments, 0, true),
- index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2;
+ // TODO: Room for optimization. Can we apply statically/unplug in same pass?
+ for (i = classes.length - 1; i >= 0; i--) {
+ constructor = classes[i];
- args.splice(index, 0, this._node);
+ classUnplug = constructor._UNPLUG;
+ if (classUnplug) {
+ // subclasses over-write
+ Y.mix(unplug, classUnplug, true);
+ }
- return Y.delegate.apply(Y, args);
-};
+ classPlug = constructor._PLUG;
+ if (classPlug) {
+ // subclasses over-write
+ Y.mix(plug, classPlug, true);
+ }
+ }
+ for (pluginClassName in plug) {
+ if (plug.hasOwnProperty(pluginClassName)) {
+ if (!unplug[pluginClassName]) {
+ this.plug(plug[pluginClassName]);
+ }
+ }
+ }
-}, '3.4.0' ,{requires:['node-base', 'event-delegate']});
+ // User Configuration
+ if (config && config.plugins) {
+ this.plug(config.plugins);
+ }
+ };
+
+ /**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of the class by default).
+ *
+ * @method Plugin.Host.plug
+ * @static
+ *
+ * @param {Function} hostClass The host class on which to register the plugins
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+ PluginHost.plug = function(hostClass, plugin, config) {
+ // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ]
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._PLUG = hostClass._PLUG || {};
+
+ if (!L.isArray(plugin)) {
+ if (config) {
+ plugin = {fn:plugin, cfg:config};
+ }
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l;i++) {
+ p = plugin[i];
+ name = p.NAME || p.fn.NAME;
+ hostClass._PLUG[name] = p;
+ }
+ }
+ };
+ /**
+ * Unregisters any class level plugins which have been registered by the host class, or any
+ * other class in the hierarchy.
+ *
+ * @method Plugin.Host.unplug
+ * @static
+ *
+ * @param {Function} hostClass The host class from which to unregister the plugins
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+ PluginHost.unplug = function(hostClass, plugin) {
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._UNPLUG = hostClass._UNPLUG || {};
+
+ if (!L.isArray(plugin)) {
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l; i++) {
+ p = plugin[i];
+ name = p.NAME;
+ if (!hostClass._PLUG[name]) {
+ hostClass._UNPLUG[name] = p;
+ } else {
+ delete hostClass._PLUG[name];
+ }
+ }
+ }
+ };
-YUI.add('node', function(Y){}, '3.4.0' ,{skinnable:false, use:['node-base', 'node-style', 'node-screen', 'node-pluginhost', 'node-event-delegate']});
+}, '3.4.0' ,{requires:['pluginhost-base']});
YUI.add('event-delegate', function(Y) {
/**
@@ -18417,825 +14744,440 @@ Y.delegate = Y.Event.delegate = delegate;
}, '3.4.0' ,{requires:['node-base']});
-YUI.add('io-base', function(Y) {
-
- /**
- * Base IO functionality. Provides basic XHR transport support.
- * @module io
- * @submodule io-base
- */
-
- /**
- * The io class is a utility that brokers HTTP requests through a simplified
- * interface. Specifically, it allows JavaScript to make HTTP requests to
- * a resource without a page reload. The underlying transport for making
- * same-domain requests is the XMLHttpRequest object. YUI.io can also use
- * Flash, if specified as a transport, for cross-domain requests.
- *
- * @class io
- */
-
- /**
- * @event io:start
- * @description This event is fired by YUI.io when a transaction is initiated.
- * @type Event Custom
- */
- var E_START = 'io:start',
-
- /**
- * @event io:complete
- * @description This event is fired by YUI.io when a transaction is complete.
- * Response status and data are accessible, if available.
- * @type Event Custom
- */
- E_COMPLETE = 'io:complete',
-
- /**
- * @event io:success
- * @description This event is fired by YUI.io when a transaction is complete, and
- * the HTTP status resolves to HTTP2xx.
- * @type Event Custom
- */
- E_SUCCESS = 'io:success',
-
- /**
- * @event io:failure
- * @description This event is fired by YUI.io when a transaction is complete, and
- * the HTTP status resolves to HTTP4xx, 5xx and above.
- * @type Event Custom
- */
- E_FAILURE = 'io:failure',
-
- /**
- * @event io:end
- * @description This event signifies the end of the transaction lifecycle. The
- * transaction transport is destroyed.
- * @type Event Custom
- */
- E_END = 'io:end',
-
- //--------------------------------------
- // Properties
- //--------------------------------------
- /**
- * @description A transaction counter that increments for each transaction.
- *
- * @property transactionId
- * @private
- * @static
- * @type int
- */
- transactionId = 0,
-
- /**
- * @description Object of default HTTP headers to be initialized and sent
- * for all transactions.
- *
- * @property _headers
- * @private
- * @static
- * @type object
- */
- _headers = {
- 'X-Requested-With' : 'XMLHttpRequest'
- },
-
- /**
- * @description Object that stores timeout values for any transaction with
- * a defined "timeout" configuration property.
- *
- * @property _timeout
- * @private
- * @static
- * @type object
- */
- _timeout = {},
-
- // Window reference
- w = Y.config.win;
-
- //--------------------------------------
- // Methods
- //--------------------------------------
-
- /**
- * @description Method that creates the XMLHttpRequest transport
- *
- * @method _xhr
- * @private
- * @static
- * @return object
- */
- function _xhr() {
- return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
- }
-
- /**
- * @description Method that increments _transactionId for each transaction.
- *
- * @method _id
- * @private
- * @static
- * @return int
- */
- function _id() {
- var id = transactionId;
-
- transactionId++;
-
- return id;
- }
-
- /**
- * @description Method that creates a unique transaction object for each
- * request.
- *
- * @method _create
- * @private
- * @static
- * @param {number} c - configuration object subset to determine if
- * the transaction is an XDR or file upload,
- * requiring an alternate transport.
- * @param {number} i - transaction id
- * @return object
- */
- function _create(c, i) {
- var o = {};
- o.id = Y.Lang.isNumber(i) ? i : _id();
- c = c || {};
-
- if (!c.use && !c.upload) {
- o.c = _xhr();
- }
- else if (c.use) {
- if (c.use === 'native') {
- if (w.XDomainRequest) {
- o.c = new XDomainRequest();
- o.t = c.use;
- }
- else {
- o.c = _xhr();
- }
- }
- else {
- o.c = Y.io._transport[c.use];
- o.t = c.use;
- }
- }
- else {
- o.c = {};
- o.t = 'io:iframe';
- }
-
- return o;
- }
-
-
- function _destroy(o) {
- if (w) {
- if (o.c && w.XMLHttpRequest) {
- o.c.onreadystatechange = null;
- }
- else if (Y.UA.ie === 6 && !o.t) {
- // IE, when using XMLHttpRequest as an ActiveX Object, will throw
- // a "Type Mismatch" error if the event handler is set to "null".
- o.c.abort();
- }
- }
-
- o.c = null;
- o = null;
- }
-
- /**
- * @description Method for creating and subscribing transaction events.
- *
- * @method _tE
- * @private
- * @static
- * @param {string} e - event to be published
- * @param {object} c - configuration data subset for event subscription.
- *
- * @return void
- */
- function _tE(e, c) {
- var eT = new Y.EventTarget().publish('transaction:' + e),
- cT = c.context || Y,
- a = c.arguments;
+YUI.add('node-event-delegate', function(Y) {
- if (a) {
- eT.on(c.on[e], cT, a);
- }
- else {
- eT.on(c.on[e], cT);
- }
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
- return eT;
- }
+/**
+ *
Sets up a delegation listener for an event occurring inside the Node. + * The delegated event will be verified against a supplied selector or + * filtering function to test if the event references at least one node that + * should trigger the subscription callback.
+ * + *Selector string filters will trigger the callback if the event originated + * from a node that matches it or is contained in a node that matches it. + * Function filters are called for each Node up the parent axis to the + * subscribing container node, and receive at each level the Node and the event + * object. The function should return true (or a truthy value) if that Node + * should trigger the subscription callback. Note, it is possible for filters + * to match multiple Nodes for a single event. In this case, the delegate + * callback will be executed for each matching Node.
+ * + *For each matching Node, the callback will be executed with its 'this'
+ * object set to the Node matched by the filter (unless a specific context was
+ * provided during subscription), and the provided event's
+ * 4h=y&WOxJEkR5;Xf-aBIMn@;ecurrentTarget
will also be set to the matching Node. The
+ * containing Node from which the subscription was originally made can be
+ * referenced as e.container
.
+ *
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param spec {String|Function} a selector that must match the target of the
+ * event or a function to test target and its parents for a match
+ * @param context {Object} optional argument that specifies what 'this' refers to.
+ * @param args* {any} 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type) {
- /**
- * @description Fires event "io:start" and creates, fires a
- * transaction-specific start event, if config.on.start is
- * defined.
- *
- * @method _ioStart
- * @private
- * @static
- * @param {number} id - transaction id
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioStart(id, c) {
- var a = c.arguments;
+ var args = Y.Array(arguments, 0, true),
+ index = (Y.Lang.isObject(type) && !Y.Lang.isArray(type)) ? 1 : 2;
- if (a) {
- Y.fire(E_START, id, a);
- }
- else {
- Y.fire(E_START, id);
- }
+ args.splice(index, 0, this._node);
- if (c.on && c.on.start) {
- _tE('start', c).fire(id);
- }
- }
+ return Y.delegate.apply(Y, args);
+};
- /**
- * @description Fires event "io:complete" and creates, fires a
- * transaction-specific "complete" event, if config.on.complete is
- * defined.
- *
- * @method _ioComplete
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioComplete(o, c) {
- var r = o.e ? { status: 0, statusText: o.e } : o.c,
- a = c.arguments;
+}, '3.4.0' ,{requires:['node-base', 'event-delegate']});
+YUI.add('node-pluginhost', function(Y) {
- if (a) {
- Y.fire(E_COMPLETE, o.id, r, a);
- }
- else {
- Y.fire(E_COMPLETE, o.id, r);
- }
+/**
+ * @module node
+ * @submodule node-pluginhost
+ */
- if (c.on && c.on.complete) {
- _tE('complete', c).fire(o.id, r);
- }
- }
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method plug
+ * @static
+ * @for Node
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
- /**
- * @description Fires event "io:end" and creates, fires a
- * transaction-specific "end" event, if config.on.end is
- * defined.
- *
- * @method _ioEnd
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioEnd(o, c) {
- var a = c.arguments;
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
- if (a) {
- Y.fire(E_END, o.id, a);
- }
- else {
- Y.fire(E_END, o.id);
- }
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
- if (c.on && c.on.end) {
- _tE('end', c).fire(o.id);
- }
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
- _destroy(o);
- }
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
- /**
- * @description Fires event "io:success" and creates, fires a
- * transaction-specific "success" event, if config.on.success is
- * defined.
- *
- * @method _ioSuccess
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioSuccess(o, c) {
- var a = c.arguments;
- if (a) {
- Y.fire(E_SUCCESS, o.id, o.c, a);
- }
- else {
- Y.fire(E_SUCCESS, o.id, o.c);
- }
+}, '3.4.0' ,{requires:['node-base', 'pluginhost']});
+YUI.add('node-screen', function(Y) {
- if (c.on && c.on.success) {
- _tE('success', c).fire(o.id, o.c);
- }
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
- _ioEnd(o, c);
- }
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
- /**
- * @description Fires event "io:failure" and creates, fires a
- * transaction-specific "failure" event, if config.on.failure is
- * defined.
- *
- * @method _ioFailure
- * @private
- * @static
- * @param {object} o - transaction object.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _ioFailure(o, c) {
- var r = o.e ? { status: 0, statusText: o.e } : o.c,
- a = c.arguments;
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
- if (a) {
- Y.fire(E_FAILURE, o.id, r, a);
- }
- else {
- Y.fire(E_FAILURE, o.id, r);
- }
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
- if (c.on && c.on.failure) {
- _tE('failure', c).fire(o.id, r);
- }
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
- _ioEnd(o, c);
- }
+ /**
+ * Pixel distance the page has been scrolled horizontally
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
- /**
- * @description Resends an XDR transaction, using the Flash tranport,
- * if the native transport fails.
- *
- * @method _resend
- * @private
- * @static
+ /**
+ * Pixel distance the page has been scrolled vertically
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
- * @param {object} o - Transaction object generated by _create().
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- *
- * @return void
- */
- function _resend(o, uri, c, d) {
- _destroy(o);
- c.xdr.use = 'flash';
- // If the original request included serialized form data and
- // additional data are defined in the configuration, it must
- // be reset to prevent data duplication.
- c.data = c.form && d ? d : null;
-
- return Y.io(uri, c, o.id);
+ return Y.DOM[name].apply(this, args);
+ }
+ };
}
+);
- /**
- * @description Method that concatenates string data for HTTP GET transactions.
- *
- * @method _concat
- * @private
- * @static
- * @param {string} s - URI or root data.
- * @param {string} d - data to be concatenated onto URI.
- * @return int
- */
- function _concat(s, d) {
- s += (s.indexOf('?') === -1 ? '?' : '&') + d;
- return s;
- }
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
- /**
- * @description Method that stores default client headers for all transactions.
- * If a label is passed with no value argument, the header will be deleted.
- *
- * @method _setHeader
- * @private
- * @static
- * @param {string} l - HTTP header
- * @param {string} v - HTTP header value
- * @return int
- */
- function _setHeader(l, v) {
- if (v) {
- _headers[l] = v;
- }
- else {
- delete _headers[l];
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
}
}
+};
- /**
- * @description Method that sets all HTTP headers to be sent in a transaction.
- *
- * @method _setHeaders
- * @private
- * @static
- * @param {object} o - XHR instance for the specific transaction.
- * @param {object} h - HTTP headers for the specific transaction, as defined
- * in the configuration object passed to YUI.io().
- * @return void
- */
- function _setHeaders(o, h) {
- var p;
- h = h || {};
-
- for (p in _headers) {
- if (_headers.hasOwnProperty(p)) {
- if (!h[p]) {
- h[p] = _headers[p];
- }
- }
- }
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
- for (p in h) {
- if (h.hasOwnProperty(p)) {
- if (h[p] !== 'disable') {
- o.setRequestHeader(p, h[p]);
- }
- }
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
}
}
+};
- /**
- * @description Terminates a transaction due to an explicit abort or
- * timeout.
- *
- * @method _ioCancel
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {string} s - Identifies timed out or aborted transaction.
- *
- * @return void
- */
- function _ioCancel(o, s) {
- if (o && o.c) {
- o.e = s;
- o.c.abort();
- }
- }
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
- /**
- * @description Starts timeout count if the configuration object
- * has a defined timeout property.
- *
- * @method _startTimeout
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} t - Timeout in milliseconds.
- * @return void
- */
- function _startTimeout(o, t) {
- _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, t);
- }
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
- /**
- * @description Clears the timeout interval started by _startTimeout().
- *
- * @method _clearTimeout
- * @private
- * @static
- * @param {number} id - Transaction id.
- * @return void
- */
- function _clearTimeout(id) {
- w.clearTimeout(_timeout[id]);
- delete _timeout[id];
- }
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
- /**
- * @description Method that determines if a transaction response qualifies
- * as success or failure, based on the response HTTP status code, and
- * fires the appropriate success or failure events.
- *
- * @method _handleResponse
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} c - Configuration object passed to io().
- * @return void
- */
- function _handleResponse(o, c) {
- var status = o.c.status;
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
- // IE reports HTTP 204 as HTTP 1223.
- if (status === 0 && o.c.responseText || status === 1223) {
- status = 200;
- }
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
- if (status >= 200 && status < 300) {
- _ioSuccess(o, c);
- }
- else {
- _ioFailure(o, c);
- }
- }
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY',
- /**
- * @description Event handler bound to onreadystatechange.
- *
- * @method _readyState
- * @private
- * @static
- * @param {object} o - Transaction object generated by _create().
- * @param {object} c - Configuration object passed to YUI.io().
- * @return void
- */
- function _readyState(o, c) {
- if (o.c.readyState === 4) {
- if (c.timeout) {
- _clearTimeout(o.id);
- }
+/**
+ * Swaps the XY position of this node with another node.
+ * @method swapXY
+ * @param {Y.Node || HTMLElement} otherNode The node to swap with.
+ * @chainable
+ */
+ 'swapXY'
+]);
- w.setTimeout(
- function() {
- _ioComplete(o, c);
- _handleResponse(o, c);
- }, 0);
- }
- }
+/**
+ * @module node
+ * @submodule node-screen
+ */
- /**
- * @description Method for requesting a transaction. _io() is implemented as
- * yui.io(). Each transaction may include a configuration object. Its
- * properties are:
- *
- * method: HTTP method verb (e.g., GET or POST). If this property is not
- * not defined, the default value will be GET.
- *
- * data: This is the name-value string that will be sent as the transaction
- * data. If the request is HTTP GET, the data become part of
- * querystring. If HTTP POST, the data are sent in the message body.
- *
- * xdr: Defines the transport to be used for cross-domain requests. By
- * setting this property, the transaction will use the specified
- * transport instead of XMLHttpRequest.
- * The properties are:
- * {
- * use: Specify the transport to be used: 'flash' and 'native'
- * dataType: Set the value to 'XML' if that is the expected
- * response content type.
- * }
- *
- *
- * form: This is a defined object used to process HTML form as data. The
- * properties are:
- * {
- * id: Node object or id of HTML form.
- * useDisabled: Boolean value to allow disabled HTML form field
- * values to be sent as part of the data.
- * }
- *
- * on: This is a defined object used to create and handle specific
- * events during a transaction lifecycle. These events will fire in
- * addition to the global io events. The events are:
- * start - This event is fired when a request is sent to a resource.
- * complete - This event fires when the transaction is complete.
- * success - This event fires when the response status resolves to
- * HTTP 2xx.
- * failure - This event fires when the response status resolves to
- * HTTP 4xx, 5xx; and, for all transaction exceptions,
- * including aborted transactions and transaction timeouts.
- * end - This even is fired at the conclusion of the transaction
- * lifecycle, after a success or failure resolution.
- *
- * The properties are:
- * {
- * start: function(id, arguments){},
- * complete: function(id, responseobject, arguments){},
- * success: function(id, responseobject, arguments){},
- * failure: function(id, responseobject, arguments){},
- * end: function(id, arguments){}
- * }
- * Each property can reference a function or be written as an
- * inline function.
- *
- * sync: To enable synchronous transactions, set the configuration property
- * "sync" to true; the default behavior is false. Synchronous
- * transactions are limited to same-domain requests only.
- *
- * context: Object reference for all defined transaction event handlers
- * when it is implemented as a method of a base object. Defining
- * "context" will set the reference of "this," used in the
- * event handlers, to the context value. In the case where
- * different event handlers all have different contexts,
- * use Y.bind() to set the execution context, bypassing this
- * configuration.
- *
- * headers: This is a defined object of client headers, as many as.
- * desired for the transaction. The object pattern is:
- * { 'header': 'value' }.
- *
- * timeout: This value, defined as milliseconds, is a time threshold for the
- * transaction. When this threshold is reached, and the transaction's
- * Complete event has not yet fired, the transaction will be aborted.
- *
- * arguments: Object, array, string, or number passed to all registered
- * event handlers. This value is available as the second
- * argument in the "start" and "abort" event handlers; and, it is
- * the third argument in the "complete", "success", and "failure"
- * event handlers.
- *
- * @method _io
- * @private
- * @static
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- * @param {number} i - transaction id, if already set.
- * @return object
- */
- function _io(uri, c, i) {
- var f, o, d, m, r, s, oD, a, j, usr, pwd,
- u = uri;
- c = Y.Object(c) || {};
- o = _create(c.xdr || c.form, i);
- usr = c.username || null;
- pwd = c.password || null;
- m = c.method ? c.method = c.method.toUpperCase() : c.method = 'GET';
- s = c.sync;
- oD = c.data;
-
- // Serialize an object into a key-value string using
- // querystring-stringify-simple.
- c.data = (Y.Lang.isObject(c.data) && Y.QueryString) ? Y.QueryString.stringify(c.data) : c.data;
-
- if (c.form) {
- if (c.form.upload) {
- // This is a file upload transaction, calling
- // upload() in io-upload-iframe.
- return Y.io.upload(o, uri, c);
- }
- else {
- // Serialize HTML form data into a key-value string.
- f = Y.io._serialize(c.form, c.data);
- if (m === 'POST' || m === 'PUT') {
- c.data = f;
- }
- else if (m === 'GET') {
- uri = _concat(uri, f);
- }
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = this.getDOMNode(),
+ region;
+
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
}
}
+ if (Y.DOM.isWindow(node)) {
+ region = Y.DOM.viewportRegion(node);
+ } else {
+ region = Y.DOM.region(node);
+ }
+ return region;
+ }
+};
- if (c.data) {
- switch (m) {
- case 'GET':
- case 'HEAD':
- case 'DELETE':
- uri = _concat(uri, c.data);
- c.data = null;
- break;
- case 'POST':
- case 'PUT':
- // If Content-Type is defined in the configuration object, or
- // or as a default header, it will be used instead of
- // 'application/x-www-form-urlencoded; charset=UTF-8'
- c.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, c.headers);
- break;
- }
- }
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
- if (o.t) {
- // Cross-domain request or custom transport detected.
- return Y.io.xdr(uri, o, c);
- }
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
- if (!s) {
- o.c.onreadystatechange = function() { _readyState(o, c); };
- }
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
- try {
- // Determine if request is to be set as
- // synchronous or asynchronous.
- o.c.open(m, uri, s ? false : true, usr, pwd);
- _setHeaders(o.c, c.headers);
- _ioStart(o.id, c);
-
- // Will work only in browsers that implement the
- // Cross-Origin Resource Sharing draft.
- if (c.xdr && c.xdr.credentials) {
- if (!Y.UA.ie) {
- o.c.withCredentials = true;
- }
- }
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (Y.instanceOf(node2, Y.Node)) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
- // Using "null" with HTTP POST will result in a request
- // with no Content-Length header defined.
- o.c.send(c.data || '');
- if (s) {
- // Create a response object for synchronous transactions,
- // merging ID and arguments fields into a single object.
- d = o.c;
- a = ['status', 'statusText', 'responseText', 'responseXML'];
- r = c.arguments ? { id: o.id, arguments: c.arguments } : { id: o.id };
- r.getAllResponseHeaders = function() { return d.getAllResponseHeaders(); };
- r.getResponseHeader = function(h) { return d.getResponseHeader(h); };
+}, '3.4.0' ,{requires:['node-base', 'dom-screen']});
+YUI.add('node-style', function(Y) {
- for (j = 0; j < 4; j++) {
- r[a[j]] = o.c[a[j]];
- }
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
- _ioComplete(o, c);
- _handleResponse(o, c);
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
- return r;
- }
- }
- catch(e) {
- if (c.xdr && c.xdr.use === 'native') {
- // This exception is usually thrown by browsers
- // that do not support XMLHttpRequest Level 2.
- // Retry the request with the XDR transport set
- // to 'flash'. If the Flash transport is not
- // initialized or available, the transaction
- // will resolve to a transport error.
- return _resend(o, u, c, oD);
- }
- else {
- _ioComplete(o, c);
- _handleResponse(o, c);
- }
- }
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
- // If config.timeout is defined, and the request is standard XHR,
- // initialize timeout polling.
- if (c.timeout) {
- _startTimeout(o, c.timeout);
- }
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
- return {
- id: o.id,
- abort: function() {
- return o.c ? _ioCancel(o, 'abort') : false;
- },
- isInProgress: function() {
- return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
- }
- };
- }
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
- _io.start = _ioStart;
- _io.complete = _ioComplete;
- _io.success = _ioSuccess;
- _io.failure = _ioFailure;
- _io.end = _ioEnd;
- _io._id = _id;
- _io._timeout = _timeout;
-
- //--------------------------------------
- // Begin public interface definition
- //--------------------------------------
- /**
- * @description Method that stores default client headers for all transactions.
- * If a label is passed with no value argument, the header will be deleted.
- * This is the interface for _setHeader().
- *
- * @method header
- * @public
- * @static
- * @param {string} l - HTTP header
- * @param {string} v - HTTP header value
- * @return int
- */
- _io.header = _setHeader;
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
- /**
- * @description Method for requesting a transaction. This
- * is the interface for _io().
- *
- * @method io
- * @public
- * @static
- * @param {string} uri - qualified path to transaction resource.
- * @param {object} c - configuration object for the transaction.
- * @return object
- */
- Y.io = _io;
- Y.io.http = _io;
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
-}, '3.4.0' ,{requires:['event-custom-base', 'querystring-stringify-simple']});
+}, '3.4.0' ,{requires:['dom-style', 'node-base']});
YUI.add('querystring-stringify-simple', function(Y) {
/*global Y */
@@ -19297,6 +15239,673 @@ QueryString.stringify = function (obj, c) {
}, '3.4.0' ,{requires:['yui-base']});
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ // Window reference
+ var L = Y.Lang,
+ // List of events that comprise the IO event lifecycle.
+ E = ['start', 'complete', 'end', 'success', 'failure'],
+ // Whitelist of used XHR response object properties.
+ P = ['status', 'statusText', 'responseText', 'responseXML'],
+ aH = 'getAllResponseHeaders',
+ oH = 'getResponseHeader',
+ w = Y.config.win,
+ xhr = w.XMLHttpRequest,
+ xdr = w.XDomainRequest,
+ _i = 0;
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class IO
+ * @constructor
+ * @param {object} c - Object of EventTarget's publish method configurations
+ * used to configure IO's events.
+ */
+ function IO (c) {
+ var io = this;
+
+ io._uid = 'io:' + _i++;
+ io._init(c);
+ Y.io._map[io._uid] = io;
+ }
+
+ IO.prototype = {
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+
+ /**
+ * @description A counter that increments for each transaction.
+ *
+ * @property _id
+ * @private
+ * @type int
+ */
+ _id: 0,
+
+ /**
+ * @description Object of IO HTTP headers sent with each transaction.
+ *
+ * @property _headers
+ * @private
+ * @type object
+ */
+ _headers: {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @type object
+ */
+ _timeout: {},
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+
+ _init: function(c) {
+ var io = this, i;
+
+ io.cfg = c || {};
+
+ Y.augment(io, Y.EventTarget);
+ for (i = 0; i < 5; i++) {
+ // Publish IO global events with configurations, if any.
+ // IO global events are set to broadcast by default.
+ // These events use the "io:" namespace.
+ io.publish('io:' + E[i], Y.merge({ broadcast: 1 }, c));
+ // Publish IO transaction events with configurations, if
+ // any. These events use the "io-trn:" namespace.
+ io.publish('io-trn:' + E[i], c);
+ }
+ },
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ _create: function(c, i) {
+ var io = this,
+ o = { id: L.isNumber(i) ? i : io._id++, uid: io._uid },
+ x = c.xdr,
+ u = x ? x.use : c.form && c.form.upload ? 'iframe' : 'xhr',
+ ie = (x && x.use === 'native' && xdr),
+ t = io._transport;
+
+ switch (u) {
+ case 'native':
+ case 'xhr':
+ o.c = ie ? new xdr() : xhr ? new xhr() : new ActiveXObject('Microsoft.XMLHTTP');
+ o.t = ie ? true : false;
+ break;
+ default:
+ o.c = t ? t[u] : {};
+ o.t = true;
+ }
+
+ return o;
+ },
+
+ _destroy: function(o) {
+ if (w) {
+ if (xhr && o.t === true) {
+ o.c.onreadystatechange = null;
+ }
+ else if (Y.UA.ie) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ o.c.abort();
+ }
+ }
+
+ o.c = null;
+ o = null;
+ },
+
+ /**
+ * @description Method for creating and firing events.
+ *
+ * @method _evt
+ * @private
+ * @param {string} e - event to be published.
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ _evt: function(e, o, c) {
+ var io = this,
+ a = c['arguments'],
+ eF = io.cfg.emitFacade,
+ // Use old-style parameters or use an Event Facade
+ p = eF ? [{ id: o.id, data: o.c, cfg: c, arguments: a }] : [o.id],
+ // IO Global events namespace.
+ gE = "io:" + e,
+ // IO Transaction events namespace.
+ tE = "io-trn:" + e;
+
+ if (!eF) {
+ if (e === E[0] || e === E[2]) {
+ if (a) {
+ p.push(a);
+ }
+ }
+ else {
+ a ? p.push(o.c, a) : p.push(o.c);
+ }
+ }
+
+ p.unshift(gE);
+ io.fire.apply(io, p);
+ if (c.on) {
+ p[0] = tE;
+ io.once(tE, c.on[e], c.context || Y);
+ io.fire.apply(io, p);
+ }
+ },
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method start
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ start: function(o, c) {
+ this._evt(E[0], o, c);
+ },
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method complete
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ complete: function(o, c) {
+ this._evt(E[1], o, c);
+ },
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method end
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ end: function(o, c) {
+ this._evt(E[2], o, c);
+ this._destroy(o);
+ },
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method success
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ success: function(o, c) {
+ this._evt(E[3], o, c);
+ this.end(o, c);
+ },
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method failure
+ * @public
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ failure: function(o, c) {
+ this._evt(E[4], o, c);
+ this.end(o, c);
+ },
+
+ /**
+ * @description Retry an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _retry
+ * @private
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ _retry: function(o, uri, c) {
+ this._destroy(o);
+ c.xdr.use = 'flash';
+ return this.send(uri, c, o.id);
+ },
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ _concat: function(s, d) {
+ s += (s.indexOf('?') === -1 ? '?' : '&') + d;
+ return s;
+ },
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ setHeader: function(l, v) {
+ if (v) {
+ this._headers[l] = v;
+ }
+ else {
+ delete this._headers[l];
+ }
+ },
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ _setHeaders: function(o, h) {
+ h = Y.merge(this._headers, h);
+ Y.Object.each(h, function(v, p) {
+ if (v !== 'disable') {
+ o.setRequestHeader(p, h[p]);
+ }
+ });
+ },
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} t - Timeout in milliseconds.
+ * @return void
+ */
+ _startTimeout: function(o, t) {
+ var io = this;
+ io._timeout[o.id] = w.setTimeout(function() { io._abort(o, 'timeout'); }, t);
+ },
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ _clearTimeout: function(id) {
+ w.clearTimeout(this._timeout[id]);
+ delete this._timeout[id];
+ },
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _result
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ _result: function(o, c) {
+ var s = o.c.status;
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (s >= 200 && s < 300 || s === 1223) {
+ this.success(o, c);
+ }
+ else {
+ this.failure(o, c);
+ }
+ },
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _rS
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ _rS: function(o, c) {
+ var io = this;
+
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ io._clearTimeout(o.id);
+ }
+
+ // Yield in the event of request timeout or abort.
+ w.setTimeout(function() { io.complete(o, c); io._result(o, c); }, 0);
+ }
+ },
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _abort
+ * @private
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ _abort: function(o, s) {
+ if (o && o.c) {
+ o.e = s;
+ o.c.abort();
+ }
+ },
+
+ /**
+ * @description Method for requesting a transaction. send() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest.
+ * The properties are:
+ * {
+ * use: Specify the transport to be used: 'flash' and 'native'
+ * dataType: Set the value to 'XML' if that is the expected
+ * response content type.
+ * }
+ *
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: Node object or id of HTML form.
+ * useDisabled: Boolean value to allow disabled HTML form field
+ * values to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, arguments){},
+ * complete: function(id, responseobject, arguments){},
+ * success: function(id, responseobject, arguments){},
+ * failure: function(id, responseobject, arguments){},
+ * end: function(id, arguments){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * sync: To enable synchronous transactions, set the configuration property
+ * "sync" to true. Synchronous requests are limited to same-domain
+ * requests only.
+ *
+ * context: Object reference for all defined transaction event handlers
+ * when it is implemented as a method of a base object. Defining
+ * "context" will set the reference of "this," used in the
+ * event handlers, to the context value. In the case where
+ * different event handlers all have different contexts,
+ * use Y.bind() to set the execution context, instead.
+ *
+ * headers: This is a defined object of client headers, as many as
+ * desired for this specific transaction. The object pattern is:
+ * { 'header': 'value' }.
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ *
+ * arguments: User-defined data passed to all registered event handlers.
+ * This value is available as the second argument in the "start"
+ * and "end" event handlers. It is the third argument in the
+ * "complete", "success", and "failure" event handlers.
+ *
+ * @method send
+ * @private
+ * @
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set.
+ * @return object
+ */
+ send: function(uri, c, i) {
+ var o, m, r, s, d, io = this,
+ u = uri;
+ c = c ? Y.Object(c) : {};
+ o = io._create(c, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+ s = c.sync;
+ d = c.data;
+
+ // Serialize an object into a key-value string using
+ // querystring-stringify-simple.
+ if (L.isObject(d)) {
+ d = Y.QueryString.stringify(d);
+ }
+
+ if (c.form) {
+ if (c.form.upload) {
+ // This is a file upload transaction, calling
+ // upload() in io-upload-iframe.
+ return io.upload(o, uri, c);
+ }
+ else {
+ // Serialize HTML form data into a key-value string.
+ d = io._serialize(c.form, d);
+ }
+ }
+
+ if (d) {
+ switch (m) {
+ case 'GET':
+ case 'HEAD':
+ case 'DELETE':
+ u = io._concat(u, d);
+ d = '';
+ break;
+ case 'POST':
+ case 'PUT':
+ // If Content-Type is defined in the configuration object, or
+ // or as a default header, it will be used instead of
+ // 'application/x-www-form-urlencoded; charset=UTF-8'
+ c.headers = Y.merge({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, c.headers);
+ break;
+ }
+ }
+
+ if (o.t) {
+ // Cross-domain request or custom transport configured.
+ return io.xdr(u, o, c);
+ }
+
+ if (!s) {
+ o.c.onreadystatechange = function() { io._rS(o, c); };
+ }
+
+ try {
+ // Determine if request is to be set as
+ // synchronous or asynchronous.
+ o.c.open(m, u, s ? false : true, c.username || null, c.password || null);
+ io._setHeaders(o.c, c.headers || {});
+ io.start(o, c);
+
+ // Will work only in browsers that implement the
+ // Cross-Origin Resource Sharing draft.
+ if (c.xdr && c.xdr.credentials) {
+ if (!Y.UA.ie) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ // Using "null" with HTTP POST will result in a request
+ // with no Content-Length header defined.
+ o.c.send(d);
+
+ if (s) {
+ // Create a response object for synchronous transactions,
+ // mixing id and arguments properties with the xhr
+ // properties whitelist.
+ r = Y.mix({ id: o.id, 'arguments': c['arguments'] }, o.c, false, P);
+ r[aH] = function() { return o.c[aH](); };
+ r[oH] = function(h) { return o.c[oH](h); };
+ io.complete(o, c);
+ io._result(o, c);
+
+ return r;
+ }
+ }
+ catch(e) {
+ if (o.t) {
+ // This exception is usually thrown by browsers
+ // that do not support XMLHttpRequest Level 2.
+ // Retry the request with the XDR transport set
+ // to 'flash'. If the Flash transport is not
+ // initialized or available, the transaction
+ // will resolve to a transport error.
+ return io._retry(o, uri, c);
+ }
+ else {
+ io.complete(o, c);
+ io._result(o, c);
+ }
+ }
+
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ io._startTimeout(o, c.timeout);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? io._abort(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ },
+ io: io
+ };
+ }
+ };
+
+ /**
+ * @description Method for requesting a transaction.
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} u - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = function(u, c) {
+ // Calling IO through the static interface will use and reuse
+ // an instance of IO.
+ var o = Y.io._map['io:0'] || new IO();
+ return o.send.apply(o, [u, c]);
+ };
+
+ Y.IO = IO;
+ // Map of all IO instances created.
+ Y.io._map = {};
+
+
+
+}, '3.4.0' ,{requires:['event-custom-base', 'querystring-stringify-simple']});
YUI.add('json-parse', function(Y) {
/**
@@ -19349,8 +15958,7 @@ function fromGlobal(ref) {
* @private
*/
var _JSON = fromGlobal('JSON'),
- // Create an indirect reference to eval to allow for minification
- _eval = fromGlobal('eval'),
+
Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
useNative = !!Native,
@@ -19482,7 +16090,7 @@ var _JSON = fromGlobal('JSON'),
// Eval the text into a JavaScript data structure, apply any
// reviver function, and return
- return _revive( _eval('(' + s + ')'), reviver );
+ return _revive( eval('(' + s + ')'), reviver );
}
throw new SyntaxError('JSON.parse');
@@ -19527,7 +16135,7 @@ Y.JSON.useNativeParse = useNative;
}, '3.4.0' );
-YUI.add('transition-native', function(Y) {
+YUI.add('transition', function(Y) {
/**
* Provides the transition method for Node.
@@ -19537,19 +16145,32 @@ YUI.add('transition-native', function(Y) {
* @requires node-style
*/
-var TRANSITION = '-webkit-transition',
- TRANSITION_CAMEL = 'WebkitTransition',
- TRANSITION_PROPERTY_CAMEL = 'WebkitTransitionProperty',
- TRANSITION_PROPERTY = '-webkit-transition-property',
- TRANSITION_DURATION = '-webkit-transition-duration',
- TRANSITION_TIMING_FUNCTION = '-webkit-transition-timing-function',
- TRANSITION_DELAY = '-webkit-transition-delay',
- TRANSITION_END = 'webkitTransitionEnd',
- ON_TRANSITION_END = 'onwebkittransitionend',
- TRANSFORM_CAMEL = 'WebkitTransform',
+var CAMEL_VENDOR_PREFIX = '',
+ VENDOR_PREFIX = '',
+ DOCUMENT = Y.config.doc,
+ DOCUMENT_ELEMENT = 'documentElement',
+ TRANSITION = 'transition',
+ TRANSITION_CAMEL = 'Transition',
+ TRANSITION_PROPERTY_CAMEL,
+ TRANSITION_PROPERTY,
+ TRANSITION_DURATION,
+ TRANSITION_TIMING_FUNCTION,
+ TRANSITION_DELAY,
+ TRANSITION_END,
+ ON_TRANSITION_END,
+ TRANSFORM_CAMEL,
EMPTY_OBJ = {},
+ VENDORS = [
+ 'Webkit',
+ 'Moz'
+ ],
+
+ VENDOR_TRANSITION_END = {
+ Webkit: 'webkitTransitionEnd'
+ },
+
/**
* A class for constructing transition instances.
* Adds the "transition" method to Node.
@@ -19561,11 +16182,6 @@ Transition = function() {
this.init.apply(this, arguments);
};
-Transition.fx = {};
-Transition.toggles = {};
-
-Transition._hasEnd = {};
-
Transition._toCamel = function(property) {
property = property.replace(/-([a-z])/gi, function(m0, m1) {
return m1.toUpperCase();
@@ -19576,11 +16192,7 @@ Transition._toCamel = function(property) {
Transition._toHyphen = function(property) {
property = property.replace(/([A-Z]?)([a-z]+)([A-Z]?)/g, function(m0, m1, m2, m3) {
- var str = '';
- if (m1) {
- str += '-' + m1.toLowerCase();
- }
- str += m2;
+ var str = ((m1) ? '-' + m1.toLowerCase() : '') + m2;
if (m3) {
str += '-' + m3.toLowerCase();
@@ -19592,15 +16204,42 @@ Transition._toHyphen = function(property) {
return property;
};
-
-Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
+Transition.SHOW_TRANSITION = 'fadeIn';
+Transition.HIDE_TRANSITION = 'fadeOut';
Transition.useNative = false;
-if (TRANSITION in Y.config.doc.documentElement.style) {
- Transition.useNative = true;
- Transition.supported = true; // TODO: remove
-}
+Y.Array.each(VENDORS, function(val) { // then vendor specific
+ var property = val + TRANSITION_CAMEL;
+ if (property in DOCUMENT[DOCUMENT_ELEMENT].style) {
+ CAMEL_VENDOR_PREFIX = val;
+ VENDOR_PREFIX = Transition._toHyphen(val) + '-';
+
+ Transition.useNative = true;
+ Transition.supported = true; // TODO: remove
+ Transition._VENDOR_PREFIX = val;
+ }
+});
+
+TRANSITION_CAMEL = CAMEL_VENDOR_PREFIX + TRANSITION_CAMEL;
+TRANSITION_PROPERTY_CAMEL = CAMEL_VENDOR_PREFIX + 'TransitionProperty';
+TRANSITION_PROPERTY = VENDOR_PREFIX + 'transition-property';
+TRANSITION_DURATION = VENDOR_PREFIX + 'transition-duration';
+TRANSITION_TIMING_FUNCTION = VENDOR_PREFIX + 'transition-timing-function';
+TRANSITION_DELAY = VENDOR_PREFIX + 'transition-delay';
+TRANSITION_END = 'transitionend';
+ON_TRANSITION_END = 'on' + CAMEL_VENDOR_PREFIX.toLowerCase() + 'transitionend';
+
+TRANSITION_END = VENDOR_TRANSITION_END[CAMEL_VENDOR_PREFIX] || TRANSITION_END;
+
+TRANSFORM_CAMEL = CAMEL_VENDOR_PREFIX + 'Transform';
+
+Transition.fx = {};
+Transition.toggles = {};
+
+Transition._hasEnd = {};
+
+Transition._reKeywords = /^(?:node|duration|iterations|easing|delay|on|onstart|onend)$/i;
Y.Node.DOM_EVENTS[TRANSITION_END] = 1;
@@ -19793,7 +16432,7 @@ Transition.prototype = {
computed = getComputedStyle(node),
attrs = Transition._nodeAttrs[uid],
cssText = '',
- cssTransition = computed[TRANSITION_PROPERTY],
+ cssTransition = computed[Transition._toCamel(TRANSITION_PROPERTY)],
transitionText = TRANSITION_PROPERTY + ': ',
duration = TRANSITION_DURATION + ': ',
@@ -19806,9 +16445,9 @@ Transition.prototype = {
// preserve existing transitions
if (cssTransition !== 'all') {
transitionText += cssTransition + ',';
- duration += computed[TRANSITION_DURATION] + ',';
- easing += computed[TRANSITION_TIMING_FUNCTION] + ',';
- delay += computed[TRANSITION_DELAY] + ',';
+ duration += computed[Transition._toCamel(TRANSITION_DURATION)] + ',';
+ easing += computed[Transition._toCamel(TRANSITION_TIMING_FUNCTION)] + ',';
+ delay += computed[Transition._toCamel(TRANSITION_DELAY)] + ',';
}
@@ -19816,7 +16455,7 @@ Transition.prototype = {
for (name in attrs) {
hyphy = Transition._toHyphen(name);
attr = attrs[name];
- if (attrs.hasOwnProperty(name) && attr.transition === anim) {
+ if ((attr = attrs[name]) && attr.transition === anim) {
if (name in node.style) { // only native styles allowed
duration += anim._prepDur(attr.duration) + ',';
delay += anim._prepDur(attr.delay) + ',';
@@ -19839,7 +16478,7 @@ Transition.prototype = {
if (!Transition._hasEnd[uid]) {
//anim._detach = Y.on(TRANSITION_END, anim._onNativeEnd, node);
//node[ON_TRANSITION_END] = anim._onNativeEnd;
- node.addEventListener(TRANSITION_END, anim._onNativeEnd, false);
+ node.addEventListener(TRANSITION_END, anim._onNativeEnd, '');
Transition._hasEnd[uid] = true;
}
@@ -19889,7 +16528,7 @@ Transition.prototype = {
_endNative: function(name) {
var node = this._node,
- value = node.ownerDocument.defaultView.getComputedStyle(node, '')[TRANSITION_PROPERTY];
+ value = node.ownerDocument.defaultView.getComputedStyle(node, '')[Transition._toCamel(TRANSITION_PROPERTY)];
if (typeof value === 'string') {
value = value.replace(new RegExp('(?:^|,\\s)' + name + ',?'), ',');
@@ -19935,15 +16574,18 @@ Transition.prototype = {
},
destroy: function() {
- var anim = this;
+ var anim = this,
+ node = anim._node;
/*
if (anim._detach) {
anim._detach.detach();
}
*/
//anim._node[ON_TRANSITION_END] = null;
- node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
- anim._node = null;
+ if (node) {
+ node.removeEventListener(TRANSITION_END, anim._onNativeEnd, false);
+ anim._node = null;
+ }
}
};
@@ -20027,7 +16669,7 @@ Y.Node.prototype.show = function(name, config, callback) {
callback = config;
config = name;
}
- name = this.SHOW_TRANSITION;
+ name = Transition.SHOW_TRANSITION;
}
this.transition(name, config, callback);
}
@@ -20058,7 +16700,7 @@ Y.Node.prototype.hide = function(name, config, callback) {
callback = config;
config = name;
}
- name = this.HIDE_TRANSITION;
+ name = Transition.HIDE_TRANSITION;
}
this.transition(name, config, callback);
} else {
@@ -20203,352 +16845,488 @@ Transition.DEFAULT_TOGGLE = 'fade';
-}, '3.4.0' ,{requires:['node-base']});
-YUI.add('transition-timer', function(Y) {
-
-/*
-* The Transition Utility provides an API for creating advanced transitions.
-* @module transition
-*/
+}, '3.4.0' ,{requires:['node-style']});
+YUI.add('selector-css2', function(Y) {
-/*
-* Provides the base Transition class, for animating numeric properties.
-*
-* @module transition
-* @submodule transition-timer
-*/
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
-var Transition = Y.Transition;
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ _reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
-Y.mix(Transition.prototype, {
- _start: function() {
- if (Transition.useNative) {
- this._runNative();
- } else {
- this._runTimer();
- }
- },
+ return ret || [];
+ },
- _runTimer: function() {
- var anim = this;
- anim._initAttrs();
+ _re: {
+ attr: /(\[[^\]]*\])/g,
+ esc: /\\[:\[\]\(\)#\.\'\>+~"]/gi,
+ pseudos: /(\([^\)]*\))/g
+ },
- Transition._running[Y.stamp(anim)] = anim;
- anim._startTime = new Date();
- Transition._startTimer();
- },
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z0-9]+[-\\w\\uE000]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w\\uE000]*)': '[className~=$1]'
+ },
- _endTimer: function() {
- var anim = this;
- delete Transition._running[Y.stamp(anim)];
- anim._startTime = null;
- },
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
- _runFrame: function() {
- var t = new Date() - this._startTime;
- this._runAttrs(t);
- },
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
- _runAttrs: function(time) {
- var anim = this,
- node = anim._node,
- config = anim._config,
- uid = Y.stamp(node),
- attrs = Transition._nodeAttrs[uid],
- customAttr = Transition.behaviors,
- done = false,
- allDone = false,
- data,
- name,
- attribute,
- setter,
- elapsed,
- delay,
- d,
- t,
- i;
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ child,
+ id,
+ className,
+ tagName;
- for (name in attrs) {
- attribute = attrs[name];
- if ((attribute && attribute.transition === anim)) {
- d = attribute.duration;
- delay = attribute.delay;
- elapsed = (time - delay) / 1000;
- t = time;
- data = {
- type: 'propertyEnd',
- propertyName: name,
- config: config,
- elapsedTime: elapsed
- };
- setter = (i in customAttr && 'set' in customAttr[i]) ?
- customAttr[i].set : Transition.DEFAULT_SETTER;
+ // if we have an initial ID, set to root when in document
+ /*
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+ */
- done = (t >= d);
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
+ // try ID first, unless no root.all && root not in document
+ // (root.all works off document, but not getElementById)
+ // TODO: move to allById?
+ if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) {
+ nodes = Y.DOM.allById(id, root);
+ // try className
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else { // default to tagName
+ nodes = root.getElementsByTagName(tagName);
+ }
- if (t > d) {
- t = d;
+ } else { // brute getElementsByTagName('*')
+ child = root.firstChild;
+ while (child) {
+ if (child.tagName) { // only collect HTMLElements
+ nodes.push(child);
+ }
+ child = child.nextSilbing || child.firstChild;
+ }
+ }
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
}
+ }
- if (!delay || time >= delay) {
- setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
- attribute.easing, attribute.unit);
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
- if (done) {
- delete attrs[name];
- anim._count--;
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
+ operator.test && !operator.test(value)) || // regex test
+ (!operator.test && // protect against RegExp as function (webkit)
+ typeof operator === 'function' && !operator(tmpNode, test[0], test[2]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
- if (config[name] && config[name].on && config[name].on.end) {
- config[name].on.end.call(Y.one(node), data);
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
}
- //node.fire('transition:propertyEnd', data);
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
- if (!allDone && anim._count <= 0) {
- allDone = true;
- anim._end(elapsed);
- anim._endTimer();
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
}
+ break;
}
}
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
}
- }
- },
+ },
- _initAttrs: function() {
- var anim = this,
- customAttr = Transition.behaviors,
- uid = Y.stamp(anim._node),
- attrs = Transition._nodeAttrs[uid],
- attribute,
- duration,
- delay,
- easing,
- val,
- name,
- mTo,
- mFrom,
- unit, begin, end;
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\uE003(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\uE004'"]*)['"]?\uE004/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Selector.operators,
+ escVal = (match[3]) ? match[3].replace(/\\/g, '') : '',
+ test;
- for (name in attrs) {
- attribute = attrs[name];
- if (attrs.hasOwnProperty(name) && (attribute && attribute.transition === anim)) {
- duration = attribute.duration * 1000;
- delay = attribute.delay * 1000;
- easing = attribute.easing;
- val = attribute.value;
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ Y.config.doc.documentElement.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
- // only allow supported properties
- if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
- begin = (name in customAttr && 'get' in customAttr[name]) ?
- customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
- mFrom = Transition.RE_UNITS.exec(begin);
- mTo = Transition.RE_UNITS.exec(val);
+ match[3] = escVal;
- begin = mFrom ? mFrom[1] : begin;
- end = mTo ? mTo[1] : val;
- unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+ // escape all but ID for prefilter, which may run through QSA (via Dom.allById)
+ token[match[1]] = (match[1] === 'id') ? match[3] : escVal;
- if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
- unit = Transition.DEFAULT_UNIT;
}
- if (typeof easing === 'string') {
- if (easing.indexOf('cubic-bezier') > -1) {
- easing = easing.substring(13, easing.length - 1).split(',');
- } else if (Transition.easings[easing]) {
- easing = Transition.easings[easing];
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ match[3] = escVal.replace(Selector._reRegExpTokens, '\\$1');
+ test = new RegExp(test.replace('{val}', match[3]));
}
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\uE005['"]?([^\uE005]*)['"]?\uE006)*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array and unescape special chars for tests
+ if (match[2]) {
+ match[2] = match[2].replace(/\\/g, '');
+ }
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
}
-
- attribute.from = Number(begin);
- attribute.to = Number(end);
- attribute.unit = unit;
- attribute.easing = easing;
- attribute.duration = duration + delay;
- attribute.delay = delay;
- } else {
- delete attrs[name];
- anim._count--;
}
}
- }
- },
-
- destroy: function() {
- this.detachAll();
- this._node = null;
- }
-}, true);
-
-Y.mix(Y.Transition, {
- _runtimeAttrs: {},
- /*
- * Regex of properties that should use the default unit.
- *
- * @property RE_DEFAULT_UNIT
- * @static
- */
- RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
-
- /*
- * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
- *
- * @property DEFAULT_UNIT
- * @static
- */
- DEFAULT_UNIT: 'px',
-
- /*
- * Time in milliseconds passed to setInterval for frame processing
- *
- * @property intervalTime
- * @default 20
- * @static
- */
- intervalTime: 20,
+ ],
- /*
- * Bucket for custom getters and setters
- *
- * @property behaviors
- * @static
- */
- behaviors: {
- left: {
- get: function(anim, attr) {
- return Y.DOM._getAttrOffset(anim._node, attr);
- }
- }
- },
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
- /*
- * The default setter to use when setting object properties.
- *
- * @property DEFAULT_SETTER
- * @static
- */
- DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
- from = Number(from);
- to = Number(to);
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
- var node = anim._node,
- val = Transition.cubicBezier(fn, elapsed / duration);
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
- val = from + val[0] * (to - from);
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser.name !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
- if (node) {
- if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
- unit = unit || '';
- Y.DOM.setStyle(node, att, val + unit);
- }
- } else {
- anim._end();
- }
- },
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
- /*
- * The default getter to use when getting object properties.
- *
- * @property DEFAULT_GETTER
- * @static
- */
- DEFAULT_GETTER: function(anim, att) {
- var node = anim._node,
- val = '';
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
- if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
- val = Y.DOM.getComputedStyle(node, att);
- }
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
- return val;
- },
+ if (!found || selector.length) { // not fully parsed
+ tokens = [];
+ }
+ return tokens;
+ },
- _startTimer: function() {
- if (!Transition._timer) {
- Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
- }
- },
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ esc = selector.match(Selector._re.esc), // pull escaped colon, brackets, etc.
+ attrs,
+ pseudos,
+ re, i, len;
- _stopTimer: function() {
- clearInterval(Transition._timer);
- Transition._timer = null;
- },
+ if (esc) {
+ selector = selector.replace(Selector._re.esc, '\uE000');
+ }
- /*
- * Called per Interval to handle each animation frame.
- * @method _runFrame
- * @private
- * @static
- */
- _runFrame: function() {
- var done = true,
- anim;
- for (anim in Transition._running) {
- if (Transition._running[anim]._runFrame) {
- done = false;
- Transition._running[anim]._runFrame();
+ attrs = selector.match(Selector._re.attr);
+ pseudos = selector.match(Selector._re.pseudos);
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '\uE001');
}
- }
- if (done) {
- Transition._stopTimer();
- }
- },
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '\uE002');
+ }
- cubicBezier: function(p, t) {
- var x0 = 0,
- y0 = 0,
- x1 = p[0],
- y1 = p[1],
- x2 = p[2],
- y2 = p[3],
- x3 = 1,
- y3 = 0,
- A = x3 - 3 * x2 + 3 * x1 - x0,
- B = 3 * x2 - 6 * x1 + 3 * x0,
- C = 3 * x1 - 3 * x0,
- D = x0,
- E = y3 - 3 * y2 + 3 * y1 - y0,
- F = 3 * y2 - 6 * y1 + 3 * y0,
- G = 3 * y1 - 3 * y0,
- H = y0,
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(new RegExp(re, 'gi'), shorthand[re]);
+ }
+ }
- x = (((A*t) + B)*t + C)*t + D,
- y = (((E*t) + F)*t + G)*t + H;
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace(/\uE001/, attrs[i]);
+ }
+ }
- return [x, y];
- },
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace(/\uE002/, pseudos[i]);
+ }
+ }
- easings: {
- ease: [0.25, 0, 1, 0.25],
- linear: [0, 0, 1, 1],
- 'ease-in': [0.42, 0, 1, 1],
- 'ease-out': [0, 0, 0.58, 1],
- 'ease-in-out': [0.42, 0, 0.58, 1]
- },
+ selector = selector.replace(/\[/g, '\uE003');
+ selector = selector.replace(/\]/g, '\uE004');
- _running: {},
- _timer: null,
+ selector = selector.replace(/\(/g, '\uE005');
+ selector = selector.replace(/\)/g, '\uE006');
- RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
-}, true);
+ if (esc) {
+ for (i = 0, len = esc.length; i < len; ++i) {
+ selector = selector.replace('\uE000', esc[i]);
+ }
+ }
-Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
+ return selector;
+ },
-Y.Transition = Transition;
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
-}, '3.4.0' ,{requires:['transition-native', 'node-style']});
+// IE wants class with native queries
+if (Y.Selector.useNative && Y.config.doc.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
-YUI.add('transition', function(Y){}, '3.4.0' ,{use:['transition-native', 'transition-timer']});
+}, '3.4.0' ,{requires:['selector-native']});
YUI.add('selector-css3', function(Y) {
/**
@@ -20701,309 +17479,559 @@ Y.Selector.combinators['~'] = {
}, '3.4.0' ,{requires:['selector-native', 'selector-css2']});
-YUI.add('dom-style-ie', function(Y) {
+YUI.add('yui-log', function(Y) {
-(function(Y) {
-var HAS_LAYOUT = 'hasLayout',
- PX = 'px',
- FILTER = 'filter',
- FILTERS = 'filters',
- OPACITY = 'opacity',
- AUTO = 'auto',
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations. This module is a `core` YUI module, it's documentation is located under the YUI class.
+ *
+ * @module yui
+ * @submodule yui-log
+ */
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1,
+ info: 1,
+ warn: 1,
+ error: 1 };
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt).
+ * @param {String} src The source of the the message (opt).
+ * @param {boolean} silent If true, the log event won't fire.
+ * @return {YUI} YUI instance.
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var bail, excl, incl, m, f,
+ Y = INSTANCE,
+ c = Y.config,
+ publisher = (Y.fire) ? Y : YUI.Env.globalEvents;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (incl && (src in incl)) {
+ bail = !incl[src];
+ } else if (excl && (src in excl)) {
+ bail = excl[src];
+ }
+ }
+ if (!bail) {
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (Y.Lang.isFunction(c.logFn)) {
+ c.logFn.call(Y, msg, cat, src);
+ } else if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (publisher && !silent) {
+
+ if (publisher == Y && (!publisher.getEvent(LOGEVENT))) {
+ publisher.publish(LOGEVENT, {
+ broadcast: 2
+ });
+ }
+
+ publisher.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt).
+ * @param {String} src The source of the the message (opt).
+ * @param {boolean} silent If true, the log event won't fire.
+ * @return {YUI} YUI instance.
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+
+}, '3.4.0' ,{requires:['yui-base']});
+YUI.add('dump', function(Y) {
+
+/**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * If included, the dump method is added to the YUI instance.
+ *
+ * @module dump
+ */
+
+ var L = Y.Lang,
+ OBJ = '{...}',
+ FUN = 'f(){...}',
+ COMMA = ', ',
+ ARROW = ' => ',
+
+ /**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed.
+ *
+ * @method dump
+ * @param {Object} o The object to dump.
+ * @param {Number} d How deep to recurse child objects, default 3.
+ * @return {String} the dump result.
+ * @for YUI
+ */
+ dump = function(o, d) {
+ var i, len, s = [], type = L.type(o);
+
+ // Cast non-objects to string
+ // Skip dates because the std toString is what we want
+ // Skip HTMLElement-like objects because trying to dump
+ // an element will cause an unhandled exception in FF 2.x
+ if (!L.isObject(o)) {
+ return o + '';
+ } else if (type == 'date') {
+ return o;
+ } else if (o.nodeType && o.tagName) {
+ return o.tagName + '#' + o.id;
+ } else if (o.document && o.navigator) {
+ return 'window';
+ } else if (o.location && o.body) {
+ return 'document';
+ } else if (type == 'function') {
+ return FUN;
+ }
+
+ // dig into child objects the depth specifed. Default 3
+ d = (L.isNumber(d)) ? d : 3;
+
+ // arrays [1, 2, 3]
+ if (type == 'array') {
+ s.push('[');
+ for (i = 0, len = o.length; i < len; i = i + 1) {
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push(']');
+ // regexp /foo/
+ } else if (type == 'regexp') {
+ s.push(o.toString());
+ // objects {k1 => v1, k2 => v2}
+ } else {
+ s.push('{');
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ try {
+ s.push(i + ARROW);
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d - 1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ } catch (e) {
+ s.push('Error: ' + e.message);
+ }
+ }
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push('}');
+ }
- BORDER_WIDTH = 'borderWidth',
- BORDER_TOP_WIDTH = 'borderTopWidth',
- BORDER_RIGHT_WIDTH = 'borderRightWidth',
- BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
- BORDER_LEFT_WIDTH = 'borderLeftWidth',
- WIDTH = 'width',
- HEIGHT = 'height',
- TRANSPARENT = 'transparent',
- VISIBLE = 'visible',
- GET_COMPUTED_STYLE = 'getComputedStyle',
- UNDEFINED = undefined,
- documentElement = Y.config.doc.documentElement,
+ return s.join('');
+ };
- testFeature = Y.Features.test,
- addFeature = Y.Features.add,
+ Y.dump = dump;
+ L.dump = dump;
- // TODO: unit-less lineHeight (e.g. 1.22)
- re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
- isIE8 = (Y.UA.ie >= 8),
- _getStyleObj = function(node) {
- return node.currentStyle || node.style;
- },
+}, '3.4.0' );
+YUI.add('transition-timer', function(Y) {
- ComputedStyle = {
- CUSTOM_STYLES: {},
+/*
+* The Transition Utility provides an API for creating advanced transitions.
+* @module transition
+*/
- get: function(el, property) {
- var value = '',
- current;
+/*
+* Provides the base Transition class, for animating numeric properties.
+*
+* @module transition
+* @submodule transition-timer
+*/
- if (el) {
- current = _getStyleObj(el)[property];
- if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
- value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
- } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
- value = current;
- } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
- value = Y.DOM.IE.COMPUTED[property](el, property);
- } else if (re_unit.test(current)) { // convert to pixel
- value = ComputedStyle.getPixel(el, property) + PX;
- } else {
- value = current;
- }
- }
+var Transition = Y.Transition;
- return value;
- },
+Y.mix(Transition.prototype, {
+ _start: function() {
+ if (Transition.useNative) {
+ this._runNative();
+ } else {
+ this._runTimer();
+ }
+ },
- sizeOffsets: {
- width: ['Left', 'Right'],
- height: ['Top', 'Bottom'],
- top: ['Top'],
- bottom: ['Bottom']
- },
+ _runTimer: function() {
+ var anim = this;
+ anim._initAttrs();
- getOffset: function(el, prop) {
- var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
- capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
- offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
- pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
- sizeOffsets = ComputedStyle.sizeOffsets[prop],
- mode = el.ownerDocument.compatMode,
- value = '';
+ Transition._running[Y.stamp(anim)] = anim;
+ anim._startTime = new Date();
+ Transition._startTimer();
+ },
- // IE pixelWidth incorrect for percent
- // manually compute by subtracting padding and border from offset size
- // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
- // reverting to auto from auto causes position stacking issues (old impl)
- if (current === AUTO || current.indexOf('%') > -1) {
- value = el['offset' + capped];
+ _endTimer: function() {
+ var anim = this;
+ delete Transition._running[Y.stamp(anim)];
+ anim._startTime = null;
+ },
- if (mode !== 'BackCompat') {
- if (sizeOffsets[0]) {
- value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
- value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
- }
+ _runFrame: function() {
+ var t = new Date() - this._startTime;
+ this._runAttrs(t);
+ },
- if (sizeOffsets[1]) {
- value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
- value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
- }
- }
+ _runAttrs: function(time) {
+ var anim = this,
+ node = anim._node,
+ config = anim._config,
+ uid = Y.stamp(node),
+ attrs = Transition._nodeAttrs[uid],
+ customAttr = Transition.behaviors,
+ done = false,
+ allDone = false,
+ data,
+ name,
+ attribute,
+ setter,
+ elapsed,
+ delay,
+ d,
+ t,
+ i;
- } else { // use style.pixelWidth, etc. to convert to pixels
- // need to map style.width to currentStyle (no currentStyle.pixelWidth)
- if (!el.style[pixel] && !el.style[prop]) {
- el.style[prop] = current;
- }
- value = el.style[pixel];
-
- }
- return value + PX;
- },
+ for (name in attrs) {
+ if ((attribute = attrs[name]) && attribute.transition === anim) {
+ d = attribute.duration;
+ delay = attribute.delay;
+ elapsed = (time - delay) / 1000;
+ t = time;
+ data = {
+ type: 'propertyEnd',
+ propertyName: name,
+ config: config,
+ elapsedTime: elapsed
+ };
- borderMap: {
- thin: (isIE8) ? '1px' : '2px',
- medium: (isIE8) ? '3px': '4px',
- thick: (isIE8) ? '5px' : '6px'
- },
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Transition.DEFAULT_SETTER;
- getBorderWidth: function(el, property, omitUnit) {
- var unit = omitUnit ? '' : PX,
- current = el.currentStyle[property];
+ done = (t >= d);
- if (current.indexOf(PX) < 0) { // look up keywords if a border exists
- if (ComputedStyle.borderMap[current] &&
- el.currentStyle.borderStyle !== 'none') {
- current = ComputedStyle.borderMap[current];
- } else { // otherwise no border (default is "medium")
- current = 0;
+ if (t > d) {
+ t = d;
}
- }
- return (omitUnit) ? parseFloat(current) : current;
- },
- getPixel: function(node, att) {
- // use pixelRight to convert to px
- var val = null,
- style = _getStyleObj(node),
- styleRight = style.right,
- current = style[att];
+ if (!delay || time >= delay) {
+ setter(anim, name, attribute.from, attribute.to, t - delay, d - delay,
+ attribute.easing, attribute.unit);
- node.style.right = current;
- val = node.style.pixelRight;
- node.style.right = styleRight; // revert
+ if (done) {
+ delete attrs[name];
+ anim._count--;
- return val;
- },
+ if (config[name] && config[name].on && config[name].on.end) {
+ config[name].on.end.call(Y.one(node), data);
+ }
- getMargin: function(node, att) {
- var val,
- style = _getStyleObj(node);
+ //node.fire('transition:propertyEnd', data);
- if (style[att] == AUTO) {
- val = 0;
- } else {
- val = ComputedStyle.getPixel(node, att);
- }
- return val + PX;
- },
+ if (!allDone && anim._count <= 0) {
+ allDone = true;
+ anim._end(elapsed);
+ anim._endTimer();
+ }
+ }
+ }
- getVisibility: function(node, att) {
- var current;
- while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
- node = node.parentNode;
}
- return (current) ? current[att] : VISIBLE;
- },
+ }
+ },
- getColor: function(node, att) {
- var current = _getStyleObj(node)[att];
+ _initAttrs: function() {
+ var anim = this,
+ customAttr = Transition.behaviors,
+ uid = Y.stamp(anim._node),
+ attrs = Transition._nodeAttrs[uid],
+ attribute,
+ duration,
+ delay,
+ easing,
+ val,
+ name,
+ mTo,
+ mFrom,
+ unit, begin, end;
- if (!current || current === TRANSPARENT) {
- Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
- current = _getStyleObj(parent)[att];
- if (current && current !== TRANSPARENT) {
- node = parent;
- return true;
+ for (name in attrs) {
+ if ((attribute = attrs[name]) && attribute.transition === anim) {
+ duration = attribute.duration * 1000;
+ delay = attribute.delay * 1000;
+ easing = attribute.easing;
+ val = attribute.value;
+
+ // only allow supported properties
+ if (name in anim._node.style || name in Y.DOM.CUSTOM_STYLES) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(anim, name) : Transition.DEFAULT_GETTER(anim, name);
+
+ mFrom = Transition.RE_UNITS.exec(begin);
+ mTo = Transition.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Transition.RE_DEFAULT_UNIT.test(name)) {
+ unit = Transition.DEFAULT_UNIT;
}
- });
- }
- return Y.Color.toRGB(current);
- },
+ if (typeof easing === 'string') {
+ if (easing.indexOf('cubic-bezier') > -1) {
+ easing = easing.substring(13, easing.length - 1).split(',');
+ } else if (Transition.easings[easing]) {
+ easing = Transition.easings[easing];
+ }
+ }
- getBorderColor: function(node, att) {
- var current = _getStyleObj(node),
- val = current[att] || current.color;
- return Y.Color.toRGB(Y.Color.toHex(val));
+ attribute.from = Number(begin);
+ attribute.to = Number(end);
+ attribute.unit = unit;
+ attribute.easing = easing;
+ attribute.duration = duration + delay;
+ attribute.delay = delay;
+ } else {
+ delete attrs[name];
+ anim._count--;
+ }
+ }
}
},
- //fontSize: getPixelFont,
- IEComputed = {};
-
-addFeature('style', 'computedStyle', {
- test: function() {
- return 'getComputedStyle' in Y.config.win;
+ destroy: function() {
+ this.detachAll();
+ this._node = null;
}
-});
+}, true);
-addFeature('style', 'opacity', {
- test: function() {
- return 'opacity' in documentElement.style;
- }
-});
+Y.mix(Y.Transition, {
+ _runtimeAttrs: {},
+ /*
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ RE_DEFAULT_UNIT: /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i,
-addFeature('style', 'filter', {
- test: function() {
- return 'filters' in documentElement;
- }
-});
+ /*
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ DEFAULT_UNIT: 'px',
-// use alpha filter for IE opacity
-if (!testFeature('style', 'opacity') && testFeature('style', 'filter')) {
- Y.DOM.CUSTOM_STYLES[OPACITY] = {
- get: function(node) {
- var val = 100;
- try { // will error if no DXImageTransform
- val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+ /*
+ * Time in milliseconds passed to setInterval for frame processing
+ *
+ * @property intervalTime
+ * @default 20
+ * @static
+ */
+ intervalTime: 20,
- } catch(e) {
- try { // make sure its in the document
- val = node[FILTERS]('alpha')[OPACITY];
- } catch(err) {
- }
+ /*
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ behaviors: {
+ left: {
+ get: function(anim, attr) {
+ return Y.DOM._getAttrOffset(anim._node, attr);
}
- return val / 100;
- },
+ }
+ },
- set: function(node, val, style) {
- var current,
- styleObj = _getStyleObj(node),
- currentFilter = styleObj[FILTER];
+ /*
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ DEFAULT_SETTER: function(anim, att, from, to, elapsed, duration, fn, unit) {
+ from = Number(from);
+ to = Number(to);
- style = style || node.style;
- if (val === '') { // normalize inline style behavior
- current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
- val = current;
+ var node = anim._node,
+ val = Transition.cubicBezier(fn, elapsed / duration);
+
+ val = from + val[0] * (to - from);
+
+ if (node) {
+ if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
+ unit = unit || '';
+ Y.DOM.setStyle(node, att, val + unit);
}
+ } else {
+ anim._end();
+ }
+ },
- if (typeof currentFilter == 'string') { // in case not appended
- style[FILTER] = currentFilter.replace(/alpha([^)]*\))/gi, '') +
- ((val < 1) ? 'alpha(' + OPACITY + '=' + val * 100 + ')' : '');
+ /*
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ DEFAULT_GETTER: function(anim, att) {
+ var node = anim._node,
+ val = '';
- if (!style[FILTER]) {
- style.removeAttribute(FILTER);
- }
+ if (att in node.style || att in Y.DOM.CUSTOM_STYLES) {
+ val = Y.DOM.getComputedStyle(node, att);
+ }
- if (!styleObj[HAS_LAYOUT]) {
- style.zoom = 1; // needs layout
- }
- }
+ return val;
+ },
+
+ _startTimer: function() {
+ if (!Transition._timer) {
+ Transition._timer = setInterval(Transition._runFrame, Transition.intervalTime);
}
- };
-}
+ },
-try {
- Y.config.doc.createElement('div').style.height = '-1px';
-} catch(e) { // IE throws error on invalid style set; trap common cases
- Y.DOM.CUSTOM_STYLES.height = {
- set: function(node, val, style) {
- var floatVal = parseFloat(val);
- if (floatVal >= 0 || val === 'auto' || val === '') {
- style.height = val;
- } else {
+ _stopTimer: function() {
+ clearInterval(Transition._timer);
+ Transition._timer = null;
+ },
+
+ /*
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ _runFrame: function() {
+ var done = true,
+ anim;
+ for (anim in Transition._running) {
+ if (Transition._running[anim]._runFrame) {
+ done = false;
+ Transition._running[anim]._runFrame();
}
}
- };
- Y.DOM.CUSTOM_STYLES.width = {
- set: function(node, val, style) {
- var floatVal = parseFloat(val);
- if (floatVal >= 0 || val === 'auto' || val === '') {
- style.width = val;
- } else {
- }
+ if (done) {
+ Transition._stopTimer();
}
- };
-}
+ },
-if (!testFeature('style', 'computedStyle')) {
- // TODO: top, right, bottom, left
- IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+ cubicBezier: function(p, t) {
+ var x0 = 0,
+ y0 = 0,
+ x1 = p[0],
+ y1 = p[1],
+ x2 = p[2],
+ y2 = p[3],
+ x3 = 1,
+ y3 = 0,
- IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+ A = x3 - 3 * x2 + 3 * x1 - x0,
+ B = 3 * x2 - 6 * x1 + 3 * x0,
+ C = 3 * x1 - 3 * x0,
+ D = x0,
+ E = y3 - 3 * y2 + 3 * y1 - y0,
+ F = 3 * y2 - 6 * y1 + 3 * y0,
+ G = 3 * y1 - 3 * y0,
+ H = y0,
- IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
- IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
- ComputedStyle.getBorderWidth;
+ x = (((A*t) + B)*t + C)*t + D,
+ y = (((E*t) + F)*t + G)*t + H;
- IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
- IEComputed.marginLeft = ComputedStyle.getMargin;
+ return [x, y];
+ },
- IEComputed.visibility = ComputedStyle.getVisibility;
- IEComputed.borderColor = IEComputed.borderTopColor =
- IEComputed.borderRightColor = IEComputed.borderBottomColor =
- IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+ easings: {
+ ease: [0.25, 0, 1, 0.25],
+ linear: [0, 0, 1, 1],
+ 'ease-in': [0.42, 0, 1, 1],
+ 'ease-out': [0, 0, 0.58, 1],
+ 'ease-in-out': [0.42, 0, 0.58, 1]
+ },
- Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+ _running: {},
+ _timer: null,
- Y.namespace('DOM.IE');
- Y.DOM.IE.COMPUTED = IEComputed;
- Y.DOM.IE.ComputedStyle = ComputedStyle;
-}
+ RE_UNITS: /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/
+}, true);
-})(Y);
+Transition.behaviors.top = Transition.behaviors.bottom = Transition.behaviors.right = Transition.behaviors.left;
+
+Y.Transition = Transition;
-}, '3.4.0' ,{requires:['dom-style']});
+}, '3.4.0' ,{requires:['transition']});
YUI.add('simpleyui', function(Y) {
// empty
diff --git a/build/slider-base/assets/skins/audio-light/rail-x.png b/build/slider-base/assets/skins/audio-light/rail-x.png
new file mode 100644
index 0000000000000000000000000000000000000000..4cd29b45b9a6e36b8da640f7f56dc78e21b70411
GIT binary patch
literal 3668
zcmV-a4y*BrP)