mirror of
https://github.com/sstent/node.git
synced 2026-01-27 07:33:13 +00:00
updated app
This commit is contained in:
385
first-project/node_modules/derby/lib/Dom.js
generated
vendored
Normal file
385
first-project/node_modules/derby/lib/Dom.js
generated
vendored
Normal file
@@ -0,0 +1,385 @@
|
||||
var racer = require('racer')
|
||||
, domShim = require('dom-shim')
|
||||
, EventDispatcher = require('./EventDispatcher')
|
||||
, escapeHtml = require('html-util').escapeHtml
|
||||
, merge = racer.util.merge
|
||||
, win = window
|
||||
, doc = document
|
||||
, markers = {}
|
||||
, elements = {
|
||||
$_win: win
|
||||
, $_doc: doc
|
||||
}
|
||||
, addListener, removeListener;
|
||||
|
||||
module.exports = Dom;
|
||||
|
||||
function Dom(model) {
|
||||
var dom = this
|
||||
, fns = this.fns
|
||||
|
||||
// Map dom event name -> true
|
||||
, listenerAdded = {}
|
||||
, captureListenerAdded = {};
|
||||
|
||||
|
||||
// DOM listener capturing allows blur and focus to be delegated
|
||||
// http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
|
||||
|
||||
var events = this._events = new EventDispatcher({
|
||||
onTrigger: onTrigger
|
||||
, onBind: function(name, listener, eventName) {
|
||||
if (!listenerAdded[eventName]) {
|
||||
addListener(doc, eventName, trigger, true);
|
||||
listenerAdded[eventName] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var captureEvents = this._captureEvents = new EventDispatcher({
|
||||
onTrigger: function(name, listener, e) {
|
||||
var el = doc.getElementById(id)
|
||||
, id = listener.id;
|
||||
if (el.tagName === 'HTML' || el.contains(e.target)) {
|
||||
onTrigger(name, listener, id, e, el);
|
||||
}
|
||||
}
|
||||
, onBind: function(name, listener) {
|
||||
if (!captureListenerAdded[name]) {
|
||||
addListener(doc, name, captureTrigger, true);
|
||||
captureListenerAdded[name] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function onTrigger(name, listener, id, e, el, next) {
|
||||
var delay = listener.delay
|
||||
, finish = listener.fn;
|
||||
|
||||
if (!finish) {
|
||||
// Update the model when the element's value changes
|
||||
finish = function() {
|
||||
var value = dom.getMethods[listener.method](el, listener.property)
|
||||
, setValue = listener.setValue;
|
||||
|
||||
// Allow the listener to override the setting function
|
||||
if (setValue) {
|
||||
setValue(model, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove this listener if its path id is no longer registered
|
||||
var path = model.__pathMap.paths[listener.pathId];
|
||||
if (!path) return false;
|
||||
|
||||
// Set the value if changed
|
||||
if (model.get(path) === value) return;
|
||||
model.pass(e).set(path, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (delay != null) {
|
||||
setTimeout(finish, delay, e, el, next, dom);
|
||||
} else {
|
||||
finish(e, el, next, dom);
|
||||
}
|
||||
}
|
||||
|
||||
function trigger(e, el, noBubble, continued) {
|
||||
if (!el) el = e.target;
|
||||
var prefix = e.type + ':'
|
||||
, id;
|
||||
|
||||
// Next can be called from a listener to continue bubbling
|
||||
function next() {
|
||||
trigger(e, el.parentNode, false, true);
|
||||
}
|
||||
next.firstTrigger = !continued;
|
||||
if (noBubble && (id = el.id)) {
|
||||
return events.trigger(prefix + id, id, e, el, next);
|
||||
}
|
||||
while (true) {
|
||||
while (!(id = el.id)) {
|
||||
if (!(el = el.parentNode)) return;
|
||||
}
|
||||
// Stop bubbling once the event is handled
|
||||
if (events.trigger(prefix + id, id, e, el, next)) return;
|
||||
if (!(el = el.parentNode)) return;
|
||||
}
|
||||
}
|
||||
|
||||
function captureTrigger(e) {
|
||||
captureEvents.trigger(e.type, e);
|
||||
}
|
||||
|
||||
this.trigger = trigger;
|
||||
this.captureTrigger = captureTrigger;
|
||||
this.addListener = addListener;
|
||||
this.removeListener = removeListener;
|
||||
|
||||
this._componentListeners = [];
|
||||
}
|
||||
|
||||
Dom.prototype = {
|
||||
clear: function() {
|
||||
this._events.clear();
|
||||
this._captureEvents.clear();
|
||||
var listeners = this._componentListeners
|
||||
, i, listener;
|
||||
for (i = listeners.length; i--;) {
|
||||
listener = listeners[i];
|
||||
removeListener(listener[0], listener[1], listener[2], listener[3]);
|
||||
}
|
||||
this._componentListeners = [];
|
||||
markers = {};
|
||||
}
|
||||
|
||||
, bind: function(eventName, id, listener) {
|
||||
if (listener.capture) {
|
||||
listener.id = id;
|
||||
this._captureEvents.bind(eventName, listener);
|
||||
} else {
|
||||
this._events.bind("" + eventName + ":" + id, listener, eventName);
|
||||
}
|
||||
}
|
||||
|
||||
, update: function(el, method, ignore, value, property, index) {
|
||||
// Don't do anything if the element is already up to date
|
||||
if (value === this.getMethods[method](el, property)) return;
|
||||
this.setMethods[method](el, ignore, value, property, index);
|
||||
}
|
||||
|
||||
, item: function(id) {
|
||||
return doc.getElementById(id) || elements[id] || getRange(id);
|
||||
}
|
||||
|
||||
, componentDom: function() {
|
||||
var componentListeners = this._componentListeners
|
||||
, dom = Object.create(this);
|
||||
dom.addListener = function(el, name, callback, captures) {
|
||||
componentListeners.push(arguments);
|
||||
addListener(el, name, callback, captures);
|
||||
};
|
||||
return dom;
|
||||
}
|
||||
|
||||
, getMethods: {
|
||||
attr: getAttr
|
||||
, prop: getProp
|
||||
, propPolite: getProp
|
||||
, html: getHtml
|
||||
// These methods return NaN, because it never equals anything else. Thus,
|
||||
// when compared against the new value, the new value will always be set
|
||||
, append: getNaN
|
||||
, insert: getNaN
|
||||
, remove: getNaN
|
||||
, move: getNaN
|
||||
}
|
||||
|
||||
, setMethods: {
|
||||
attr: setAttr
|
||||
, prop: setProp
|
||||
, propPolite: setProp
|
||||
, html: setHtml
|
||||
, append: setAppend
|
||||
, insert: setInsert
|
||||
, remove: setRemove
|
||||
, move: setMove
|
||||
}
|
||||
|
||||
, fns: {
|
||||
$forChildren: forChildren
|
||||
, $forName: forName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getAttr(el, attr) {
|
||||
return el.getAttribute(attr);
|
||||
}
|
||||
function getProp(el, prop) {
|
||||
return el[prop];
|
||||
}
|
||||
function getHtml(el) {
|
||||
return el.innerHTML;
|
||||
}
|
||||
function getNaN() {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
function setAttr(el, ignore, value, attr) {
|
||||
if (ignore && el.id === ignore) return;
|
||||
el.setAttribute(attr, value);
|
||||
}
|
||||
function setProp(el, ignore, value, prop) {
|
||||
if (ignore && el.id === ignore) return;
|
||||
el[prop] = value;
|
||||
}
|
||||
function propPolite(el, ignore, value, prop) {
|
||||
if (ignore && el.id === ignore) return;
|
||||
if (el !== doc.activeElement || !doc.hasFocus()) {
|
||||
el[prop] = value;
|
||||
}
|
||||
}
|
||||
function setHtml(obj, ignore, value, escape) {
|
||||
if (escape) value = escapeHtml(value);
|
||||
if (obj.nodeType) {
|
||||
// Element
|
||||
if (ignore && obj.id === ignore) return;
|
||||
obj.innerHTML = value;
|
||||
} else {
|
||||
// Range
|
||||
obj.deleteContents();
|
||||
obj.insertNode(obj.createContextualFragment(value));
|
||||
}
|
||||
}
|
||||
function setAppend(obj, ignore, value, escape) {
|
||||
if (escape) value = escapeHtml(value);
|
||||
if (obj.nodeType) {
|
||||
// Element
|
||||
obj.insertAdjacentHTML('beforeend', value);
|
||||
} else {
|
||||
// Range
|
||||
var el = obj.endContainer
|
||||
, ref = el.childNodes[obj.endOffset];
|
||||
el.insertBefore(obj.createContextualFragment(value), ref);
|
||||
}
|
||||
}
|
||||
function setInsert(obj, ignore, value, escape, index) {
|
||||
if (escape) value = escapeHtml(value);
|
||||
if (obj.nodeType) {
|
||||
// Element
|
||||
if (ref = obj.childNodes[index]) {
|
||||
ref.insertAdjacentHTML('beforebegin', value);
|
||||
} else {
|
||||
obj.insertAdjacentHTML('beforeend', value);
|
||||
}
|
||||
} else {
|
||||
// Range
|
||||
var el = obj.startContainer
|
||||
, ref = el.childNodes[obj.startOffset + index];
|
||||
el.insertBefore(obj.createContextualFragment(value), ref);
|
||||
}
|
||||
}
|
||||
function setRemove(el, ignore, index) {
|
||||
if (!el.nodeType) {
|
||||
// Range
|
||||
index += el.startOffset;
|
||||
el = el.startContainer;
|
||||
}
|
||||
var child = el.childNodes[index];
|
||||
if (child) el.removeChild(child);
|
||||
}
|
||||
function setMove(el, ignore, from, to, howMany) {
|
||||
var child, fragment, nextChild, offset, ref, toEl;
|
||||
if (!el.nodeType) {
|
||||
offset = el.startOffset;
|
||||
from += offset;
|
||||
to += offset;
|
||||
el = el.startContainer;
|
||||
}
|
||||
child = el.childNodes[from];
|
||||
|
||||
// Don't move if the item at the destination is passed as the ignore
|
||||
// option, since this indicates the intended item was already moved
|
||||
// Also don't move if the child to move matches the ignore option
|
||||
if (!child || ignore && (toEl = el.childNodes[to]) &&
|
||||
toEl.id === ignore || child.id === ignore) return;
|
||||
|
||||
ref = el.childNodes[to > from ? to + howMany : to];
|
||||
if (howMany > 1) {
|
||||
fragment = document.createDocumentFragment();
|
||||
while (howMany--) {
|
||||
nextChild = child.nextSibling;
|
||||
fragment.appendChild(child);
|
||||
if (!(child = nextChild)) break;
|
||||
}
|
||||
el.insertBefore(fragment, ref);
|
||||
return;
|
||||
}
|
||||
el.insertBefore(child, ref);
|
||||
}
|
||||
|
||||
function forChildren(e, el, next, dom) {
|
||||
// Prevent infinte emission
|
||||
if (!next.firstTrigger) return;
|
||||
|
||||
// Re-trigger the event on all child elements
|
||||
var children = el.childNodes;
|
||||
for (var i = 0, len = children.length, child; i < len; i++) {
|
||||
child = children[i];
|
||||
if (child.nodeType !== 1) continue; // Node.ELEMENT_NODE
|
||||
dom.trigger(e, child, true, true);
|
||||
forChildren(e, child, next, dom);
|
||||
}
|
||||
}
|
||||
|
||||
function forName(e, el, next, dom) {
|
||||
// Prevent infinte emission
|
||||
if (!next.firstTrigger) return;
|
||||
|
||||
var name = el.getAttribute('name');
|
||||
if (!name) return;
|
||||
|
||||
// Re-trigger the event on all other elements with
|
||||
// the same 'name' attribute
|
||||
var elements = doc.getElementsByName(name)
|
||||
, len = elements.length;
|
||||
if (!(len > 1)) return;
|
||||
for (var i = 0, element; i < len; i++) {
|
||||
element = elements[i];
|
||||
if (element === el) continue;
|
||||
dom.trigger(e, element, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
function getRange(name) {
|
||||
var start = markers[name]
|
||||
, end = markers['$' + name]
|
||||
, comment, commentIterator, range;
|
||||
|
||||
if (!(start && end)) {
|
||||
// NodeFilter.SHOW_COMMENT == 128
|
||||
commentIterator = doc.createTreeWalker(doc.body, 128, null, false);
|
||||
while (comment = commentIterator.nextNode()) {
|
||||
markers[comment.data] = comment;
|
||||
}
|
||||
start = markers[name];
|
||||
end = markers['$' + name];
|
||||
if (!(start && end)) return;
|
||||
}
|
||||
|
||||
// Comment nodes may continue to exist even if they have been removed from
|
||||
// the page. Thus, make sure they are still somewhere in the page body
|
||||
if (!doc.body.contains(start)) {
|
||||
delete markers[name];
|
||||
delete markers['$' + name];
|
||||
return;
|
||||
}
|
||||
range = doc.createRange();
|
||||
range.setStartAfter(start);
|
||||
range.setEndBefore(end);
|
||||
return range;
|
||||
}
|
||||
|
||||
if (doc.addEventListener) {
|
||||
addListener = function(el, name, callback, captures) {
|
||||
el.addEventListener(name, callback, captures || false);
|
||||
};
|
||||
removeListener = function(el, name, callback, captures) {
|
||||
el.removeEventListener(name, callback, captures || false);
|
||||
};
|
||||
|
||||
} else if (doc.attachEvent) {
|
||||
addListener = function(el, name, callback) {
|
||||
function listener() {
|
||||
if (!event.target) event.target = event.srcElement;
|
||||
callback(event);
|
||||
}
|
||||
callback.$derbyListener = listener;
|
||||
el.attachEvent('on' + name, listener);
|
||||
};
|
||||
removeListener = function(el, name, callback) {
|
||||
el.detachEvent('on' + name, callback.$derbyListener);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user