mirror of
https://github.com/sstent/node.git
synced 2026-01-27 23:51:45 +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);
|
||||
};
|
||||
}
|
||||
43
first-project/node_modules/derby/lib/EventDispatcher.js
generated
vendored
Normal file
43
first-project/node_modules/derby/lib/EventDispatcher.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
function empty() {}
|
||||
|
||||
module.exports = EventDispatcher;
|
||||
|
||||
function EventDispatcher(options) {
|
||||
if (options == null) options = {};
|
||||
this._onTrigger = options.onTrigger || empty;
|
||||
this._onBind = options.onBind || empty;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
EventDispatcher.prototype = {
|
||||
clear: function() {
|
||||
this.names = {};
|
||||
}
|
||||
|
||||
, bind: function(name, listener, arg0) {
|
||||
this._onBind(name, listener, arg0);
|
||||
var names = this.names
|
||||
, obj = names[name] || {};
|
||||
obj[JSON.stringify(listener)] = listener;
|
||||
return names[name] = obj;
|
||||
}
|
||||
|
||||
, trigger: function(name, value, arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
var names = this.names
|
||||
, listeners = names[name]
|
||||
, onTrigger = this._onTrigger
|
||||
, count = 0
|
||||
, key, listener;
|
||||
for (key in listeners) {
|
||||
listener = listeners[key];
|
||||
count++;
|
||||
if (false !== onTrigger(name, listener, value, arg0, arg1, arg2, arg3, arg4, arg5)) {
|
||||
continue;
|
||||
}
|
||||
delete listeners[key];
|
||||
count--;
|
||||
}
|
||||
if (!count) delete names[name];
|
||||
return count;
|
||||
}
|
||||
}
|
||||
153
first-project/node_modules/derby/lib/PathMap.js
generated
vendored
Normal file
153
first-project/node_modules/derby/lib/PathMap.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
module.exports = PathMap
|
||||
|
||||
function PathMap() {
|
||||
this.clear();
|
||||
}
|
||||
PathMap.prototype = {
|
||||
clear: function() {
|
||||
this.count = 0;
|
||||
this.ids = {};
|
||||
this.paths = {};
|
||||
this.arrays = {};
|
||||
}
|
||||
|
||||
, id: function(path) {
|
||||
var id;
|
||||
// Return the path for an id, or create a new id and index it
|
||||
return this.ids[path] || (
|
||||
id = ++this.count
|
||||
, this.paths[id] = path
|
||||
, this._indexArray(path, id)
|
||||
, this.ids[path] = id
|
||||
);
|
||||
}
|
||||
|
||||
, _indexArray: function(path, id) {
|
||||
var arr, index, match, nested, remainder, set, setArrays;
|
||||
while (match = /^(.+)\.(\d+)(\*?(?:\..+|$))/.exec(path)) {
|
||||
path = match[1];
|
||||
index = +match[2];
|
||||
remainder = match[3];
|
||||
arr = this.arrays[path] || (this.arrays[path] = []);
|
||||
set = arr[index] || (arr[index] = {});
|
||||
if (nested) {
|
||||
setArrays = set.arrays || (set.arrays = {});
|
||||
setArrays[remainder] = true;
|
||||
} else {
|
||||
set[id] = remainder;
|
||||
}
|
||||
nested = true;
|
||||
}
|
||||
}
|
||||
|
||||
, _incrItems: function(path, map, start, end, byNum, oldArrays, oldPath) {
|
||||
var arrayMap, arrayPath, arrayPathTo, i, id, ids, itemPath, remainder;
|
||||
if (oldArrays == null) oldArrays = {};
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
ids = map[i];
|
||||
if (!ids) continue;
|
||||
|
||||
for (id in ids) {
|
||||
remainder = ids[id];
|
||||
if (id === 'arrays') {
|
||||
for (remainder in ids[id]) {
|
||||
arrayPath = (oldPath || path) + '.' + i + remainder;
|
||||
arrayMap = oldArrays[arrayPath] || this.arrays[arrayPath];
|
||||
if (arrayMap) {
|
||||
arrayPathTo = path + '.' + (i + byNum) + remainder;
|
||||
this.arrays[arrayPathTo] = arrayMap;
|
||||
this._incrItems(arrayPathTo, arrayMap, 0, arrayMap.length, 0, oldArrays, arrayPath);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
itemPath = path + '.' + (i + byNum) + remainder;
|
||||
this.paths[id] = itemPath;
|
||||
this.ids[itemPath] = +id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, _delItems: function(path, map, start, end, len, oldArrays) {
|
||||
var arrayLen, arrayMap, arrayPath, i, id, ids, itemPath, remainder;
|
||||
if (oldArrays == null) oldArrays = {};
|
||||
|
||||
for (i = start; i < len; i++) {
|
||||
ids = map[i];
|
||||
if (!ids) continue;
|
||||
|
||||
for (id in ids) {
|
||||
if (id === 'arrays') {
|
||||
for (remainder in ids[id]) {
|
||||
arrayPath = path + '.' + i + remainder;
|
||||
if (arrayMap = this.arrays[arrayPath]) {
|
||||
arrayLen = arrayMap.length;
|
||||
this._delItems(arrayPath, arrayMap, 0, arrayLen, arrayLen, oldArrays);
|
||||
oldArrays[arrayPath] = arrayMap;
|
||||
delete this.arrays[arrayPath];
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
itemPath = this.paths[id];
|
||||
delete this.ids[itemPath];
|
||||
if (i > end) continue;
|
||||
delete this.paths[id];
|
||||
}
|
||||
}
|
||||
|
||||
return oldArrays;
|
||||
}
|
||||
|
||||
, onRemove: function(path, start, howMany) {
|
||||
var map = this.arrays[path]
|
||||
, end, len, oldArrays;
|
||||
if (!map) return;
|
||||
end = start + howMany;
|
||||
len = map.length;
|
||||
// Delete indicies for removed items
|
||||
oldArrays = this._delItems(path, map, start, end + 1, len);
|
||||
// Decrement indicies of later items
|
||||
this._incrItems(path, map, end, len, -howMany, oldArrays);
|
||||
map.splice(start, howMany);
|
||||
}
|
||||
|
||||
, onInsert: function(path, start, howMany) {
|
||||
var map = this.arrays[path]
|
||||
, end, len, oldArrays;
|
||||
if (!map) return;
|
||||
end = start + howMany;
|
||||
len = map.length;
|
||||
// Delete indicies for items in inserted positions
|
||||
oldArrays = this._delItems(path, map, start, end + 1, len);
|
||||
// Increment indicies of later items
|
||||
this._incrItems(path, map, start, len, howMany, oldArrays);
|
||||
while (howMany--) {
|
||||
map.splice(start, 0, {});
|
||||
}
|
||||
}
|
||||
|
||||
, onMove: function(path, from, to, howMany) {
|
||||
var map = this.arrays[path]
|
||||
, afterFrom, afterTo, items, oldArrays;
|
||||
if (!map) return;
|
||||
afterFrom = from + howMany;
|
||||
afterTo = to + howMany;
|
||||
// Adjust paths for items between from and to
|
||||
if (from > to) {
|
||||
oldArrays = this._delItems(path, map, to, afterFrom, afterFrom);
|
||||
this._incrItems(path, map, to, from, howMany, oldArrays);
|
||||
} else {
|
||||
oldArrays = this._delItems(path, map, from, afterTo, afterTo);
|
||||
this._incrItems(path, map, afterFrom, afterTo, -howMany, oldArrays);
|
||||
}
|
||||
// Adjust paths for the moved item(s)
|
||||
this._incrItems(path, map, from, afterFrom, to - from, oldArrays);
|
||||
// Fix the array index
|
||||
items = map.splice(from, howMany);
|
||||
map.splice.apply(map, [to, 0].concat(items));
|
||||
}
|
||||
}
|
||||
1125
first-project/node_modules/derby/lib/View.js
generated
vendored
Normal file
1125
first-project/node_modules/derby/lib/View.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
322
first-project/node_modules/derby/lib/View.server.js
generated
vendored
Normal file
322
first-project/node_modules/derby/lib/View.server.js
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
var EventDispatcher = require('./EventDispatcher')
|
||||
, racer = require('racer')
|
||||
, Promise = racer.util.Promise
|
||||
, isProduction = racer.util.isProduction
|
||||
, merge = racer.util.merge
|
||||
, finishAfter = racer.util.async.finishAfter
|
||||
, Model = racer["protected"].Model
|
||||
, uglify = require('racer/node_modules/uglify-js')
|
||||
, files = require('./files')
|
||||
, htmlUtil = require('html-util')
|
||||
, escapeHtml = htmlUtil.escapeHtml
|
||||
, trimLeading = htmlUtil.trimLeading
|
||||
, refresh = require('./refresh.server')
|
||||
, errorHtml = refresh.errorHtml
|
||||
, cssError = refresh.cssError
|
||||
, templateError = refresh.templateError
|
||||
, View = module.exports = require('./View')
|
||||
, emptyModel = new Model
|
||||
, emptyRes = {
|
||||
getHeader: empty
|
||||
, setHeader: empty
|
||||
, write: empty
|
||||
, end: empty
|
||||
}
|
||||
, emptyPathMap = {
|
||||
id: empty
|
||||
}
|
||||
, emptyEventDispatcher = {
|
||||
bind: empty
|
||||
}
|
||||
|
||||
emptyModel._commit = empty;
|
||||
emptyModel.bundle = empty;
|
||||
|
||||
function empty() {}
|
||||
|
||||
function escapeInlineScript(s) {
|
||||
return s.replace(/<\//g, '<\\/');
|
||||
}
|
||||
|
||||
function loadTemplatesScript(requirePath, templates, instances, libraryData) {
|
||||
return '(function() {\n' +
|
||||
'var view = require("' + requirePath + '").view;\n' +
|
||||
'view._makeAll(\n' +
|
||||
JSON.stringify(templates, null, 2) + ', ' +
|
||||
JSON.stringify(instances, null, 2) + ');\n' +
|
||||
'view._makeComponents(\n' +
|
||||
JSON.stringify(libraryData, null, 2) + ');\n' +
|
||||
'})();';
|
||||
}
|
||||
|
||||
View.prototype.isServer = true;
|
||||
|
||||
View.prototype.inline = function(fn) {
|
||||
return this._inline += uglify("(" + fn + ")()") + ';';
|
||||
};
|
||||
|
||||
View.prototype._load = function(isStatic, callback) {
|
||||
var view = this
|
||||
, appFilename, clientName, count, errors, finish, js, options
|
||||
, promise, requirePath, root, libraries, fileInfo, loadTemplates;
|
||||
|
||||
if (isProduction) {
|
||||
this._watch = false;
|
||||
this._load = function(isStatic, callback) {
|
||||
callback();
|
||||
};
|
||||
} else {
|
||||
this._watch = true;
|
||||
}
|
||||
|
||||
// Use a promise to avoid simultaneously loading multiple times
|
||||
if (promise = this._loadPromise) {
|
||||
return promise.on(callback);
|
||||
}
|
||||
promise = this._loadPromise = (new Promise).on(callback);
|
||||
|
||||
// Once loading is complete, make the files reload from disk the next time
|
||||
promise.on(function() {
|
||||
delete view._loadPromise;
|
||||
});
|
||||
|
||||
errors = {};
|
||||
|
||||
if (isStatic) {
|
||||
root = this._root;
|
||||
clientName = this._clientName;
|
||||
count = 2;
|
||||
finish = function() {
|
||||
if (--count) return;
|
||||
promise.resolve();
|
||||
};
|
||||
|
||||
} else {
|
||||
appFilename = this._appFilename;
|
||||
options = this._derbyOptions || {};
|
||||
fileInfo = files.parseName(appFilename, options);
|
||||
this._root = root = fileInfo.root;
|
||||
this._requirePath = requirePath = fileInfo.require;
|
||||
this._clientName = clientName = fileInfo.clientName;
|
||||
if (!clientName) promise.resolve();
|
||||
|
||||
count = 3;
|
||||
finish = function() {
|
||||
if (--count) return;
|
||||
|
||||
// Templates are appended to the js bundle here so that it does
|
||||
// not have to be regenerated if only the template files are modified
|
||||
if (isProduction) loadTemplates = uglify(loadTemplates);
|
||||
js += ';' + loadTemplates;
|
||||
|
||||
view._errors = errorHtml(errors) || '';
|
||||
|
||||
files.writeJs(root, js, options, function(err, jsFile, appHash) {
|
||||
if (err) throw err;
|
||||
view._jsFile = jsFile;
|
||||
view._appHash = appHash;
|
||||
promise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
if (this._js) {
|
||||
js = this._js;
|
||||
finish();
|
||||
|
||||
} else {
|
||||
files.js(appFilename, function(err, value, inline) {
|
||||
if (err) throw err;
|
||||
js = value;
|
||||
if (!isProduction) view._js = value;
|
||||
if (inline) view.inline("function(){" + inline + "}");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._loadCss(root, clientName, function(err, css) {
|
||||
if (err) {
|
||||
css = '<style id=$_css></style>';
|
||||
errors['CSS'] = cssError(err);
|
||||
} else {
|
||||
css = css ? '<style id=$_css>' + css + '</style>' : '';
|
||||
}
|
||||
view._css = css;
|
||||
finish();
|
||||
});
|
||||
|
||||
libraries = this._libraries;
|
||||
this._loadTemplates(root, clientName, function(err, templates, instances, libraryData) {
|
||||
if (err) errors['Template'] = templateError(err);
|
||||
loadTemplates = loadTemplatesScript(requirePath, templates, instances, libraryData);
|
||||
view._makeAll(templates, instances);
|
||||
view._makeComponents(libraryData);
|
||||
finish();
|
||||
});
|
||||
};
|
||||
|
||||
View.prototype._loadCss = function(root, clientName, callback) {
|
||||
files.css(root, clientName, isProduction, function(err, value) {
|
||||
value = isProduction ? trimLeading(value) : '\n' + value;
|
||||
callback(err, value);
|
||||
});
|
||||
};
|
||||
|
||||
View.prototype._loadTemplates = function(root, clientName, callback) {
|
||||
var count = 1
|
||||
, libraries = this._libraries
|
||||
, libraryData = {}
|
||||
, templates, instances, finish, libraryName, library
|
||||
|
||||
for (libraryName in libraries) count++;
|
||||
finish = finishAfter(count, function(err) {
|
||||
callback(err, templates, instances, libraryData);
|
||||
});
|
||||
|
||||
files.templates(root, clientName, function(err, _templates, _instances) {
|
||||
if (err) {
|
||||
templates = {};
|
||||
instances = {};
|
||||
} else {
|
||||
templates = _templates;
|
||||
instances = _instances;
|
||||
}
|
||||
finish(err);
|
||||
});
|
||||
|
||||
for (libraryName in libraries) {
|
||||
library = libraries[libraryName];
|
||||
files.library(library.root, function(err, components) {
|
||||
if (err) return finish(err);
|
||||
var libraryTemplates = {}
|
||||
, libraryInstances = {}
|
||||
, componentName, component;
|
||||
for (componentName in components) {
|
||||
component = components[componentName];
|
||||
// TODO: Namespace component partials of each component
|
||||
merge(libraryTemplates, component.templates);
|
||||
merge(libraryInstances, component.instances);
|
||||
}
|
||||
libraryData[libraryName] = {
|
||||
templates: libraryTemplates
|
||||
, instances: libraryInstances
|
||||
};
|
||||
finish();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
View.prototype.render = function(res) {
|
||||
var view = this
|
||||
, i, arg, ctx, isStatic, model, ns;
|
||||
if (res == null) res = emptyRes;
|
||||
for (i = 1; i <= 5; i++) {
|
||||
arg = arguments[i];
|
||||
if (arg instanceof Model) {
|
||||
model = arg;
|
||||
} else if (typeof arg === 'object') {
|
||||
ctx = arg;
|
||||
} else if (typeof arg === 'string') {
|
||||
ns = arg;
|
||||
} else if (typeof arg === 'number') {
|
||||
res.statusCode = arg;
|
||||
} else if (typeof arg === 'boolean') {
|
||||
isStatic = arg;
|
||||
}
|
||||
}
|
||||
if (model == null) model = emptyModel;
|
||||
|
||||
// Load templates, css, and scripts from files
|
||||
this._load(isStatic, function() {
|
||||
view._render(res, model, ns, ctx, isStatic);
|
||||
});
|
||||
};
|
||||
|
||||
View.prototype._init = function(model) {
|
||||
// Initialize view & model for rendering
|
||||
model.__events = emptyEventDispatcher;
|
||||
model.__blockPaths = {};
|
||||
model.__pathMap = emptyPathMap;
|
||||
this.model = model;
|
||||
this._idCount = 0;
|
||||
var libraries = this._libraries
|
||||
, name
|
||||
for (name in libraries) {
|
||||
libraries[name].view._init(model);
|
||||
}
|
||||
};
|
||||
|
||||
View.prototype._render = function(res, model, ns, ctx, isStatic) {
|
||||
this._init(model);
|
||||
|
||||
if (!res.getHeader('content-type')) {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
}
|
||||
|
||||
try {
|
||||
// The view.get function renders and sets event listeners
|
||||
|
||||
var doctype = this.get('doctype', ns, ctx)
|
||||
, root = this.get('root', ns, ctx)
|
||||
, charset = this.get('charset', ns, ctx)
|
||||
, title = escapeHtml(this.get('title$s', ns, ctx))
|
||||
, head = this.get('head', ns, ctx)
|
||||
, header = this.get('header', ns, ctx)
|
||||
, view = this
|
||||
, body, scripts, tail;
|
||||
|
||||
// The first chunk includes everything through header. Head should contain
|
||||
// any meta tags and script tags, since it is included before CSS.
|
||||
// If there is a small amount of header HTML that will display well by itself,
|
||||
// it is a good idea to add this to the Header view so that it renders ASAP.
|
||||
res.write(doctype + root + charset + "<title>" + title + "</title>" + head + this._css + header);
|
||||
|
||||
// Remaining HTML
|
||||
body = this.get('body', ns, ctx) + this.get('footer', ns, ctx);
|
||||
if (body.slice(0, 4) === '<!--') {
|
||||
body = '­' + body;
|
||||
}
|
||||
res.write(body);
|
||||
} catch (err) {
|
||||
var errText = templateError(err);
|
||||
if (!this._errors) this._errors = errorHtml({Template: errText});
|
||||
res.write('<!DOCTYPE html><meta charset=utf-8><title></title>' + this._css);
|
||||
}
|
||||
|
||||
tail = this.get('tail', ns, ctx);
|
||||
|
||||
// Wait for transactions to finish and package up the racer model data
|
||||
|
||||
// TODO: There is a potential race condition with rendering based on the
|
||||
// model before it is bundled. However, components may want to run init
|
||||
// code that performs model mutations, so we can't bundle until after that.
|
||||
// Figure out some solution to make sure that the client will have exactly
|
||||
// the same model data when rendering to set up browser events, etc.
|
||||
model.bundle(function(bundle) {
|
||||
view._renderScripts(res, ns, ctx, isStatic, tail, bundle);
|
||||
});
|
||||
};
|
||||
|
||||
View.prototype._renderScripts = function(res, ns, ctx, isStatic, tail, bundle) {
|
||||
var clientName = this._clientName;
|
||||
|
||||
// Inline scripts and external scripts
|
||||
scripts = "<script>";
|
||||
if (!isStatic) {
|
||||
scripts += "function " + clientName + "(){" + clientName + "=1}";
|
||||
}
|
||||
scripts += escapeInlineScript(this._inline) + "</script>" + this.get('scripts', ns, ctx);
|
||||
if (!isStatic) {
|
||||
scripts += "<script defer async onload=" + clientName + "() src=" + this._jsFile + "></script>";
|
||||
}
|
||||
res.write(scripts);
|
||||
|
||||
// Initialization script and Tail
|
||||
if (isStatic) return res.end(tail);
|
||||
|
||||
res.end("<script>(function(){function f(){setTimeout(function(){" + clientName +
|
||||
"=require('" + this._requirePath + "')(" + escapeInlineScript(bundle) + ",'" +
|
||||
this._appHash + "'," + (+this._watch) + ",'" + (ns || '') + "'" +
|
||||
(ctx ? ',' + escapeInlineScript(JSON.stringify(ctx)) : '') + ")},0)}" +
|
||||
clientName + "===1?f():" + clientName + "=f})()</script>" + tail + this._errors);
|
||||
};
|
||||
277
first-project/node_modules/derby/lib/derby.Model.js
generated
vendored
Normal file
277
first-project/node_modules/derby/lib/derby.Model.js
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
var EventDispatcher = require('./EventDispatcher')
|
||||
, PathMap = require('./PathMap')
|
||||
, Model = require('racer')["protected"].Model
|
||||
, arraySlice = [].slice;
|
||||
|
||||
exports.init = init;
|
||||
|
||||
// Add support for creating a model alias from a DOM node or jQuery object
|
||||
Model.prototype.__at = Model.prototype.at;
|
||||
Model.prototype.at = function(node, absolute) {
|
||||
var isNode = node && (node.parentNode || node.jquery && (node = node[0]));
|
||||
if (!isNode) return this.__at(node, absolute);
|
||||
|
||||
updateMarkers();
|
||||
|
||||
var blockPaths = this.__blockPaths
|
||||
, pathMap = this.__pathMap
|
||||
, child, i, id, isArray, last, path, pathId, children, len;
|
||||
while (node) {
|
||||
if (node.$derbyMarkerParent) {
|
||||
node = last;
|
||||
while (node = node.previousSibling) {
|
||||
if (!(id = node.$derbyMarkerId)) continue;
|
||||
pathId = blockPaths[id];
|
||||
if (node.$derbyMarkerEnd || !pathId) break;
|
||||
|
||||
path = pathMap.paths[pathId];
|
||||
if (pathMap.arrays[path] && last) {
|
||||
i = 0;
|
||||
while (node = node.nextSibling) {
|
||||
if (node === last) {
|
||||
path = path + '.' + i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return this.__at(path, absolute);
|
||||
}
|
||||
last = last.parentNode;
|
||||
node = last.parentNode;
|
||||
continue;
|
||||
}
|
||||
if ((id = node.id) && (pathId = blockPaths[id])) {
|
||||
path = pathMap.paths[pathId];
|
||||
isArray = pathMap.arrays[path] || Array.isArray(this.get(path));
|
||||
if (isArray && last) {
|
||||
children = node.childNodes;
|
||||
for (i = 0, len = children.length; i < len; i++) {
|
||||
child = children[i];
|
||||
if (child === last) {
|
||||
path = path + '.' + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.__at(path, absolute);
|
||||
}
|
||||
last = node;
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
||||
// Just return the model if a path can't be found
|
||||
return this;
|
||||
}
|
||||
|
||||
function updateMarkers() {
|
||||
// NodeFilter.SHOW_COMMENT == 128
|
||||
var commentIterator = document.createTreeWalker(document.body, 128, null, false)
|
||||
, comment, id;
|
||||
while (comment = commentIterator.nextNode()) {
|
||||
if (comment.$derbyChecked) continue;
|
||||
comment.$derbyChecked = true;
|
||||
id = comment.data;
|
||||
if (id.charAt(0) !== '$') continue;
|
||||
if (id.charAt(1) === '$') {
|
||||
comment.$derbyMarkerEnd = true;
|
||||
id = id.slice(1);
|
||||
}
|
||||
comment.$derbyMarkerId = id;
|
||||
comment.parentNode.$derbyMarkerParent = true;
|
||||
}
|
||||
}
|
||||
|
||||
function init(model, dom, view) {
|
||||
var pathMap = model.__pathMap = new PathMap;
|
||||
var events = model.__events = new EventDispatcher({
|
||||
onTrigger: function(pathId, listener, type, local, options, value, index, arg) {
|
||||
var id = listener[0]
|
||||
, el = dom.item(id);
|
||||
|
||||
// Fail and remove the listener if the element can't be found
|
||||
if (!el) return false;
|
||||
|
||||
var method = listener[1]
|
||||
, property = listener[2]
|
||||
, partial = listener.partial
|
||||
, path = pathMap.paths[pathId]
|
||||
, triggerId;
|
||||
if (method === 'propPolite' && local) method = 'prop';
|
||||
if (partial) {
|
||||
triggerId = id;
|
||||
if (method === 'html' && type) {
|
||||
// Handle array updates
|
||||
method = type;
|
||||
if (type === 'append') {
|
||||
path += '.' + (index = model.get(path).length - 1);
|
||||
triggerId = null;
|
||||
} else if (type === 'insert') {
|
||||
path += '.' + index;
|
||||
triggerId = null;
|
||||
} else if (type === 'remove') {
|
||||
partial = null;
|
||||
} else if (type === 'move') {
|
||||
partial = null;
|
||||
property = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (listener.getValue) {
|
||||
value = listener.getValue(model, path);
|
||||
}
|
||||
if (partial) {
|
||||
value = partial(listener.ctx, model, path, triggerId, value, index, listener);
|
||||
if (value == null) return;
|
||||
}
|
||||
dom.update(el, method, options && options.ignore, value, property, index);
|
||||
}
|
||||
});
|
||||
|
||||
// Derby's mutator listeners are added via unshift instead of model.on, because
|
||||
// it needs to handle events in the same order that racer applies mutations.
|
||||
// If there is a listener to an event that applies a mutation, event listeners
|
||||
// later in the listeners queues could receive events in a different order
|
||||
|
||||
model.listeners('set').unshift(function(args, out, local, pass) {
|
||||
var arrayPath, i, index, path, value;
|
||||
model.emit('pre:set', args, out, local, pass);
|
||||
path = args[0], value = args[1];
|
||||
|
||||
// For set operations on array items, also emit a remove and insert in case the
|
||||
// array is bound
|
||||
if (/\.\d+$/.test(path)) {
|
||||
i = path.lastIndexOf('.');
|
||||
arrayPath = path.slice(0, i);
|
||||
index = path.slice(i + 1);
|
||||
triggerEach(events, pathMap, arrayPath, 'remove', local, pass, index);
|
||||
triggerEach(events, pathMap, arrayPath, 'insert', local, pass, value, index);
|
||||
}
|
||||
return triggerEach(events, pathMap, path, 'html', local, pass, value);
|
||||
});
|
||||
|
||||
model.listeners('del').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:del', args, out, local, pass);
|
||||
var path = args[0];
|
||||
return triggerEach(events, pathMap, path, 'html', local, pass);
|
||||
});
|
||||
|
||||
model.listeners('push').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:push', args, out, local, pass);
|
||||
var path = args[0]
|
||||
, values = arraySlice.call(args, 1);
|
||||
for (var i = 0, len = values.length, value; i < len; i++) {
|
||||
value = values[i];
|
||||
triggerEach(events, pathMap, path, 'append', local, pass, value);
|
||||
}
|
||||
});
|
||||
|
||||
model.listeners('move').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:move', args, out, local, pass);
|
||||
var path = args[0]
|
||||
, from = args[1]
|
||||
, to = args[2]
|
||||
, howMany = args[3]
|
||||
, len = model.get(path).length;
|
||||
from = refIndex(from);
|
||||
to = refIndex(to);
|
||||
if (from < 0) from += len;
|
||||
if (to < 0) to += len;
|
||||
if (from === to) return;
|
||||
// Update indicies in pathMap
|
||||
pathMap.onMove(path, from, to, howMany);
|
||||
triggerEach(events, pathMap, path, 'move', local, pass, from, howMany, to);
|
||||
});
|
||||
|
||||
model.listeners('unshift').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:unshift', args, out, local, pass);
|
||||
var path = args[0]
|
||||
, values = arraySlice.call(args, 1);
|
||||
insert(events, pathMap, path, 0, values, local, pass);
|
||||
});
|
||||
|
||||
model.listeners('insert').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:insert', args, out, local, pass);
|
||||
var path = args[0]
|
||||
, index = args[1]
|
||||
, values = arraySlice.call(args, 2);
|
||||
insert(events, pathMap, path, index, values, local, pass);
|
||||
});
|
||||
|
||||
model.listeners('remove').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:remove', args, out, local, pass);
|
||||
var path = args[0]
|
||||
, start = args[1]
|
||||
, howMany = args[2];
|
||||
remove(events, pathMap, path, start, howMany, local, pass);
|
||||
});
|
||||
|
||||
model.listeners('pop').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:pop', args, out, local, pass);
|
||||
var path = args[0];
|
||||
remove(events, pathMap, path, model.get(path).length, 1, local, pass);
|
||||
});
|
||||
|
||||
model.listeners('shift').unshift(function(args, out, local, pass) {
|
||||
model.emit('pre:shift', args, out, local, pass);
|
||||
var path = args[0];
|
||||
remove(events, pathMap, path, 0, 1, local, pass);
|
||||
});
|
||||
|
||||
['connected', 'canConnect'].forEach(function(event) {
|
||||
model.listeners(event).unshift(function(value) {
|
||||
triggerEach(events, pathMap, event, null, true, null, value);
|
||||
});
|
||||
});
|
||||
|
||||
model.on('reInit', function() {
|
||||
view.history.refresh();
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
function triggerEach(events, pathMap, path, arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||
var id = pathMap.ids[path]
|
||||
, segments = path.split('.')
|
||||
, i, pattern;
|
||||
|
||||
// Trigger an event on the path if it has a pathMap ID
|
||||
if (id) events.trigger(id, arg0, arg1, arg2, arg3, arg4, arg5);
|
||||
|
||||
// Also trigger a pattern event for the path and each of its parent paths
|
||||
// This is used by view helper functions to match updates on a path
|
||||
// or any of its child segments
|
||||
i = segments.length + 1;
|
||||
while (--i) {
|
||||
pattern = segments.slice(0, i).join('.') + '*';
|
||||
if (id = pathMap.ids[pattern]) {
|
||||
events.trigger(id, arg0, arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get index if event was from refList id object
|
||||
function refIndex(obj) {
|
||||
return typeof obj === 'object' ? obj.index : +obj;
|
||||
}
|
||||
|
||||
function insert(events, pathMap, path, start, values, local, pass) {
|
||||
start = refIndex(start);
|
||||
// Update indicies in pathMap
|
||||
pathMap.onInsert(path, start, values.length);
|
||||
for (var i = 0, len = values.length, value; i < len; i++) {
|
||||
value = values[i];
|
||||
triggerEach(events, pathMap, path, 'insert', local, pass, value, start + i);
|
||||
}
|
||||
}
|
||||
|
||||
function remove(events, pathMap, path, start, howMany, local, pass) {
|
||||
start = refIndex(start);
|
||||
var end = start + howMany;
|
||||
// Update indicies in pathMap
|
||||
pathMap.onRemove(path, start, howMany);
|
||||
for (var i = start; i < end; i++) {
|
||||
triggerEach(events, pathMap, path, 'remove', local, pass, start);
|
||||
}
|
||||
}
|
||||
78
first-project/node_modules/derby/lib/derby.browser.js
generated
vendored
Normal file
78
first-project/node_modules/derby/lib/derby.browser.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
var racer = require('racer')
|
||||
, tracks = require('tracks')
|
||||
, derbyModel = require('./derby.Model')
|
||||
, Dom = require('./Dom')
|
||||
, View = require('./View')
|
||||
, autoRefresh = require('./refresh').autoRefresh;
|
||||
|
||||
module.exports = derbyBrowser;
|
||||
|
||||
function derbyBrowser(derby) {
|
||||
derby.createApp = createApp;
|
||||
}
|
||||
derbyBrowser.decorate = 'derby';
|
||||
derbyBrowser.useWith = {server: false, browser: true};
|
||||
|
||||
function createApp(appModule) {
|
||||
var appExports = appModule.exports
|
||||
, view, model;
|
||||
|
||||
appModule.exports = function(modelBundle, appHash, debug, ns, ctx) {
|
||||
tracks.set('debug', debug);
|
||||
|
||||
// The init event is fired after the model data is initialized but
|
||||
// before the socket object is set
|
||||
racer.on('init', function(_model) {
|
||||
model = view.model = _model;
|
||||
var dom = view.dom = new Dom(model);
|
||||
derbyModel.init(model, dom, view);
|
||||
// Ignore errors thrown when rendering; these will also be thrown
|
||||
// on the server, and throwing here causes the app not to connect
|
||||
try {
|
||||
// Render immediately upon initialization so that the page is in
|
||||
// the same state it was when rendered on the server
|
||||
view.render(model, ns, ctx, true);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
|
||||
// The ready event is fired after the model data is initialized and
|
||||
// the socket object is set
|
||||
if (debug) {
|
||||
racer.on('ready', function(model) {
|
||||
autoRefresh(view, model, appHash);
|
||||
});
|
||||
}
|
||||
racer.init(modelBundle);
|
||||
return appExports;
|
||||
};
|
||||
|
||||
// Expose methods on the application module. Note that view must added
|
||||
// to both appModule.exports and appExports, since it is used before
|
||||
// the initialization function to make templates
|
||||
appModule.exports.view = appExports.view = view =
|
||||
new View(this._libraries, appExports);
|
||||
|
||||
function createPage() {
|
||||
return {
|
||||
render: function(ns, ctx) {
|
||||
view.render(model, ns, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
function onRoute(callback, page, params, next, isTransitional) {
|
||||
if (isTransitional) {
|
||||
callback(model, params, next);
|
||||
} else {
|
||||
callback(page, model, params, next);
|
||||
}
|
||||
}
|
||||
tracks.setup(appExports, createPage, onRoute);
|
||||
view.history = appExports.history;
|
||||
|
||||
appExports.ready = function(fn) {
|
||||
racer.on('ready', fn);
|
||||
};
|
||||
return appExports;
|
||||
}
|
||||
34
first-project/node_modules/derby/lib/derby.js
generated
vendored
Normal file
34
first-project/node_modules/derby/lib/derby.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
var path = require('path')
|
||||
, racer = require('racer')
|
||||
, View = require('./View')
|
||||
, derby = module.exports = Object.create(racer)
|
||||
, derbyPlugin = racer.util.isServer ?
|
||||
__dirname + '/derby.server' : require('./derby.browser');
|
||||
|
||||
// Allow derby object to be targeted via plugin.decorate
|
||||
racer._makePlugable('derby', derby);
|
||||
|
||||
// Shared methods for both server and browser
|
||||
derby._libraries = {};
|
||||
derby.createLibrary = createLibrary;
|
||||
|
||||
// Add appropriate server-side or browser-side methods
|
||||
derby.use(derbyPlugin);
|
||||
|
||||
function createLibrary(filename, scripts, options) {
|
||||
if (!options) options = {};
|
||||
var root = path.dirname(filename)
|
||||
, name = options.name || path.basename(root)
|
||||
, view = new View;
|
||||
|
||||
// This is needed, since component names are all lowercased
|
||||
for (scriptName in scripts) {
|
||||
scripts[scriptName.toLowerCase()] = scripts[scriptName];
|
||||
}
|
||||
|
||||
this._libraries[name] = {
|
||||
root: root
|
||||
, view: view
|
||||
, scripts: scripts
|
||||
};
|
||||
}
|
||||
166
first-project/node_modules/derby/lib/derby.server.js
generated
vendored
Normal file
166
first-project/node_modules/derby/lib/derby.server.js
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
var fs = require('fs')
|
||||
, path = require('path')
|
||||
, http = require('http')
|
||||
, racer = require('racer')
|
||||
, tracks = require('tracks')
|
||||
, up = require('up')
|
||||
, View = require('./View.server')
|
||||
, autoRefresh = require('./refresh.server').autoRefresh
|
||||
, util = racer.util
|
||||
, merge = util.merge
|
||||
, isProduction = util.isProduction
|
||||
, proto;
|
||||
|
||||
module.exports = derbyServer;
|
||||
|
||||
function derbyServer(derby) {
|
||||
merge(derby, proto);
|
||||
|
||||
Object.defineProperty(derby, 'version', {
|
||||
get: function() {
|
||||
return require('../package.json').version;
|
||||
}
|
||||
});
|
||||
}
|
||||
derbyServer.decorate = 'derby';
|
||||
derbyServer.useWith = {server: true, browser: false};
|
||||
|
||||
|
||||
proto = {
|
||||
// TODO: Remove in lieu of get / set methods
|
||||
options: {}
|
||||
|
||||
, run: function(file, port, options) {
|
||||
var master, onMessage, server, upService;
|
||||
|
||||
// Resolve relative filenames
|
||||
file = path.resolve(file);
|
||||
if (port == null) port = isProduction ? 80 : 3000;
|
||||
if (options == null) options = {numWorkers: 1};
|
||||
|
||||
try {
|
||||
server = require(file);
|
||||
} catch (e) {
|
||||
console.error('Error requiring server module from `%s`', file);
|
||||
throw e;
|
||||
}
|
||||
if (!(server instanceof http.Server)) {
|
||||
throw new Error('`' + file + '` does not export a valid `http.Server`');
|
||||
}
|
||||
if (!isProduction) {
|
||||
// TODO: This extends the internal API of Up. It would be better
|
||||
// if Up supported workers being able to force a global reload
|
||||
onMessage = up.Worker.prototype.onMessage;
|
||||
up.Worker.prototype.onMessage = function(message) {
|
||||
if (message.type === 'reload') {
|
||||
return upService.reload();
|
||||
}
|
||||
onMessage.call(this, message);
|
||||
};
|
||||
}
|
||||
master = http.createServer().listen(port);
|
||||
upService = up(master, file, options);
|
||||
process.on('SIGUSR2', function() {
|
||||
console.log('SIGUSR2 signal detected - reloading');
|
||||
upService.reload();
|
||||
});
|
||||
console.log('Starting cluster with %d workers in %s mode',
|
||||
options.numWorkers, process.env.NODE_ENV);
|
||||
console.log('`kill -s SIGUSR2 %s` to force cluster reload', process.pid);
|
||||
console.log('Go to: http://localhost:%d/', port);
|
||||
}
|
||||
|
||||
, createApp: function(appModule) {
|
||||
var appExports = appModule.exports
|
||||
, view = new View(this._libraries)
|
||||
, options = this.options
|
||||
, session, store;
|
||||
|
||||
view._derbyOptions = options;
|
||||
view._appFilename = appModule.filename;
|
||||
function setStore(_store) {
|
||||
autoRefresh(_store, options, view);
|
||||
if (session != null) session._setStore(_store);
|
||||
return store = _store;
|
||||
}
|
||||
|
||||
// Expose methods on the application module
|
||||
|
||||
function Page(model, res) {
|
||||
this._model = model;
|
||||
this._res = res;
|
||||
}
|
||||
Page.prototype.render = function(ns, ctx, status) {
|
||||
view.render(this._res, this._model, ns, ctx, status);
|
||||
};
|
||||
|
||||
function createPage(req, res) {
|
||||
var model = req.model || store.createModel();
|
||||
return new Page(model, res);
|
||||
}
|
||||
function onRoute(callback, page, params, next, isTransitional) {
|
||||
if (isTransitional) {
|
||||
callback(page._model, params, next);
|
||||
} else {
|
||||
callback(page, page._model, params, next);
|
||||
}
|
||||
}
|
||||
tracks.setup(appExports, createPage, onRoute);
|
||||
|
||||
appExports._setStore = setStore;
|
||||
appExports.view = view;
|
||||
appExports.ready = function() {};
|
||||
appExports.createStore = function(options) {
|
||||
return setStore(racer.createStore(options));
|
||||
};
|
||||
appExports.session = function() {
|
||||
return session = racer.session(store);
|
||||
};
|
||||
appExports.render = function(res, model, ns, ctx, status) {
|
||||
return view.render(res, model, ns, ctx, status);
|
||||
};
|
||||
|
||||
// Render immediately upon creating the app so that files
|
||||
// will be cached for the first render
|
||||
process.nextTick(function() {
|
||||
view.render();
|
||||
});
|
||||
return appExports;
|
||||
}
|
||||
|
||||
, createStatic: function(root) {
|
||||
return new Static(root, this._libraries);
|
||||
}
|
||||
|
||||
, createStore: function() {
|
||||
var len = arguments.length
|
||||
, last = arguments[len - 1]
|
||||
, options, app, store;
|
||||
// Last argument may be a createStore options object
|
||||
if (!last.view) {
|
||||
options = last;
|
||||
len--;
|
||||
}
|
||||
store = racer.createStore(options);
|
||||
for (var i = len; i--;) {
|
||||
app = arguments[i];
|
||||
app._setStore(store);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
};
|
||||
|
||||
function Static(root, libraries) {
|
||||
this.root = root;
|
||||
this.libraries = libraries;
|
||||
this.views = {};
|
||||
}
|
||||
Static.prototype.render = function(name, res, model, ns, ctx, status) {
|
||||
var view = this.views[name];
|
||||
if (!view) {
|
||||
view = this.views[name] = new View(this.libraries);
|
||||
view._root = this.root;
|
||||
view._clientName = name;
|
||||
}
|
||||
view.render(res, model, ns, ctx, status, true);
|
||||
};
|
||||
109
first-project/node_modules/derby/lib/eventBinding.js
generated
vendored
Normal file
109
first-project/node_modules/derby/lib/eventBinding.js
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
var util = require('racer').util
|
||||
, lookup = require('racer/lib/path').lookup
|
||||
, merge = util.merge
|
||||
, viewPath = require('./viewPath')
|
||||
, ctxPath = viewPath.ctxPath
|
||||
, pathFnArgs = viewPath.pathFnArgs
|
||||
, setBoundFn = viewPath.setBoundFn;
|
||||
|
||||
exports.splitEvents = splitEvents;
|
||||
exports.containsEvent = containsEvent;
|
||||
exports.addDomEvent = util.isServer ? empty : addDomEvent;
|
||||
|
||||
function splitEvents(eventNames) {
|
||||
var pairs = eventNames.replace(/\s/g, '').split(',')
|
||||
, eventList = []
|
||||
, pair, segments, name, eventName, delay, fn;
|
||||
for (var i = pairs.length; i--;) {
|
||||
pair = pairs[i];
|
||||
segments = pair.split(':');
|
||||
name = segments[0].split('/');
|
||||
eventName = name[0];
|
||||
delay = name[1];
|
||||
fn = segments[1] || '';
|
||||
eventList.push([eventName, delay, fn]);
|
||||
}
|
||||
return eventList;
|
||||
}
|
||||
|
||||
function containsEvent(eventNames, expected) {
|
||||
var eventList = splitEvents(eventNames)
|
||||
, eventName;
|
||||
for (var i = eventList.length; i--;) {
|
||||
eventName = eventList[i][0];
|
||||
if (eventName === expected) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function addDomEvent(events, attrs, eventNames, name, options) {
|
||||
var eventList = splitEvents(eventNames)
|
||||
, args;
|
||||
if (name) {
|
||||
if (~name.indexOf('(')) {
|
||||
args = pathFnArgs(name);
|
||||
if (!args.length) return;
|
||||
|
||||
events.push(function(ctx, modelEvents, dom, pathMap, view) {
|
||||
var id = attrs._id || attrs.id
|
||||
, paths = []
|
||||
, arg, path, pathId, event, eventName, eventOptions, i, j;
|
||||
options.setValue = function(model, value) {
|
||||
return setBoundFn(view, ctx, model, name, value);
|
||||
}
|
||||
for (i = args.length; i--;) {
|
||||
arg = args[i];
|
||||
path = ctxPath(ctx, arg);
|
||||
paths.push(path);
|
||||
pathId = pathMap.id(path);
|
||||
for (j = eventList.length; j--;) {
|
||||
event = eventList[j];
|
||||
eventName = event[0];
|
||||
eventOptions = merge({pathId: pathId, delay: event[1]}, options);
|
||||
dom.bind(eventName, id, eventOptions);
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
events.push(function(ctx, modelEvents, dom, pathMap) {
|
||||
var id = attrs._id || attrs.id
|
||||
, pathId = pathMap.id(ctxPath(ctx, name))
|
||||
, event, eventName, eventOptions, i;
|
||||
for (i = eventList.length; i--;) {
|
||||
event = eventList[i];
|
||||
eventName = event[0];
|
||||
eventOptions = merge({pathId: pathId, delay: event[1]}, options);
|
||||
dom.bind(eventName, id, eventOptions);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
events.push(function(ctx, modelEvents, dom, pathMap, view) {
|
||||
var id = attrs._id || attrs.id
|
||||
, fnCtx = ctx.$fnCtx || view._appExports
|
||||
, event, eventName, eventOptions, i, fnName;
|
||||
for (i = eventList.length; i--;) {
|
||||
event = eventList[i];
|
||||
eventName = event[0];
|
||||
eventOptions = fnListener(dom, fnCtx, event[1], event[2]);
|
||||
merge(eventOptions, options);
|
||||
dom.bind(eventName, id, eventOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fnListener(dom, fnCtx, delay, fnName) {
|
||||
var listener = {
|
||||
delay: delay
|
||||
, fn: function() {
|
||||
listener.fn = dom.fns[fnName] || fnCtx[fnName] || lookup(fnName, fnCtx);
|
||||
listener.fn.apply(null, arguments);
|
||||
}
|
||||
};
|
||||
return listener;
|
||||
}
|
||||
|
||||
function empty() {}
|
||||
460
first-project/node_modules/derby/lib/files.js
generated
vendored
Normal file
460
first-project/node_modules/derby/lib/files.js
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
||||
var pathUtil = require('path')
|
||||
, dirname = pathUtil.dirname
|
||||
, basename = pathUtil.basename
|
||||
, join = pathUtil.join
|
||||
, exists = pathUtil.exists
|
||||
, relative = pathUtil.relative
|
||||
, fs = require('fs')
|
||||
, crypto = require('crypto')
|
||||
, stylus = require('stylus')
|
||||
, nib = require('nib')
|
||||
, less = require('less')
|
||||
, racer = require('racer')
|
||||
, Promise = racer.util.Promise
|
||||
, finishAfter = racer.util.async.finishAfter
|
||||
, asyncForEach = racer.util.async.forEach
|
||||
, htmlUtil = require('html-util')
|
||||
, parseHtml = htmlUtil.parse
|
||||
, minifyHtml = htmlUtil.minify
|
||||
, styleCompilers = {
|
||||
stylus: stylusCompiler
|
||||
, less: lessCompiler
|
||||
}
|
||||
, onlyWhitespace = /^[\s\n]*$/;
|
||||
|
||||
exports.css = css;
|
||||
exports.templates = templates;
|
||||
exports.js = js;
|
||||
exports.library = library;
|
||||
exports.parseName = parseName;
|
||||
exports.hashFile = hashFile;
|
||||
exports.writeJs = writeJs;
|
||||
exports.watch = watch;
|
||||
|
||||
function css(root, clientName, compress, callback) {
|
||||
// TODO: Set default configuration options in a single place
|
||||
var styles = require('./derby').settings.styles || ['less', 'stylus']
|
||||
, compiled = []
|
||||
, finish;
|
||||
|
||||
if (!Array.isArray(styles)) styles = [styles];
|
||||
|
||||
finish = finishAfter(styles.length, function(err) {
|
||||
callback(err, compiled.join(''));
|
||||
});
|
||||
|
||||
styles.forEach(function(style, i) {
|
||||
var compiler = styleCompilers[style];
|
||||
if (!compiler) finish(new Error('Unable to find compiler for: ' + style));
|
||||
|
||||
compiler(root, clientName, compress, function(err, value) {
|
||||
compiled[i] = value || '';
|
||||
finish(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function stylusCompiler(root, clientName, compress, callback) {
|
||||
findPath(root + '/styles', clientName, '.styl', function(path) {
|
||||
if (!path) return callback('');
|
||||
fs.readFile(path, 'utf8', function(err, styl) {
|
||||
if (err) return callback(err);
|
||||
stylus(styl)
|
||||
.use(nib())
|
||||
.set('filename', path)
|
||||
.set('compress', compress)
|
||||
.render(callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function lessCompiler(root, clientName, compress, callback) {
|
||||
findPath(root + '/styles', clientName, '.less', function(path) {
|
||||
if (!path) return callback('');
|
||||
|
||||
fs.readFile(path, 'utf8', function(err, lessFile) {
|
||||
if (err) return callback(err);
|
||||
var parser = new less.Parser({
|
||||
paths: [root + '/styles']
|
||||
, filename: path
|
||||
});
|
||||
parser.parse(lessFile, function(err, tree) {
|
||||
var compiled;
|
||||
if (err) return callback(err);
|
||||
try {
|
||||
compiled = tree.toCSS({compress: compress});
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, compiled);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function templates(root, clientName, callback) {
|
||||
loadTemplates(root + '/views', clientName, callback);
|
||||
}
|
||||
|
||||
function js(parentFilename, options, callback) {
|
||||
var finish, inline, inlineFile, js;
|
||||
|
||||
// Needed for tests
|
||||
if (!parentFilename) return;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// TODO: Move this to config:
|
||||
// Express will try to include mime, which won't work in the browser
|
||||
// It doesn't actually need this for routing, so we just ignore it
|
||||
if (options.ignore) {
|
||||
options.ignore.push('mime');
|
||||
} else {
|
||||
options.ignore = ['mime'];
|
||||
}
|
||||
if (options.require) {
|
||||
options.require.push(parentFilename);
|
||||
} else {
|
||||
options.require = [parentFilename];
|
||||
}
|
||||
inlineFile = join(dirname(parentFilename), 'inline.js');
|
||||
finish = finishAfter(2, function(err) {
|
||||
callback(err, js, inline);
|
||||
});
|
||||
racer.js(options, function(err, value) {
|
||||
js = value;
|
||||
finish(err);
|
||||
});
|
||||
fs.readFile(inlineFile, 'utf8', function(err, value) {
|
||||
inline = value;
|
||||
// Ignore file not found error
|
||||
if (err && err.code === 'ENOENT') err = null;
|
||||
finish(err);
|
||||
});
|
||||
}
|
||||
|
||||
function library(root, callback) {
|
||||
var components = {};
|
||||
|
||||
fs.readdir(root, function(err, files) {
|
||||
if (err) return callback(err);
|
||||
asyncForEach(files, libraryFile, function(err) {
|
||||
if (err) return callback(err);
|
||||
callback(null, components);
|
||||
});
|
||||
});
|
||||
|
||||
function libraryFile(file, callback) {
|
||||
var path = root + '/' + file
|
||||
fs.stat(path, function(err, stats) {
|
||||
if (err) return callback(err);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return addComponent(root, file, callback);
|
||||
}
|
||||
if (extensions['html'].test(file)) {
|
||||
file = file.replace(extensions['html'], '');
|
||||
return addComponent(root, file, callback);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function addComponent(root, name, callback) {
|
||||
loadTemplates(root, name, function(err, templates, instances) {
|
||||
components[name] = {
|
||||
templates: templates
|
||||
, instances: instances
|
||||
};
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseName(parentFilename, options) {
|
||||
var parentDir = dirname(parentFilename)
|
||||
, root = parentDir
|
||||
, base = basename(parentFilename, '.js');
|
||||
if (base === 'index') {
|
||||
base = basename(parentDir);
|
||||
root = dirname(dirname(parentDir));
|
||||
} else if (basename(parentDir) === 'lib') {
|
||||
root = dirname(parentDir);
|
||||
}
|
||||
return {
|
||||
root: root
|
||||
, clientName: options.name || base
|
||||
, require: './' + basename(parentFilename)
|
||||
};
|
||||
}
|
||||
|
||||
function hashFile(file) {
|
||||
var hash = crypto.createHash('md5').update(file).digest('base64');
|
||||
// Base64 uses characters reserved in URLs and adds extra padding charcters.
|
||||
// Replace "/" and "+" with the unreserved "-" and "_" and remove "=" padding
|
||||
return hash.replace(/[\/\+=]/g, function(match) {
|
||||
switch (match) {
|
||||
case '/': return '-';
|
||||
case '+': return '_';
|
||||
case '=': return '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeJs(root, js, options, callback) {
|
||||
var staticRoot = options.staticRoot || join(root, 'public')
|
||||
, staticDir = options.staticDir || 'gen'
|
||||
, staticPath = join(staticRoot, staticDir)
|
||||
, hash = hashFile(js)
|
||||
, filename = hash + '.js'
|
||||
, jsFile = join('/', staticDir, filename)
|
||||
, filePath = join(staticPath, filename);
|
||||
|
||||
function finish() {
|
||||
fs.writeFile(filePath, js, function(err) {
|
||||
callback(err, jsFile, hash);
|
||||
});
|
||||
}
|
||||
exists(staticPath, function(value) {
|
||||
if (value) return finish();
|
||||
|
||||
exists(staticRoot, function(value) {
|
||||
if (value) {
|
||||
fs.mkdir(staticPath, '0777', function(err) {
|
||||
finish();
|
||||
})
|
||||
return;
|
||||
}
|
||||
fs.mkdir(staticRoot, '0777', function(err) {
|
||||
fs.mkdir(staticPath, '0777', function(err) {
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function watch(dir, type, onChange) {
|
||||
var extension = extensions[type]
|
||||
, watchFn = fs.watch ? systemWatch : pollWatch;
|
||||
|
||||
files(dir, extension).forEach(watchFn);
|
||||
|
||||
function systemWatch(file) {
|
||||
fs.watch(file, function() {
|
||||
onChange(file);
|
||||
});
|
||||
}
|
||||
function pollWatch(file) {
|
||||
fs.watchFile(file, {interval: 100}, function(curr, prev) {
|
||||
if (prev.mtime < curr.mtime) {
|
||||
onChange(file);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function findPath(root, name, extension, callback) {
|
||||
if (name.charAt(0) !== '/') {
|
||||
name = join(root, name);
|
||||
}
|
||||
var path = name + extension;
|
||||
exists(path, function(value) {
|
||||
if (value) return callback(path);
|
||||
path = join(name, 'index' + extension);
|
||||
exists(path, function(value) {
|
||||
callback(value ? path : null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadTemplates(root, fileName, callback) {
|
||||
var count = 0
|
||||
, calls = {incr: incr, finish: finish};
|
||||
function incr() {
|
||||
count++;
|
||||
}
|
||||
function finish(err, templates, instances) {
|
||||
if (err) {
|
||||
calls.finish = function() {};
|
||||
return callback(err);
|
||||
}
|
||||
--count || callback(null, templates, instances);
|
||||
}
|
||||
forTemplate(root, fileName, 'import', calls);
|
||||
}
|
||||
|
||||
function forTemplate(root, fileName, get, calls, files, templates, instances, alias, currentNs) {
|
||||
if (currentNs == null) currentNs = '';
|
||||
calls.incr();
|
||||
findPath(root, fileName, '.html', function(path) {
|
||||
var getCount, got, matchesGet, promise;
|
||||
if (path === null) {
|
||||
if (!files) {
|
||||
// Return without doing anything if the path isn't found, and this is the
|
||||
// initial automatic lookup based on the clientName
|
||||
return calls.finish(null, {}, {});
|
||||
} else {
|
||||
return calls.finish(new Error("Can't find file " + fileName));
|
||||
}
|
||||
}
|
||||
files || (files = {});
|
||||
templates || (templates = {});
|
||||
instances || (instances = {});
|
||||
|
||||
got = false;
|
||||
if (get === 'import') {
|
||||
matchesGet = function() {
|
||||
return got = true;
|
||||
}
|
||||
} else if (Array.isArray(get)) {
|
||||
getCount = get.length;
|
||||
matchesGet = function(name) {
|
||||
--getCount || (got = true);
|
||||
return ~get.indexOf(name);
|
||||
}
|
||||
} else {
|
||||
matchesGet = function(name) {
|
||||
got = true;
|
||||
return get === name;
|
||||
}
|
||||
}
|
||||
|
||||
promise = files[path];
|
||||
if (!promise) {
|
||||
promise = files[path] = new Promise;
|
||||
fs.readFile(path, 'utf8', function(err, file) {
|
||||
promise.resolve(err, file);
|
||||
});
|
||||
}
|
||||
promise.on(function(err, file) {
|
||||
if (err) calls.finish(err);
|
||||
parseTemplateFile(root, dirname(path), path, calls, files, templates, instances, alias, currentNs, matchesGet, file);
|
||||
if (!got) {
|
||||
calls.finish(new Error("Can't find template '" + get + "' in " + path));
|
||||
}
|
||||
calls.finish(null, templates, instances);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parseTemplateFile(root, dir, path, calls, files, templates, instances, alias, currentNs, matchesGet, file) {
|
||||
var relativePath = relative(root, path)
|
||||
, as, importTemplates, name, ns, src, templateOptions;
|
||||
|
||||
parseHtml(file, {
|
||||
// Force template tags to be treated as raw tags,
|
||||
// meaning their contents are not parsed as HTML
|
||||
rawTags: /^(?:[^\s=\/>]+:|style|script)$/i
|
||||
, matchEnd: matchEnd
|
||||
, start: start
|
||||
, text: text
|
||||
});
|
||||
|
||||
function matchEnd(tagName) {
|
||||
if (tagName.slice(-1) === ':') {
|
||||
return /<\/?[^\s=\/>]+:[\s>]/;
|
||||
}
|
||||
return new RegExp('</' + tagName, 'i');
|
||||
}
|
||||
|
||||
function start(tag, tagName, attrs) {
|
||||
var i = tagName.length - 1
|
||||
, srcNs, template;
|
||||
|
||||
as, importTemplates, ns, src = null;
|
||||
name = (tagName.charAt(i) === ':' ? tagName.slice(0, i) : '').toLowerCase();
|
||||
if (name === 'import') {
|
||||
src = attrs.src, ns = attrs.ns, as = attrs.as, template = attrs.template;
|
||||
if (!src) {
|
||||
calls.finish(new Error("Template import in " + path +
|
||||
" must have a 'src' attribute"));
|
||||
}
|
||||
if (template) {
|
||||
importTemplates = template.toLowerCase().split(' ');
|
||||
if (importTemplates.length > 1 && (as != null)) {
|
||||
calls.finish(new Error("Template import of '" + src + "' in " +
|
||||
path + " can't specify multiple 'template' values with 'as'"));
|
||||
}
|
||||
}
|
||||
if ('ns' in attrs) {
|
||||
if (as) calls.finish(new Error("Template import of '" + src +
|
||||
"' in " + path + " can't specifiy both 'ns' and 'as' attributes"));
|
||||
// Import into the namespace specified via 'ns' underneath
|
||||
// the current namespace
|
||||
ns = ns
|
||||
? currentNs ? currentNs + ':' + ns : ns
|
||||
: currentNs;
|
||||
} else if (as) {
|
||||
// If 'as' is specified, import into the current namespace
|
||||
ns = currentNs;
|
||||
} else {
|
||||
// If no namespace is specified, use the src as a namespace.
|
||||
// Remove leading '.' and '/' characters
|
||||
srcNs = src.replace(/^[.\/]*/, '');
|
||||
ns = currentNs ? currentNs + ':' + srcNs : srcNs;
|
||||
}
|
||||
ns = ns.toLowerCase();
|
||||
} else {
|
||||
templateOptions = attrs;
|
||||
}
|
||||
}
|
||||
|
||||
function text(text, isRawText) {
|
||||
var instanceName, templateName, toGet;
|
||||
if (!matchesGet(name)) return;
|
||||
if (src) {
|
||||
if (!onlyWhitespace.test(text)) {
|
||||
calls.finish(new Error("Template import of '" + src + "' in " +
|
||||
path + " can't contain content: " + text));
|
||||
}
|
||||
toGet = importTemplates || 'import';
|
||||
return forTemplate(root, join(dir, src), toGet, calls, files, templates, instances, as, ns);
|
||||
}
|
||||
templateName = relativePath + ':' + name;
|
||||
instanceName = alias || name;
|
||||
if (currentNs) {
|
||||
instanceName = currentNs + ':' + instanceName;
|
||||
}
|
||||
instances[instanceName] = [templateName, templateOptions];
|
||||
if (templates[templateName]) return;
|
||||
if (!(name && isRawText)) {
|
||||
if (onlyWhitespace.test(text)) return;
|
||||
calls.finish(new Error("Can't read template in " + path +
|
||||
" near the text: " + text));
|
||||
}
|
||||
templates[templateName] = minifyHtml(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: These should be set as configuration options
|
||||
var extensions = {
|
||||
html: /\.html$/i
|
||||
, css: /\.styl$|\.css|\.less$/i
|
||||
, js: /\.js$/i
|
||||
};
|
||||
|
||||
var ignoreDirectories = ['node_modules', '.git', 'gen'];
|
||||
|
||||
function ignored(path) {
|
||||
return ignoreDirectories.indexOf(path) === -1;
|
||||
}
|
||||
|
||||
function files(dir, extension, out) {
|
||||
if (out == null) out = [];
|
||||
fs.readdirSync(dir).filter(ignored).forEach(function(p) {
|
||||
p = join(dir, p);
|
||||
if (fs.statSync(p).isDirectory()) {
|
||||
files(p, extension, out);
|
||||
} else if (extension.test(p)) {
|
||||
out.push(p);
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
173
first-project/node_modules/derby/lib/markup.js
generated
vendored
Normal file
173
first-project/node_modules/derby/lib/markup.js
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
var eventBinding = require('./eventBinding')
|
||||
, splitEvents = eventBinding.splitEvents
|
||||
, containsEvent = eventBinding.containsEvent
|
||||
, addDomEvent = eventBinding.addDomEvent
|
||||
, TEXT_EVENTS = 'keyup,keydown,paste/0,dragover/0,blur'
|
||||
, AUTOCOMPLETE_OFF = {
|
||||
checkbox: true
|
||||
, radio: true
|
||||
}
|
||||
, onBindA, onBindForm;
|
||||
|
||||
module.exports = {
|
||||
bound: {
|
||||
'value': {
|
||||
'input': function(events, attrs, name) {
|
||||
var type = attrs.type
|
||||
, eventNames, method;
|
||||
if (type === 'radio' || type === 'checkbox') return;
|
||||
if (type === 'range' || 'x-blur' in attrs) {
|
||||
// Only update after the element loses focus
|
||||
delete attrs['x-blur'];
|
||||
eventNames = 'change,blur';
|
||||
} else {
|
||||
// By default, update as the user types
|
||||
eventNames = TEXT_EVENTS;
|
||||
}
|
||||
if ('x-ignore-focus' in attrs) {
|
||||
// Update value regardless of focus
|
||||
delete attrs['x-ignore-focus'];
|
||||
method = 'prop';
|
||||
} else {
|
||||
// Update value unless window and element are focused
|
||||
method = 'propPolite';
|
||||
}
|
||||
addDomEvent(events, attrs, eventNames, name, {
|
||||
method: 'prop'
|
||||
, property: 'value'
|
||||
});
|
||||
return {method: method};
|
||||
}
|
||||
}
|
||||
|
||||
, 'checked': {
|
||||
'*': function(events, attrs, name) {
|
||||
addDomEvent(events, attrs, 'change', name, {
|
||||
method: 'prop'
|
||||
, property: 'checked'
|
||||
});
|
||||
return {method: 'prop'};
|
||||
}
|
||||
}
|
||||
|
||||
, 'selected': {
|
||||
'*': function(events, attrs, name) {
|
||||
addDomEvent(events, attrs, 'change', name, {
|
||||
method: 'prop'
|
||||
, property: 'selected'
|
||||
});
|
||||
return {method: 'prop'};
|
||||
}
|
||||
}
|
||||
|
||||
, 'disabled': {
|
||||
'*': function() {
|
||||
return {method: 'prop'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, boundParent: {
|
||||
'contenteditable': {
|
||||
'*': function(events, attrs, name) {
|
||||
addDomEvent(events, attrs, TEXT_EVENTS, name, {
|
||||
method: 'html'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
, '*': {
|
||||
'textarea': function(events, attrs, name) {
|
||||
addDomEvent(events, attrs, TEXT_EVENTS, name, {
|
||||
method: 'prop'
|
||||
, property: 'value'
|
||||
});
|
||||
return {method: 'prop', property: 'value'};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, element: {
|
||||
'select': function(events, attrs) {
|
||||
// Distribute change event to child nodes of select elements
|
||||
addDomEvent(events, attrs, 'change:$forChildren');
|
||||
return {addId: true};
|
||||
}
|
||||
|
||||
, 'input': function(events, attrs) {
|
||||
if (AUTOCOMPLETE_OFF[attrs.type] && !('autocomplete' in attrs)) {
|
||||
attrs.autocomplete = 'off';
|
||||
}
|
||||
if (attrs.type === 'radio') {
|
||||
// Distribute change events to other elements with the same name
|
||||
addDomEvent(events, attrs, 'change:$forName');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, attr: {
|
||||
'x-bind': {
|
||||
'*': function(events, attrs, eventNames) {
|
||||
addDomEvent(events, attrs, eventNames);
|
||||
return {addId: true, del: true};
|
||||
}
|
||||
|
||||
, 'a': onBindA = function(events, attrs, eventNames) {
|
||||
if (containsEvent(eventNames, 'click') && !('href' in attrs)) {
|
||||
attrs.href = '#';
|
||||
if (!('onclick' in attrs)) {
|
||||
attrs.onclick = 'return false';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, 'form': onBindForm = function(events, attrs, eventNames) {
|
||||
if (containsEvent(eventNames, 'submit')) {
|
||||
if (!('onsubmit' in attrs)) {
|
||||
attrs.onsubmit = 'return false';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, 'x-capture': {
|
||||
'*': function(events, attrs, eventNames) {
|
||||
addDomEvent(events, attrs, eventNames, null, {capture: true});
|
||||
return {addId: true, del: true};
|
||||
}
|
||||
, 'a': onBindA
|
||||
, 'form': onBindForm
|
||||
}
|
||||
|
||||
, 'x-as': {
|
||||
'*': function(events, attrs, name) {
|
||||
events.push(function(ctx) {
|
||||
var elements = ctx.$elements || (ctx.$elements = {});
|
||||
elements[name] = attrs._id || attrs.id;
|
||||
});
|
||||
return {addId: true, del: true}
|
||||
}
|
||||
}
|
||||
|
||||
, 'checked': {
|
||||
'*': function() {
|
||||
return {bool: true};
|
||||
}
|
||||
}
|
||||
|
||||
, 'selected': {
|
||||
'*': function() {
|
||||
return {bool: true};
|
||||
}
|
||||
}
|
||||
|
||||
, 'disabled': {
|
||||
'*': function() {
|
||||
return {bool: true};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, TEXT_EVENTS: TEXT_EVENTS
|
||||
, AUTOCOMPLETE_OFF: AUTOCOMPLETE_OFF
|
||||
};
|
||||
70
first-project/node_modules/derby/lib/refresh.js
generated
vendored
Normal file
70
first-project/node_modules/derby/lib/refresh.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
exports.errorHtml = errorHtml;
|
||||
exports.autoRefresh = autoRefresh;
|
||||
|
||||
var errors = {};
|
||||
|
||||
function errorHtml(errors) {
|
||||
var text = ''
|
||||
, type, err;
|
||||
for (type in errors) {
|
||||
err = errors[type];
|
||||
text += '<h3>' + type + ' Error</h3><pre>' + err + '</pre>';
|
||||
}
|
||||
if (!text) return;
|
||||
return '<div id=$_derbyError style="position:absolute;background:rgba(0,0,0,.7);top:0;left:0;right:0;bottom:0;text-align:center">' +
|
||||
'<div style="background:#fff;padding:20px 40px;margin:60px;display:inline-block;text-align:left">' +
|
||||
text + '</div></div>';
|
||||
}
|
||||
|
||||
function autoRefresh(view, model, appHash) {
|
||||
var socket = model.socket;
|
||||
|
||||
model.on('connectionStatus', function(connected, canConnect) {
|
||||
if (!canConnect) window.location.reload(true);
|
||||
});
|
||||
socket.on('connect', function() {
|
||||
socket.emit('derbyClient', appHash, function(reload) {
|
||||
if (reload) window.location.reload(true);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('refreshCss', function(err, css) {
|
||||
var el = document.getElementById('$_css');
|
||||
if (el) el.innerHTML = css;
|
||||
updateError('CSS', err);
|
||||
});
|
||||
|
||||
socket.on('refreshHtml', function(err, templates, instances, libraryData) {
|
||||
view._makeAll(templates, instances);
|
||||
view._makeComponents(libraryData);
|
||||
try {
|
||||
view.history.refresh();
|
||||
} catch (_err) {
|
||||
err || (err = _err.stack);
|
||||
}
|
||||
updateError('Template', err);
|
||||
});
|
||||
}
|
||||
|
||||
function updateError(type, err) {
|
||||
if (err) {
|
||||
errors[type] = err;
|
||||
} else {
|
||||
delete errors[type];
|
||||
}
|
||||
var el = document.getElementById('$_derbyError')
|
||||
, html = errorHtml(errors)
|
||||
, fragment, range;
|
||||
if (html) {
|
||||
if (el) {
|
||||
el.outerHTML = html;
|
||||
} else {
|
||||
range = document.createRange();
|
||||
range.selectNode(document.body);
|
||||
fragment = range.createContextualFragment(html);
|
||||
document.body.appendChild(fragment);
|
||||
}
|
||||
} else {
|
||||
if (el) el.parentNode.removeChild(el);
|
||||
}
|
||||
}
|
||||
75
first-project/node_modules/derby/lib/refresh.server.js
generated
vendored
Normal file
75
first-project/node_modules/derby/lib/refresh.server.js
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
var isProduction = require('racer').util.isProduction
|
||||
, files = require('./files')
|
||||
, refresh = module.exports = require('./refresh');
|
||||
|
||||
refresh.cssError = cssError;
|
||||
refresh.templateError = templateError;
|
||||
refresh.autoRefresh = autoRefresh;
|
||||
|
||||
function cssError(err) {
|
||||
if (err.stack) {
|
||||
console.error('\nCSS PARSE ERROR\n' + err.stack);
|
||||
return err.stack;
|
||||
} else {
|
||||
console.error('\nCSS PARSE ERROR\n' + err.message + '\n' + err.filename);
|
||||
return err.message + '\n' + err.filename;
|
||||
}
|
||||
}
|
||||
|
||||
function templateError(err) {
|
||||
console.error('\nTEMPLATE ERROR\n' + err.stack);
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
function autoRefresh(store, options, view) {
|
||||
if (isProduction || store._derbySocketsSetup) return;
|
||||
store._derbySocketsSetup = true;
|
||||
var listeners = {};
|
||||
|
||||
store.sockets.on('connection', function(socket) {
|
||||
socket.on('derbyClient', function(appHash, callback) {
|
||||
var appFilename, reload, sockets;
|
||||
reload = appHash !== view._appHash;
|
||||
callback(reload);
|
||||
if (reload) return;
|
||||
|
||||
appFilename = view._appFilename;
|
||||
if (listeners[appFilename]) {
|
||||
return listeners[appFilename].push(socket);
|
||||
}
|
||||
|
||||
sockets = listeners[appFilename] = [socket];
|
||||
addWatches(appFilename, options, sockets, view);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addWatches(appFilename, options, sockets, view) {
|
||||
var parsed = files.parseName(appFilename, options)
|
||||
, root = parsed.root
|
||||
, clientName = parsed.clientName;
|
||||
|
||||
files.watch(root, 'css', function() {
|
||||
view._loadCss(root, clientName, function(err, css) {
|
||||
var errText;
|
||||
if (err) errText = cssError(err);
|
||||
for (var i = sockets.length; i--;) {
|
||||
sockets[i].emit('refreshCss', errText, css);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
files.watch(root, 'html', function() {
|
||||
view._loadTemplates(root, clientName, function(err, templates, instances, libraryData) {
|
||||
var errText;
|
||||
if (err) errText = templateError(err);
|
||||
for (var i = sockets.length; i--;) {
|
||||
sockets[i].emit('refreshHtml', errText, templates, instances, libraryData);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
files.watch(root, 'js', function() {
|
||||
process.send({type: 'reload'});
|
||||
});
|
||||
}
|
||||
269
first-project/node_modules/derby/lib/viewPath.js
generated
vendored
Normal file
269
first-project/node_modules/derby/lib/viewPath.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
var lookup = require('racer/lib/path').lookup
|
||||
, trimLeading = require('html-util').trimLeading;
|
||||
|
||||
exports.wrapRemainder = wrapRemainder;
|
||||
exports.extractPlaceholder = extractPlaceholder;
|
||||
exports.pathFnArgs = pathFnArgs;
|
||||
exports.ctxPath = ctxPath;
|
||||
exports.dataValue = dataValue;
|
||||
exports.setBoundFn = setBoundFn;
|
||||
|
||||
function wrapRemainder(tagName, remainder) {
|
||||
if (!remainder) return false;
|
||||
return !(new RegExp('^<\/' + tagName, 'i')).test(remainder);
|
||||
}
|
||||
|
||||
var openPlaceholder = /^([\s\S]*?)(\{{1,3})([\s\S]*)/
|
||||
, placeholderContent = /^\s*([\#\/]?)(?:(else\sif|if|else|unless|each|with|unescaped)(?!\())?\s*([^\s(>]*(?:\s*\([\s\S]*\))?)(?:\s+as\s+:([^\s>]+))?/;
|
||||
|
||||
function extractPlaceholder(text) {
|
||||
var match = openPlaceholder.exec(text);
|
||||
if (!match) return;
|
||||
var pre = match[1]
|
||||
, open = match[2]
|
||||
, remainder = match[3]
|
||||
, openLen = open.length
|
||||
, bound = openLen === 1
|
||||
, macro = openLen === 3
|
||||
, end = matchBraces(remainder, openLen, 0, '{', '}')
|
||||
, endInner = end - openLen
|
||||
, inner = remainder.slice(0, endInner)
|
||||
, post = remainder.slice(end)
|
||||
, content = placeholderContent.exec(inner)
|
||||
, escaped, name, type;
|
||||
if (!content) return;
|
||||
type = content[2];
|
||||
escaped = true;
|
||||
if (type === 'unescaped') {
|
||||
escaped = false;
|
||||
type = '';
|
||||
}
|
||||
name = content[3];
|
||||
if (bound) name = name.replace(/\bthis\b/, '.');
|
||||
if (macro && name === 'content') escaped = false;
|
||||
return {
|
||||
pre: trimLeading(pre)
|
||||
, post: trimLeading(post)
|
||||
, bound: bound
|
||||
, macro: macro
|
||||
, hash: content[1]
|
||||
, escaped: escaped
|
||||
, type: type
|
||||
, name: name
|
||||
, alias: content[4]
|
||||
};
|
||||
}
|
||||
|
||||
function matchBraces(text, num, i, openChar, closeChar) {
|
||||
var close, hasClose, hasOpen, open;
|
||||
i++;
|
||||
while (num) {
|
||||
close = text.indexOf(closeChar, i);
|
||||
open = text.indexOf(openChar, i);
|
||||
hasClose = ~close;
|
||||
hasOpen = ~open;
|
||||
if (hasClose && (!hasOpen || (close < open))) {
|
||||
i = close + 1;
|
||||
num--;
|
||||
continue;
|
||||
} else if (hasOpen) {
|
||||
i = open + 1;
|
||||
num++;
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
var fnCall = /^([^(]+)\s*\(\s*([\s\S]*?)\s*\)\s*$/
|
||||
, argSeparator = /\s*([,(])\s*/g
|
||||
, notSeparator = /[^,\s]/g
|
||||
, notPathArg = /(?:^['"\d\-[{])|(?:^null$)|(?:^true$)|(?:^false$)/;
|
||||
|
||||
function fnArgs(inner) {
|
||||
var args = []
|
||||
, lastIndex = 0
|
||||
, match, end, last;
|
||||
while (match = argSeparator.exec(inner)) {
|
||||
if (match[1] === '(') {
|
||||
end = matchBraces(inner, 1, argSeparator.lastIndex, '(', ')');
|
||||
args.push(inner.slice(lastIndex, end));
|
||||
notSeparator.lastIndex = end;
|
||||
lastIndex = argSeparator.lastIndex =
|
||||
notSeparator.test(inner) ? notSeparator.lastIndex - 1 : end;
|
||||
continue;
|
||||
}
|
||||
args.push(inner.slice(lastIndex, match.index));
|
||||
lastIndex = argSeparator.lastIndex;
|
||||
}
|
||||
last = inner.slice(lastIndex);
|
||||
if (last) args.push(last);
|
||||
return args;
|
||||
}
|
||||
|
||||
function fnCallError(name) {
|
||||
throw new Error('malformed view function call: ' + name);
|
||||
}
|
||||
|
||||
function fnArgValue(view, ctx, model, name, macro, arg) {
|
||||
if (arg === 'null') return null;
|
||||
if (arg === 'true') return true;
|
||||
if (arg === 'false') return false;
|
||||
var firstChar = arg.charAt(0)
|
||||
, match;
|
||||
if (firstChar === "'") {
|
||||
match = /^'(.*)'$/.exec(arg) || fnCallError(name);
|
||||
return match[1];
|
||||
}
|
||||
if (firstChar === '"') {
|
||||
match = /^"(.*)"$/.exec(arg) || fnCallError(name);
|
||||
return match[1];
|
||||
}
|
||||
if (/^[\d\-]/.test(firstChar) && !isNaN(arg)) {
|
||||
return +arg;
|
||||
}
|
||||
if (firstChar === '[' || firstChar === '{') {
|
||||
throw new Error('object literals not supported in view function call: ' + name);
|
||||
}
|
||||
return dataValue(view, ctx, model, arg, macro);
|
||||
}
|
||||
|
||||
function fnValue(view, ctx, model, name, macro) {
|
||||
var match = fnCall.exec(name) || fnCallError(name)
|
||||
, fnName = match[1]
|
||||
, args = fnArgs(match[2])
|
||||
, fn, fnName, i;
|
||||
for (i = args.length; i--;) {
|
||||
args[i] = fnArgValue(view, ctx, model, name, macro, args[i]);
|
||||
}
|
||||
if (!(fn = view.getFns[fnName])) {
|
||||
throw new Error('view function "' + fnName + '" not found for call: ' + name);
|
||||
}
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
|
||||
function pathFnArgs(name, paths) {
|
||||
var match = fnCall.exec(name) || fnCallError(name)
|
||||
, args = fnArgs(match[2])
|
||||
, i, arg;
|
||||
if (paths == null) paths = [];
|
||||
for (i = args.length; i--;) {
|
||||
arg = args[i];
|
||||
if (notPathArg.test(arg)) continue;
|
||||
if (~arg.indexOf('(')) {
|
||||
pathFnArgs(arg, paths);
|
||||
continue;
|
||||
}
|
||||
paths.push(arg);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
function macroName(ctx, name, noReplace) {
|
||||
var macroCtx = ctx.$macroCtx
|
||||
, path = ctxPath(macroCtx, name, false, noReplace)
|
||||
, segments = path.split('.')
|
||||
, base = segments[0].toLowerCase()
|
||||
, remainder = segments[1]
|
||||
, value = lookup(base, macroCtx)
|
||||
, macroVar = value && value.$macroVar;
|
||||
if (!macroVar) return remainder ? base + '.' + remainder : base;
|
||||
return remainder ?
|
||||
(/\.+/.test(macroVar) ? macroVar.slice(1) : macroVar) + '.' + remainder :
|
||||
macroVar;
|
||||
}
|
||||
|
||||
function ctxPath(ctx, name, macro, noReplace) {
|
||||
if (macro) name = macroName(ctx, name, noReplace);
|
||||
|
||||
var firstChar = name.charAt(0)
|
||||
, i, aliasName, firstChar, indices;
|
||||
if (firstChar === ':') {
|
||||
if (~(i = name.indexOf('.'))) {
|
||||
aliasName = name.slice(1, i);
|
||||
name = name.slice(i);
|
||||
} else {
|
||||
aliasName = name.slice(1);
|
||||
name = '';
|
||||
}
|
||||
i = ctx.$depth - ctx.$aliases[aliasName];
|
||||
if (i !== i) throw new Error("Can't find alias for " + aliasName);
|
||||
} else if (firstChar === '.') {
|
||||
i = 0;
|
||||
while (name.charAt(i) === '.') {
|
||||
i++;
|
||||
}
|
||||
name = i === name.length ? '' : name.slice(i - 1);
|
||||
}
|
||||
if (i && (name = ctx.$paths[i - 1] + name) && !noReplace) {
|
||||
indices = ctx.$indices;
|
||||
i = indices.length;
|
||||
name = name.replace(/\$#/g, function() {
|
||||
return indices[--i];
|
||||
});
|
||||
}
|
||||
return name.replace(/\[([^\]]+)\]/g, function(match, name) {
|
||||
return lookup(name, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
function dataValue(view, ctx, model, name, macro) {
|
||||
var path, value;
|
||||
if (~name.indexOf('(')) {
|
||||
return fnValue(view, ctx, model, name, macro);
|
||||
}
|
||||
if (macro) {
|
||||
// Get macro content sections
|
||||
value = lookup(name.toLowerCase(), ctx.$macroCtx);
|
||||
if (value && !value.$macroVar) {
|
||||
return typeof value === 'function' ? value(ctx, model) : value;
|
||||
}
|
||||
}
|
||||
path = ctxPath(ctx, name, macro);
|
||||
value = lookup(path, ctx);
|
||||
if (value !== void 0) return value;
|
||||
value = model.get(path);
|
||||
return value !== void 0 ? value : model[path];
|
||||
}
|
||||
|
||||
function setBoundFn(view, ctx, model, name, value) {
|
||||
var match = fnCall.exec(name) || fnCallError(name)
|
||||
, fnName = match[1]
|
||||
, args = fnArgs(match[2])
|
||||
, get = view.getFns[fnName]
|
||||
, set = view.setFns[fnName]
|
||||
, macro = false
|
||||
, numInputs = set.length - 1
|
||||
, arg, i, inputs, out, path, len;
|
||||
|
||||
if (!(get && set)) {
|
||||
throw new Error('view function "' + fnName + '" not found for binding to: ' + name);
|
||||
}
|
||||
|
||||
if (numInputs) {
|
||||
inputs = [value];
|
||||
i = 0;
|
||||
while (i < numInputs) {
|
||||
inputs.push(fnArgValue(view, ctx, model, name, macro, args[i++]));
|
||||
}
|
||||
out = set.apply(null, inputs);
|
||||
} else {
|
||||
out = set(value);
|
||||
}
|
||||
if (!out) return;
|
||||
|
||||
for (i = 0, len = out.length; i < len; i++) {
|
||||
value = out[i];
|
||||
arg = args[i + numInputs];
|
||||
if (~arg.indexOf('(')) {
|
||||
setBoundFn(view, ctx, model, arg, value);
|
||||
continue;
|
||||
}
|
||||
if (value === void 0 || notPathArg.test(arg)) continue;
|
||||
path = ctxPath(ctx, arg);
|
||||
if (model.get(path) === value) continue;
|
||||
model.set(path, value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user