lsd_rails 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Packages/Sheet.js/Source/SheetParser.Value.js +1 -1
- data/Packages/lsd/Source/Action/Clone.js +2 -12
- data/Packages/lsd/Source/Action/Delete.js +1 -7
- data/Packages/lsd/Source/Action/Display.js +7 -7
- data/Packages/lsd/Source/Action/Edit.js +2 -2
- data/Packages/lsd/Source/Action/Invoke.js +2 -2
- data/Packages/lsd/Source/Action/Submit.js +2 -0
- data/Packages/lsd/Source/Action/Toggle.js +4 -0
- data/Packages/lsd/Source/Action/Update.js +2 -1
- data/Packages/lsd/Source/Action.js +4 -5
- data/Packages/lsd/Source/Document.js +10 -7
- data/Packages/lsd/Source/LSD.js +22 -127
- data/Packages/lsd/Source/Layer.js +4 -4
- data/Packages/lsd/Source/Layout.js +1077 -259
- data/Packages/lsd/Source/Mixin/Animation.js +1 -1
- data/Packages/lsd/Source/Mixin/Choice.js +2 -2
- data/Packages/lsd/Source/Mixin/Command.js +26 -8
- data/Packages/lsd/Source/Mixin/ContentEditable.js +2 -1
- data/Packages/lsd/Source/{Trait → Mixin}/Date.js +21 -11
- data/Packages/lsd/Source/{Trait/Menu.js → Mixin/Details.js} +6 -1
- data/Packages/lsd/Source/Mixin/Draggable.js +1 -1
- data/Packages/lsd/Source/Mixin/Fieldset.js +51 -49
- data/Packages/lsd/Source/Mixin/Focusable.js +38 -39
- data/Packages/lsd/Source/Mixin/Invokable.js +13 -14
- data/Packages/lsd/Source/Mixin/List.js +9 -17
- data/Packages/lsd/Source/Mixin/Placeholder.js +2 -7
- data/Packages/lsd/Source/Mixin/Request.js +5 -3
- data/Packages/lsd/Source/Mixin/Resizable.js +3 -3
- data/Packages/lsd/Source/Mixin/Resource.js +1 -1
- data/Packages/lsd/Source/Mixin/Root.js +17 -1
- data/Packages/lsd/Source/Mixin/Scrollable.js +1 -1
- data/Packages/lsd/Source/{Trait → Mixin}/Slider.js +0 -0
- data/Packages/lsd/Source/Mixin/Sortable.js +2 -2
- data/Packages/lsd/Source/Mixin/Submittable.js +2 -1
- data/Packages/lsd/Source/Mixin/Target.js +11 -6
- data/Packages/lsd/Source/Mixin/Touchable.js +2 -2
- data/Packages/lsd/Source/Mixin/Unselectable.js +1 -1
- data/Packages/lsd/Source/Mixin/Uploader.js +11 -13
- data/Packages/lsd/Source/Mixin/Validity.js +35 -9
- data/Packages/lsd/Source/Mixin/Value.js +5 -3
- data/Packages/lsd/Source/Module/Accessories/Attributes.js +90 -89
- data/Packages/lsd/Source/Module/Accessories/Chain.js +40 -25
- data/Packages/lsd/Source/Module/Accessories/Element.js +59 -58
- data/Packages/lsd/Source/Module/Accessories/Events.js +25 -10
- data/Packages/lsd/Source/Module/Accessories/Options.js +11 -13
- data/Packages/lsd/Source/Module/Accessories/States.js +42 -5
- data/Packages/lsd/Source/Module/Accessories/Styles.js +1 -1
- data/Packages/lsd/Source/Module/Accessories/Tag.js +21 -7
- data/Packages/lsd/Source/Module/Ambient/Allocations.js +178 -64
- data/Packages/lsd/Source/Module/Ambient/DOM.js +98 -55
- data/Packages/lsd/Source/Module/Ambient/Expectations.js +57 -8
- data/Packages/lsd/Source/Module/Ambient/Interpolations.js +147 -0
- data/Packages/lsd/Source/Module/Ambient/Layout.js +52 -15
- data/Packages/lsd/Source/Module/Ambient/Proxies.js +44 -36
- data/Packages/lsd/Source/Module/Ambient/Relations.js +3 -1
- data/Packages/lsd/Source/Module/Ambient/Selectors.js +3 -3
- data/Packages/lsd/Source/Module/Ambient.js +2 -2
- data/Packages/lsd/Source/Module/Graphics/Layers.js +1 -1
- data/Packages/lsd/Source/Module/Graphics/Render.js +1 -1
- data/Packages/lsd/Source/Relation.js +19 -20
- data/Packages/lsd/Source/Sheet.js +4 -5
- data/Packages/lsd/Source/{Behavior.js → Tools/Behavior.js} +0 -0
- data/Packages/lsd/Source/Tools/Command.js +190 -0
- data/Packages/lsd/Source/Tools/Helpers.js +109 -0
- data/Packages/lsd/Source/Tools/Interpolation.js +351 -0
- data/Packages/lsd/Source/Tools/Microdata.js +75 -0
- data/Packages/lsd/Source/Tools/Object.js +192 -0
- data/Packages/lsd/Source/Tools/Position.js +208 -0
- data/Packages/lsd/Source/Tools/Require.js +76 -0
- data/Packages/lsd/Source/Type.js +2 -2
- data/Packages/lsd/package.yml +11 -7
- data/Packages/lsd-mobile/Source/Body/Dialog.js +2 -2
- data/Packages/lsd-mobile/Source/Input/Date.js +9 -84
- data/Packages/lsd-native/Source/Input/Checkbox.js +4 -4
- data/Packages/lsd-native/Source/Input/Date.js +46 -6
- data/Packages/lsd-native/Source/Input/Radio.js +1 -4
- data/Packages/lsd-native/Source/Input.js +5 -6
- data/Packages/lsd-widgets/Source/Body/Dialog.js +9 -6
- data/Packages/lsd-widgets/Source/Body/Page.js +1 -1
- data/Packages/lsd-widgets/Source/Body.js +1 -1
- data/Packages/lsd-widgets/Source/Button.js +1 -1
- data/Packages/lsd-widgets/Source/Form.js +1 -1
- data/Packages/lsd-widgets/Source/Input/Checkbox.js +2 -2
- data/Packages/lsd-widgets/Source/Input/Date.js +45 -16
- data/Packages/lsd-widgets/Source/Input/File.js +2 -2
- data/Packages/lsd-widgets/Source/Input/HTML.js +2 -2
- data/Packages/lsd-widgets/Source/Input/Radio.js +1 -1
- data/Packages/lsd-widgets/Source/Input/Range.js +2 -2
- data/Packages/lsd-widgets/Source/Input/Submit.js +1 -1
- data/Packages/lsd-widgets/Source/Input.js +2 -11
- data/Packages/lsd-widgets/Source/Label.js +4 -8
- data/Packages/lsd-widgets/Source/Menu/List.js +3 -3
- data/Packages/lsd-widgets/Source/Menu.js +2 -2
- data/Packages/lsd-widgets/Source/Progress.js +1 -1
- data/Packages/lsd-widgets/Source/Select.js +7 -6
- data/Packages/lsd-widgets/Source/Table/Calendar.js +41 -15
- data/Packages/lsd-widgets/Source/Table.js +7 -2
- data/Packages/mootools-ext/Source/Core/Class.Mixin.js +43 -38
- data/Packages/mootools-ext/Source/Core/Class.States.js +26 -22
- data/Packages/mootools-ext/Source/Element/Element.from.js +1 -1
- data/Packages/mootools-ext/Source/Element/Properties/Item.js +1 -2
- data/Packages/mootools-ext/Source/Request/Request.Statuses.js +9 -7
- data/Packages/mootools-ext/Source/Types/{FastArray.js → Object.Array.js} +8 -14
- data/Packages/mootools-ext/package.yml +1 -2
- data/lib/lsd.rake +28 -0
- metadata +15 -11
- data/Packages/lsd/Source/Command.js +0 -135
- data/Packages/lsd/Source/Interpolation.js +0 -103
- data/Packages/lsd/Source/Module/Ambient/Container.js +0 -56
- data/Packages/mootools-ext/Source/Element/Element.onDispose.js +0 -36
@@ -11,9 +11,10 @@ authors: Yaroslaff Fedin
|
|
11
11
|
|
12
12
|
requires:
|
13
13
|
- LSD
|
14
|
-
- More/Object.Extras
|
15
14
|
- LSD.Interpolation
|
16
|
-
-
|
15
|
+
- LSD.Helpers
|
16
|
+
- LSD.Microdata
|
17
|
+
- More/Object.Extras
|
17
18
|
|
18
19
|
provides:
|
19
20
|
- LSD.Layout
|
@@ -32,242 +33,322 @@ provides:
|
|
32
33
|
extract attributes and classes from elements.
|
33
34
|
*/
|
34
35
|
|
35
|
-
LSD.Layout = function(widget, layout,
|
36
|
+
LSD.Layout = function(widget, layout, memo) {
|
36
37
|
this.origin = widget;
|
37
|
-
this.setOptions(options);
|
38
|
-
this.context = LSD[this.options.context.capitalize()];
|
39
38
|
if (widget) if (!layout && !widget.lsd) {
|
40
39
|
layout = widget;
|
41
40
|
widget = null;
|
42
41
|
} else if (!widget.lsd) widget = this.convert(widget);
|
43
|
-
if (layout) this.render(layout, widget);
|
42
|
+
if (layout) this.render(layout, widget, memo);
|
44
43
|
};
|
45
44
|
|
46
|
-
LSD.Layout.
|
47
|
-
|
48
|
-
|
49
|
-
clone: false,
|
50
|
-
context: 'element',
|
51
|
-
interpolate: null
|
52
|
-
},
|
53
|
-
|
45
|
+
LSD.Layout.context = 'element';
|
46
|
+
|
47
|
+
LSD.Layout.prototype = Object.append({
|
54
48
|
$family: Function.from('layout'),
|
55
49
|
|
56
|
-
render: function(layout, parent,
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
50
|
+
render: function(layout, parent, memo) {
|
51
|
+
if (layout.getLayout) layout = layout.getLayout();
|
52
|
+
var type = (layout.push) ? 'array' : (layout.item && ('length' in layout)) ? 'children' :
|
53
|
+
layout.nodeType ? LSD.Layout.NodeTypes[layout.nodeType] : layout.indexOf ? 'string' : 'object';
|
54
|
+
var result = this[type](layout, parent, memo);
|
55
|
+
if (!this.result) this.result = result;
|
56
|
+
return result;
|
63
57
|
},
|
64
58
|
|
65
59
|
// type handlers
|
66
60
|
|
67
|
-
array: function(array, parent,
|
61
|
+
array: function(array, parent, memo) {
|
68
62
|
for (var i = 0, result = [], length = array.length; i < length; i++)
|
69
|
-
result[i] = this.render(array[i], parent,
|
63
|
+
result[i] = this.render(array[i], parent, memo)
|
70
64
|
return result;
|
71
65
|
},
|
72
66
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
67
|
+
/*
|
68
|
+
Process single element. The ultimate goal is to create a widget to be used with the given
|
69
|
+
element. But if the widget is not found, the element may still be used as a source of
|
70
|
+
microdata, or as an intermediate node in a complex mutation. It also handles cloning of nodes.
|
71
|
+
|
72
|
+
If a method is invoked on an element directly, it postpones a processing and enters a `walk`
|
73
|
+
loop instead. The walk loop takes care about all the child node iteration and prepares the
|
74
|
+
meta information to process an element in correct context of parents and siblings. Then it
|
75
|
+
calls `element` method back with all the data in `memo`.
|
76
|
+
|
77
|
+
An element then goes through a `match` routine, which iterates through things on the stack
|
78
|
+
and matches selectors against a node. There are two results of this action:
|
79
|
+
|
80
|
+
* A node may be mutated into a widget, if the mutation selector matches. A mutation provides
|
81
|
+
options for the widget, to be initialized shortly after. If multiple mutations match, their
|
82
|
+
options are merged together.
|
83
|
+
* A node may advance complex mutation selectors. If a node is a `<li>` element, and there's a
|
84
|
+
`li > a` selector on the stack, the match creates a new mutation `> a` to be used for
|
85
|
+
childnodes of the `<li>` element.
|
86
|
+
|
87
|
+
If there was a succesful mutation a widget is created. Otherwise, function attempts to convert
|
88
|
+
the node into widget (if the node is `<input type=text>` it will try to find `Input.Text` and
|
89
|
+
`Input` widgets). If there was no widget, but the layout is set to clone with `memo.clone`,
|
90
|
+
it clones the node.
|
91
|
+
|
92
|
+
Then, it appends the result (widget or node) into a parent node.
|
93
|
+
*/
|
94
|
+
|
95
|
+
element: function(element, parent, memo) {
|
96
|
+
// Prepare options and run walker (once per element tree)
|
97
|
+
if (!memo || !memo.walking) return this.walk(element, parent, memo);
|
98
|
+
this.match(element, memo);
|
99
|
+
var group, converted = element.uid && Element.retrieve(element, 'widget');
|
77
100
|
var ascendant = parent[0] || parent, container = parent[1] || parent.toElement();
|
78
|
-
// Retrieve the stack if the render was not triggered from the root of the layout
|
79
|
-
if (!stack) {
|
80
|
-
stack = [];
|
81
|
-
if ((group = ascendant.mutations['>'])) stack.push(group);
|
82
|
-
for (var node = ascendant; node; node = node.parentNode)
|
83
|
-
if ((group = node.mutations[' '])) stack.push(group);
|
84
|
-
//for (var node = ascendant; node; node = node.previousSibling) {
|
85
|
-
// if ((group = node.mutations['+'])) stack.push(group);
|
86
|
-
// if ((group = node.mutations['-'])) stack.push(group);
|
87
|
-
//}
|
88
|
-
}
|
89
|
-
// Match all selectors in the stack and find a right mutation
|
90
|
-
var index = stack.length;
|
91
|
-
if (index) {
|
92
|
-
var mutation, advanced, tagName = LSD.toLowerCase(element.tagName);
|
93
|
-
for (var i = index, item, result, ary = ['*', tagName]; item = stack[--i];)
|
94
|
-
for (var j = 0, value = item[1] || item, tag; tag = ary[j++];)
|
95
|
-
if ((group = value[tag]))
|
96
|
-
for (var k = 0, possibility, sel; possibility = group[k++];) {
|
97
|
-
var result = possibility[1];
|
98
|
-
if ((!mutation || (result && !result.indexOf)) && (sel = possibility[0])) {
|
99
|
-
if ((!sel.id && !sel.classes && !sel.attributes && !sel.pseudos) ? (tagName == sel.tag || j == 0) :
|
100
|
-
(Slick.matchSelector(element, sel.tag, sel.id, sel.classes, sel.attributes, sel.pseudos)))
|
101
|
-
if (!result || !result.call || (result = result(element)))
|
102
|
-
if (!result || !result.push) {
|
103
|
-
mutation = result || true;
|
104
|
-
} else (advanced || (advanced = [])).push(result);
|
105
|
-
}
|
106
|
-
}
|
107
|
-
}
|
108
|
-
// prepare options (once)
|
109
|
-
if (!opts || !opts.lazy) {
|
110
|
-
opts = Object.append({lazy: true}, opts);
|
111
|
-
if (this.options.context && LSD.Widget.prototype.options.context != this.options.context)
|
112
|
-
opts.context = this.options.context;
|
113
|
-
if (this.options.interpolation) opts.interpolation = this.options.interpolation;
|
114
|
-
var prepared = true;
|
115
|
-
}
|
116
101
|
// Create, clone or reuse a widget.
|
117
102
|
if (!converted) {
|
118
|
-
if (
|
119
|
-
|
120
|
-
|
103
|
+
if (!memo.defaults) memo.defaults = this.getOptions(memo, parent);
|
104
|
+
// Retrieve the widget type object that finds the appropriate classes for widgets
|
105
|
+
if (!memo.type) memo.type = this.getType(memo, parent);
|
106
|
+
if (memo.options) {
|
107
|
+
// If there are options produced by the selector matching routine, it's a widget
|
108
|
+
var widget = memo.type.create(element, memo.options);
|
121
109
|
} else {
|
122
|
-
|
110
|
+
// Otherwise, try converting the element (will turn <input type=date> into Input.Date)
|
111
|
+
var widget = memo.type.convert(element, memo.defaults);
|
123
112
|
}
|
124
113
|
} else {
|
125
|
-
var widget =
|
114
|
+
var widget = memo.clone ? converted.cloneNode(false) : converted;
|
126
115
|
}
|
127
116
|
// Append widget into parent widget without moving elements
|
128
117
|
if (widget) {
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
element.parentNode.replaceChild(widget.element, element);
|
136
|
-
}.bind(this);
|
137
|
-
this.appendChild(ascendant, widget, opts, override)
|
138
|
-
}
|
118
|
+
this.appendChild(ascendant, widget, memo, function(container, child) {
|
119
|
+
if (widget.origin == element && element.parentNode && element.parentNode == container) {
|
120
|
+
element.parentNode.replaceChild(widget.element, element);
|
121
|
+
} else if (!child.parentNode) return parent[1] || container;
|
122
|
+
return false;
|
123
|
+
});
|
139
124
|
} else {
|
140
|
-
if (
|
141
|
-
|
142
|
-
}
|
143
|
-
var newParent = [widget || ascendant, clone || (widget && widget.element) || element];
|
144
|
-
// Put away selectors in the stack that should not be matched again widget children
|
145
|
-
var group, direct, following;
|
146
|
-
for (var i = stack.length; group = stack[--i];) {
|
147
|
-
switch (group[0]) {
|
148
|
-
case '+':
|
149
|
-
stack.pop();
|
150
|
-
break;
|
151
|
-
case '~':
|
152
|
-
(following || (following = [])).push(stack.pop());
|
153
|
-
break;
|
154
|
-
case '>':
|
155
|
-
(direct || (direct = [])).push(stack.pop());
|
156
|
-
}
|
157
|
-
}
|
158
|
-
if (opts && opts.before) {
|
159
|
-
var before = opts.before;
|
160
|
-
delete opts.before;
|
125
|
+
if (memo.clone) var clone = element.cloneNode(false);
|
126
|
+
this.appendChild(container, clone || element, memo);
|
161
127
|
}
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
if (
|
178
|
-
options = {};
|
179
|
-
for (var option in opts) if (LSD.Layout.Inheritable[option]) options[option] = opts[option];
|
180
|
-
opts = options;
|
181
|
-
}
|
182
|
-
// Render children
|
128
|
+
return widget || clone || element;
|
129
|
+
},
|
130
|
+
|
131
|
+
/*
|
132
|
+
Process child nodes of an element. Child node collection is actually an array,
|
133
|
+
and should be given to the function as array. What makes `children` different from
|
134
|
+
`array` is that it keeps track of sibling combinators like `~` and `+` and maintains
|
135
|
+
the mutation stack, by collecting mutations and proxies from parent widget via `push`
|
136
|
+
& `pop`
|
137
|
+
*/
|
138
|
+
|
139
|
+
children: function(children, parent, memo) {
|
140
|
+
if (!memo) memo = {};
|
141
|
+
if (!memo.type) memo.type = this.getType(memo, parent);
|
142
|
+
var widget = (!parent[1] || parent[1] == parent[0].element);
|
143
|
+
if (widget) this.push(parent, memo);
|
183
144
|
for (var j = children.length - 1, child; j > -1 && (child = children[j]) && child.nodeType != 1; j--);
|
184
|
-
|
185
|
-
|
145
|
+
var args = [null, parent, memo];
|
146
|
+
for (var i = 0, child, previous, result = []; child = children[i]; i++) {
|
147
|
+
/*
|
148
|
+
Pick up selectors targetting on a node's next siblings
|
149
|
+
*/
|
186
150
|
if (previous && i) {
|
187
|
-
if ((group = previous.mutations['~'])) stack.push(['~', group]);
|
188
|
-
if ((group = previous.mutations['+'])) stack.push(['+', group]);
|
151
|
+
if ((group = previous.mutations['~'])) memo.stack.push(['~', group]);
|
152
|
+
if ((group = previous.mutations['+'])) memo.stack.push(['+', group]);
|
153
|
+
}
|
154
|
+
memo.last = (i == j);
|
155
|
+
memo.first = (i == 0);
|
156
|
+
args[0] = child;
|
157
|
+
/*
|
158
|
+
If the child is element, walk it again and render it there, otherwise render it right away
|
159
|
+
*/
|
160
|
+
if (child.nodeType == 1) {
|
161
|
+
previous = this.walk.apply(this, args);
|
162
|
+
if (!previous.lsd) previous = null;
|
163
|
+
} else {
|
164
|
+
this[LSD.Layout.NodeTypes[child.nodeType]].apply(this, args);
|
165
|
+
previous = null;
|
189
166
|
}
|
190
|
-
previous = this[LSD.Layout.NodeTypes[child.nodeType]](child, newParent, opts, stack, i == j);
|
191
|
-
if (!previous.lsd) previous = null;
|
192
|
-
}
|
193
|
-
// Put advanced selectors back to the stack
|
194
|
-
if (advanced) for (var i = 0; group = advanced[i++];)
|
195
|
-
if (group[0] != '+' || !last) stack.push(group);
|
196
|
-
// Put back selectors for next siblings
|
197
|
-
if (!last) {
|
198
|
-
if (following) for (var i = 0; group = following[i++];) stack.push(group);
|
199
|
-
if (direct) for (var i = 0; group = direct[i++];) stack.push(group);
|
200
167
|
}
|
201
|
-
|
202
|
-
|
203
|
-
return
|
168
|
+
delete memo.last; delete memo.first;
|
169
|
+
if (widget) this.pop(parent, memo)
|
170
|
+
return children;
|
204
171
|
},
|
205
172
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
173
|
+
/*
|
174
|
+
Process a single textnode. It may be cloned and/or interpolated. Interpolation is a process
|
175
|
+
of finding variable expressions in text content. LSD uses `{hello}` syntax to embed variables
|
176
|
+
in text. There are multiple ways of adding values for those variables in widgets - selectors,
|
177
|
+
field values, microdata and custom dictionaries.
|
178
|
+
*/
|
179
|
+
|
180
|
+
textnode: function(element, parent, memo) {
|
181
|
+
if (memo && memo.clone) var clone = element.cloneNode(false);
|
182
|
+
this.appendChild(parent, clone || element, memo);
|
183
|
+
LSD.Interpolation.textnode(clone || element, parent[0] || parent);
|
184
|
+
return clone || element;
|
216
185
|
},
|
217
186
|
|
218
|
-
|
219
|
-
|
187
|
+
/*
|
188
|
+
Process a single comment node. LSD uses comments as boundaries of conditional blocks of HTML.
|
189
|
+
A comment like `<!-- if hello -->` will make all contents after the comment definition on that
|
190
|
+
level be displayed only if `hello` expression evaluates to true. Expressions are in fact
|
191
|
+
interpolations that are bound to show or hide parts of layout.
|
192
|
+
*/
|
193
|
+
|
194
|
+
comment: function(comment, parent, memo) {
|
195
|
+
var keyword = Element.retrieve(comment, 'keyword');
|
196
|
+
this.appendChild(parent, comment, memo);
|
197
|
+
if (keyword) return keyword === true ? comment : keyword;
|
198
|
+
else keyword = this.keyword(comment.nodeValue, parent, memo, comment);
|
199
|
+
if (keyword) {
|
200
|
+
Element.store(comment, 'keyword', keyword);
|
201
|
+
if (keyword !== true) (memo.branches || (memo.branches = [])).push(keyword);
|
202
|
+
return keyword;
|
203
|
+
} else return comment;
|
220
204
|
},
|
221
205
|
|
222
|
-
|
223
|
-
|
224
|
-
|
206
|
+
/*
|
207
|
+
Process a document fragment. The best way to build and process HTML DOM elements is to do
|
208
|
+
all the things in memory first, and then inject the already-prepared element tree into the
|
209
|
+
document. Fragments are used to create a virtual DOM tree from a HTML string via
|
210
|
+
document.createFragment(html). They are also useful, because they can operate on multiple nodes
|
211
|
+
coming from a fragment. So there may be more than one root node parsed from HTML, but they
|
212
|
+
are processed as a single fragment node.
|
213
|
+
*/
|
214
|
+
|
215
|
+
fragment: function(element, parent, memo) {
|
216
|
+
return this.children(LSD.slice(element.childNodes), parent, memo);
|
225
217
|
},
|
226
218
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
}
|
219
|
+
/*
|
220
|
+
Creates a text node from a string and try to interpolate it.
|
221
|
+
*/
|
222
|
+
|
223
|
+
string: function(string, parent, memo) {
|
224
|
+
var element = parent[1] || parent.toElement();
|
225
|
+
var textnode = element.ownerDocument.createTextNode(string);
|
226
|
+
this.appendChild(element, textnode, memo);
|
227
|
+
LSD.Interpolation.textnode(textnode, parent[0] || parent);
|
237
228
|
return textnode;
|
238
229
|
},
|
239
230
|
|
240
|
-
|
241
|
-
|
231
|
+
/*
|
232
|
+
Create from a javascript object layout template.
|
233
|
+
*/
|
234
|
+
|
235
|
+
object: function(object, parent, memo) {
|
236
|
+
var result = {}, layout, branch;
|
242
237
|
for (var selector in object) {
|
243
|
-
layout = object[selector] === true ? null : object[selector];
|
244
|
-
if (
|
245
|
-
|
238
|
+
layout = object[selector] === true || !object[selector] ? null : object[selector];
|
239
|
+
if (!memo) memo = {};
|
240
|
+
if ((branch = this.keyword(selector, parent, memo))) {
|
241
|
+
(memo.branches || (memo.branches = [])).push(branch);
|
242
|
+
branch.setLayout(layout, true);
|
243
|
+
result[selector] = [branch, layout];
|
246
244
|
} else {
|
247
|
-
branch
|
248
|
-
|
249
|
-
|
245
|
+
if (branch && memo && memo.branches && memo.branches[memo.branches.length - 1] == branch) {
|
246
|
+
memo.branches.pop();
|
247
|
+
branch = null;
|
248
|
+
}
|
249
|
+
var rendered = this.selector(selector, parent, memo);
|
250
|
+
if (rendered.promise) {
|
251
|
+
rendered.children = layout;
|
252
|
+
result[selector] = [rendered, layout];
|
253
|
+
} else {
|
254
|
+
result[selector] = [rendered, !layout || this.render(layout, rendered.lsd ? rendered : [parent[0] || parent, rendered], null, memo)];
|
255
|
+
}
|
250
256
|
}
|
251
257
|
}
|
258
|
+
if (memo.predecessors) delete memo.predecessors;
|
252
259
|
return result;
|
253
260
|
},
|
254
261
|
|
255
|
-
|
256
|
-
|
257
|
-
|
262
|
+
/*
|
263
|
+
Builds a single node from a selector. It accepts raw selectors in form of strings
|
264
|
+
and also options set. The result may be either element or widget.
|
265
|
+
|
266
|
+
The method creates widget if:
|
267
|
+
|
268
|
+
* If a tag name is not a standart HTML element tag name. A custom tag means widget.
|
269
|
+
* A tag name and attributes combination matches a known widget class name
|
270
|
+
in current scope. e.g. `input[type=text][kind=colorful]` matches
|
271
|
+
`Input.Text.Colorful` class name. Only `type` and `kind` attribute have the
|
272
|
+
subclassing semantics.
|
273
|
+
* Selector is given as parsed options object and `source` option is given.
|
274
|
+
It makes the method skip all tests and build the widget with that source.
|
275
|
+
It often happens after element mutation and results in a source expression,
|
276
|
+
which is basically another selector that has enough information about the
|
277
|
+
widget class to be used.
|
278
|
+
|
279
|
+
If a tag name in selector has dashes which makes the method treat it like a `source`
|
280
|
+
option described above. For example, `p-more[name=signin]` does not imply `p` tag,
|
281
|
+
but `p-more` source which translates to P.More class name. If the actual class
|
282
|
+
object is not found by that name, a widget is created without specific role.
|
283
|
+
|
284
|
+
If a `memo.lazy` flag is set, the node is not rendered immediately. Function
|
285
|
+
returns a `Promise` object that keeps all options around. Those options later
|
286
|
+
may be passed to this `selector` method again to be finally built. Promise object
|
287
|
+
is compatible with Proxy options object and is used that way by an `object` layout
|
288
|
+
method. Proxies set expectation of an element that was meant to be rendered and
|
289
|
+
lets other parts of layout to be rendered. While those parts are rendered,
|
290
|
+
the elements being processed and matched against all promise expectations.
|
291
|
+
That allows layout engine to reuse DOM nodes that happen to match the selector
|
292
|
+
instead of building its own new DOM nodes.
|
293
|
+
*/
|
294
|
+
|
295
|
+
selector: function(string, parent, memo) {
|
296
|
+
if (string.match) var options = LSD.Layout.parse(string, parent);
|
297
|
+
else var options = string;
|
298
|
+
if (!memo) memo = {};
|
299
|
+
if (!memo.type) memo.type = this.getType(memo, parent);
|
300
|
+
var source = options.source;
|
301
|
+
if (source && source.match && !source.match(LSD.Layout.rSimpleSource)) {
|
302
|
+
delete options.source;
|
303
|
+
Object.merge(options, LSD.Layout.parse(source, parent));
|
304
|
+
}
|
305
|
+
if (options.allocation || options.source || (options.tag && (memo.type.find(options.tag) || !LSD.Layout.NodeNames[options.tag]))) {
|
258
306
|
var allocation = options.allocation;
|
259
|
-
|
260
|
-
|
261
|
-
if (
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
307
|
+
// If a pseudo class is recognized as allocatable widget,
|
308
|
+
// it needs to retrieve the selectors that gives additional options
|
309
|
+
if (allocation) {
|
310
|
+
if (memo.lazy) {
|
311
|
+
var allocated = (parent[0] || parent).preallocate(allocation);
|
312
|
+
// The node is prematurely built, because allocation is not lazy
|
313
|
+
if (allocated.nodeType) return allocated;
|
314
|
+
if (!allocated.source) throw "Allocation of type " + allocation.type + " did not provide `source` selector to build nodes"
|
315
|
+
memo.options = allocated;
|
316
|
+
var selector = allocated.selector || allocated.source;
|
317
|
+
if (options.order) selector += options.order;
|
318
|
+
if (options.combinator) selector = options.order + selector;
|
319
|
+
// Parse selector coming from allocation and pass the promise
|
320
|
+
return this.selector(selector, parent, memo);
|
321
|
+
} else {
|
322
|
+
var widget = (parent[0] || parent).allocate(allocation.type, allocation.kind, allocation.options);
|
323
|
+
if (widget.parentNode) {
|
324
|
+
if (widget.lsd) parent = [widget.parentNode, (widget.element && widget.element.parentNode) || widget.parentNode.element]
|
325
|
+
else parent = [parent[0] || parent, widget.parentNode];
|
326
|
+
}
|
327
|
+
}
|
328
|
+
} else {
|
329
|
+
if (!memo.defaults) memo.defaults = this.getOptions(memo, parent);
|
330
|
+
var opts = Object.append({}, memo.defaults, memo.options, options);
|
331
|
+
if (memo.lazy) return new LSD.Layout.Promise(this, string, true, parent, opts, memo);
|
332
|
+
if (options.combinator) {
|
333
|
+
memo.combinator = options.combinator;
|
334
|
+
delete options.combinator;
|
335
|
+
}
|
336
|
+
var widget = memo.type.create(opts);
|
337
|
+
}
|
338
|
+
if (widget.element && widget.element.childNodes.length) var nodes = LSD.slice(widget.element.childNodes);
|
339
|
+
this.appendChild(parent, widget, memo, parent[1]);
|
340
|
+
if (nodes && nodes.length) this.children(nodes, [widget, widget.element], memo);
|
269
341
|
} else {
|
270
|
-
|
342
|
+
if (memo.options) {
|
343
|
+
options = Object.merge(memo.options, options);
|
344
|
+
delete memo.options;
|
345
|
+
}
|
346
|
+
if (options.combinator) {
|
347
|
+
memo.combinator = options.combinator;
|
348
|
+
delete options.combinator;
|
349
|
+
}
|
350
|
+
if (memo.lazy) return new LSD.Layout.Promise(this, string, false, parent, options, memo);
|
351
|
+
var props = {}, tag = (!options.tag || options.tag == '*') ? 'div' : options.tag;
|
271
352
|
if (options.id) props.id = options.id;
|
272
353
|
var attributes = options.attributes;
|
273
354
|
if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){
|
@@ -278,116 +359,839 @@ LSD.Layout.prototype = Object.append(new Options, {
|
|
278
359
|
}
|
279
360
|
var element = document.createElement(tag);
|
280
361
|
for (var name in props) element.setAttribute(name, props[name]);
|
281
|
-
if (options.classes) element.className =
|
282
|
-
if (parent) this.appendChild(parent
|
362
|
+
if (options.classes) element.className = LSD.Object.prototype.join.call(options.classes, ' ');
|
363
|
+
if (parent) this.appendChild(parent, element, memo);
|
364
|
+
if (options.content) element.innerHTML = options.content;
|
283
365
|
}
|
284
366
|
return widget || element;
|
285
367
|
},
|
286
368
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
369
|
+
keyword: function(text, parent, memo, element) {
|
370
|
+
var parsed = LSD.Layout.extractKeyword(text);
|
371
|
+
if (!parsed) return;
|
372
|
+
var keyword = LSD.Layout.Keyword[parsed.keyword];
|
373
|
+
if (!keyword) return;
|
374
|
+
var options = keyword(parsed.expression);
|
375
|
+
var parentbranch = memo.branches && memo.branches[memo.branches.length - 1];
|
376
|
+
if (options.ends || options.link) {
|
377
|
+
if (!(options.superbranch = (memo.branches && memo.branches.pop())))
|
378
|
+
throw "Alternative branch is missing its original branch";
|
379
|
+
var node = options.superbranch.options.element;
|
380
|
+
if (node) {
|
381
|
+
for (var layout = []; (node = node.nextSibling) != element;) layout.push(node);
|
382
|
+
options.superbranch.setLayout(layout);
|
383
|
+
}
|
384
|
+
if (element && options.superbranch.options.clean) {
|
385
|
+
element.parentNode.removeChild(element);
|
386
|
+
element = options.superbranch.options.element;
|
387
|
+
if (element) element.parentNode.removeChild(element);
|
388
|
+
}
|
389
|
+
if (options.ends) return true;
|
390
|
+
}
|
391
|
+
if (options.branch) {
|
392
|
+
options.keyword = parsed.keyword;
|
393
|
+
options.parent = parentbranch;
|
394
|
+
options.widget = parent[0] || parent;
|
395
|
+
options.element = element;
|
396
|
+
return new LSD.Layout.Branch(options);
|
397
|
+
} else {
|
398
|
+
if (options.layout) {
|
399
|
+
return this.render(options.layout);
|
400
|
+
}
|
293
401
|
}
|
294
402
|
},
|
295
403
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
404
|
+
/*
|
405
|
+
Remove rendered content from DOM. It only removes argument from DOM, keeping
|
406
|
+
all of its contents untouched.
|
407
|
+
|
408
|
+
The function accepts all kind of arguments, but it works best when paired with
|
409
|
+
results of `render()`. This way the meta-constructs like conditions and loops
|
410
|
+
will be preserved and gracefully removed.
|
411
|
+
*/
|
412
|
+
remove: function(layout, parent, memo) {
|
413
|
+
return this.set(layout, parent, memo, false)
|
414
|
+
},
|
415
|
+
|
416
|
+
/*
|
417
|
+
Re-add once rendered content that was removed. When a `render`ed chunk of layout
|
418
|
+
was removed, `add` is the function that will gracefully add it back.
|
419
|
+
*/
|
420
|
+
add: function(layout, parent, memo) {
|
421
|
+
return this.set(layout, parent, memo, true)
|
422
|
+
},
|
423
|
+
|
424
|
+
set: function(layout, parent, memo, state) {
|
425
|
+
switch (typeOf(layout)) {
|
426
|
+
case "array": case "collection":
|
427
|
+
for (var i = 0, j = layout.length, value; i < j; i++)
|
428
|
+
if ((value = layout[i])) this.manipulate(state, parent, value, memo);
|
305
429
|
break;
|
306
|
-
case "
|
307
|
-
|
308
|
-
|
430
|
+
case "object":
|
431
|
+
for (var key in layout) {
|
432
|
+
var value = layout[key];
|
433
|
+
if (value && (value = value[0])) this.manipulate(state, parent, value, memo);
|
434
|
+
}
|
309
435
|
break;
|
310
|
-
case "
|
311
|
-
|
436
|
+
case "widget": case "string":
|
437
|
+
return this.manipulate(state, parent, layout);
|
312
438
|
}
|
313
|
-
return
|
439
|
+
return layout;
|
314
440
|
},
|
315
441
|
|
316
|
-
|
317
|
-
if (
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
442
|
+
manipulate: function(state, parent, child, memo) {
|
443
|
+
if (state) {
|
444
|
+
this.appendChild(parent, child, memo);
|
445
|
+
} else {
|
446
|
+
this.removeChild(child.parentNode || parent, child, memo)
|
447
|
+
}
|
448
|
+
if (child.nodeType == 8) {
|
449
|
+
var keyword = Element.retrieve(child, 'keyword');
|
450
|
+
if (keyword && keyword !== true) keyword[state ? 'attach' : 'detach']();
|
451
|
+
}
|
452
|
+
return child;
|
453
|
+
},
|
454
|
+
|
455
|
+
/*
|
456
|
+
Places a node in a parent node. The function appends node in the end of
|
457
|
+
the parent node by default. Alternatively, a node given with `memo.before`
|
458
|
+
will be used as a place to insert before.
|
459
|
+
|
460
|
+
`override` function may be used when a widget is inserted into another widget
|
461
|
+
but DOM manipulation should be altered in some way.
|
462
|
+
|
463
|
+
A function may be given proxies stack with `memo.proxies`. The stack is then
|
464
|
+
be iterated and proxies are matched against the child node. Upon a succesful
|
465
|
+
match, a proxy function returns an object with values that may specify a new
|
466
|
+
`parent`, `override` or `before` variables for manipulation.
|
467
|
+
*/
|
468
|
+
|
469
|
+
appendChild: function(parents, child, memo, override) {
|
470
|
+
if (parents.push) var widget = parents[0], element = parents[1];
|
471
|
+
else if (parents.lsd) var widget = parents, element = widget.element || widget.toElement();
|
472
|
+
else var element = parents;
|
473
|
+
var parent = child.lsd ? widget : element, before;
|
474
|
+
if (memo && memo.combinator) {
|
475
|
+
var combinator = LSD.Layout.Combinators[memo.combinator];
|
476
|
+
if (combinator) {
|
477
|
+
var result = combinator(parent, child);
|
478
|
+
if (result) {
|
479
|
+
if (result.parent) parent = result.parent;
|
480
|
+
if (result.before) before = result.before;
|
481
|
+
}
|
323
482
|
}
|
324
|
-
|
325
|
-
|
326
|
-
|
483
|
+
delete memo.combinator;
|
484
|
+
}
|
485
|
+
if (memo && memo.proxies)
|
486
|
+
proxy: for (var i = 0, group, proxies, close; group = memo.proxies[i++];) {
|
487
|
+
proxies = group[1];
|
488
|
+
close = element == group[0];
|
489
|
+
for (var j = 0, proxy; proxy = proxies[j++];) {
|
490
|
+
if (close || proxy.deep || proxy.element == element) {
|
491
|
+
if (LSD.Module.Proxies.match(child, proxy, group[0])) {
|
492
|
+
var result = LSD.Module.Proxies.invoke(parent, child, proxy, memo);
|
493
|
+
if (result) {
|
494
|
+
if (result.parent) parent = result.parent;
|
495
|
+
if (result.override) override = result.override;
|
496
|
+
if (result.before) before = result.before;
|
497
|
+
break proxy
|
498
|
+
} else if (result === false) return false;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
}
|
327
502
|
}
|
328
|
-
|
503
|
+
if (before || (memo && (before = memo.before))) {
|
504
|
+
if (!parent.lsd) {
|
505
|
+
if (before.lsd) before = before.toElement();
|
506
|
+
parent = before.parentNode;
|
507
|
+
};
|
508
|
+
parent.insertBefore(child, before, override);
|
509
|
+
} else {
|
510
|
+
if (child.parentNode == parent) return;
|
511
|
+
parent.appendChild(child, override);
|
512
|
+
}
|
513
|
+
if (child.lsd) {
|
514
|
+
var doc = parent.document;
|
515
|
+
if (child.document != doc) child.setDocument(doc);
|
329
516
|
}
|
517
|
+
return true;
|
330
518
|
},
|
331
519
|
|
332
520
|
removeChild: function(parent, child, override) {
|
333
|
-
if (child.
|
334
|
-
|
335
|
-
|
521
|
+
if (parent.push) parent = parent[child.lsd ? 0 : 1] || parent;
|
522
|
+
if (!child.lsd && parent.lsd) parent = parent.toElement();
|
523
|
+
if (child.parentNode != parent) return;
|
524
|
+
parent.removeChild(child, override);
|
525
|
+
return true;
|
526
|
+
},
|
527
|
+
|
528
|
+
/*
|
529
|
+
Realize layout promises, reuse found elements and build
|
530
|
+
the ones that are missing
|
531
|
+
*/
|
532
|
+
realize: function(layout, memo) {
|
533
|
+
if (!memo) memo = {};
|
534
|
+
if (layout.hasOwnProperty('length')) {
|
535
|
+
for (var i = 0, j = layout.length, value; i < j; i++)
|
536
|
+
if ((value = layout[i])) layout[i] = this.realize(value, memo);
|
537
|
+
} else if (layout.nodeType) {
|
538
|
+
return;
|
539
|
+
} else if (!layout.promise) {
|
540
|
+
for (var key in layout) {
|
541
|
+
var row = layout[key];
|
542
|
+
var value = row[0]
|
543
|
+
var children = row[1];
|
544
|
+
if (value) {
|
545
|
+
if (value.promise) {
|
546
|
+
row[0] = value.result || value.realize(memo);
|
547
|
+
row[1] = children = value.advanced;
|
548
|
+
}
|
549
|
+
if (children && children !== true) this.realize(children, memo);
|
550
|
+
}
|
551
|
+
}
|
552
|
+
} else {
|
553
|
+
return layout.result || layout.realize(memo);
|
554
|
+
}
|
555
|
+
},
|
556
|
+
|
557
|
+
/*
|
558
|
+
Match all selectors in the stack and finds a right mutation
|
559
|
+
*/
|
560
|
+
match: function(element, memo, soft) {
|
561
|
+
var stack = memo.stack
|
562
|
+
var options, advanced, tagName = LSD.toLowerCase(element.tagName);
|
563
|
+
/*
|
564
|
+
Matching process happens twice. First time, it matches against
|
565
|
+
selectors on the stack with no regard to tag name (as `*`),
|
566
|
+
and the other time it takes tag name into account
|
567
|
+
*/
|
568
|
+
for (var i = stack.length, item, result, ary = ['*', tagName]; item = stack[--i];)
|
569
|
+
for (var j = 0, value = item[1] || item, tag; tag = ary[j++];)
|
570
|
+
if ((group = value[tag]))
|
571
|
+
for (var k = 0, possibility, exp; possibility = group[k++];) {
|
572
|
+
var exp = possibility[0], result = possibility[1];
|
573
|
+
if ((!exp.classes && !exp.attributes && !exp.pseudos)
|
574
|
+
// Quickly match tag and id, if other things dont matter
|
575
|
+
? ((j == 0 || tagName == exp.tag) && (!exp.id || element.id == exp.id))
|
576
|
+
// Or do a full match
|
577
|
+
: (Slick.matchSelector(element, exp.tag, exp.id, exp.classes, exp.attributes, exp.pseudos)))
|
578
|
+
/*
|
579
|
+
If selector matches, proceed and execute callback
|
580
|
+
A callback may be a:
|
581
|
+
|
582
|
+
* **string**, to be evaluated as a mutation selector
|
583
|
+
and parsed into options
|
584
|
+
* **object**, with options for widget
|
585
|
+
* **a function**, that may return a group of mutations
|
586
|
+
that should be applied to the following elements
|
587
|
+
* **true**, to initialize widget on that element with
|
588
|
+
no specific options.
|
589
|
+
|
590
|
+
An element may match more than one mutation. In
|
591
|
+
that case options extracted from parsing selectors
|
592
|
+
will be merged together.
|
593
|
+
|
594
|
+
If callbacks produced options, the widget will
|
595
|
+
be initialized on that element with those options.
|
596
|
+
|
597
|
+
`soft` parameter tells matcher to skip mutations
|
598
|
+
and only advance selectors instead.
|
599
|
+
*/
|
600
|
+
if (!result || !result.call || (result = result(element))) {
|
601
|
+
if (!result) result = true;
|
602
|
+
if (result.push) {
|
603
|
+
(advanced || (advanced = [])).push(result);
|
604
|
+
} else if (!soft) {
|
605
|
+
if (!options) options = this.getOptions(memo);
|
606
|
+
if (result !== true)
|
607
|
+
options = LSD.reverseMerge(options, result.match ? LSD.Layout.parse(result) : result);
|
608
|
+
}
|
609
|
+
}
|
610
|
+
}
|
611
|
+
if (advanced) memo.advanced = advanced;
|
612
|
+
if (options) memo.options = options;
|
613
|
+
},
|
614
|
+
|
615
|
+
/*
|
616
|
+
Collect mutations and proxies from the widget. When it is first called
|
617
|
+
on a memo that doesnt have things collected from parent widgets,
|
618
|
+
it attempts to walk up and restore the context. Restored mutation stack
|
619
|
+
is limited to parents, so ~ and + combinators are not processed on parent
|
620
|
+
nodes for speed. So only > and <space> combiantors will be collected.
|
621
|
+
*/
|
622
|
+
|
623
|
+
push: function(parent, memo) {
|
624
|
+
var group, stack = memo.stack;
|
625
|
+
if (stack) {
|
626
|
+
var widget = parent[0] || parent;
|
627
|
+
/*
|
628
|
+
Collect mutations from a widget
|
629
|
+
*/
|
630
|
+
if ((group = widget.mutations[' '])) stack.push([' ', group]);
|
631
|
+
if ((group = widget.mutations['>'])) stack.push(['>', group]);
|
632
|
+
if (widget.proxies) (memo.proxies || (memo.proxies = [])).push([parent[1] || parent.element, widget.proxies]);
|
633
|
+
} else {
|
634
|
+
var element = parent[1] || parent.element;
|
635
|
+
var stack = memo.stack = [], direct;
|
636
|
+
for (var parents = [], node = element; node && node.nodeType == 1; node = node.parentNode)
|
637
|
+
parents.push(node);
|
638
|
+
for (var i = parents.length, node, widget; node = parents[--i];) {
|
639
|
+
this.match(node, memo, true);
|
640
|
+
if (direct) {
|
641
|
+
for (var j = 0, index; index = direct[j++];) stack.splice(index, 1);
|
642
|
+
direct = null;
|
643
|
+
}
|
644
|
+
if (memo.advanced) {
|
645
|
+
stack.push.apply(stack, memo.advanced);
|
646
|
+
reduce: for (var j = stack.length; group = stack[--j];) {
|
647
|
+
switch (group[0]) {
|
648
|
+
case '+': case '~':
|
649
|
+
stack.splice(j, 1);
|
650
|
+
break;
|
651
|
+
case '>':
|
652
|
+
(direct || (direct = [])).push(j);
|
653
|
+
break;
|
654
|
+
default:
|
655
|
+
break reduce;
|
656
|
+
}
|
657
|
+
}
|
658
|
+
delete memo.advanced;
|
659
|
+
}
|
660
|
+
widget = node.uid && LSD.Module.DOM.find(node, true);
|
661
|
+
if (widget) {
|
662
|
+
if ((group = widget.mutations[' '])) stack.push([' ', group]);
|
663
|
+
if (i == 0 && (group = widget.mutations['>'])) stack.push(['>', group]);
|
664
|
+
if (widget.proxies) (memo.proxies || (memo.proxies = [])).push([node, widget.proxies]);
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
},
|
669
|
+
|
670
|
+
/*
|
671
|
+
Remove proxies that were collected from given widget from
|
672
|
+
current stacks.
|
673
|
+
*/
|
674
|
+
|
675
|
+
pop: function(parent, memo) {
|
676
|
+
var group, widget = parent[0] || parent, stack = memo.stack;
|
677
|
+
if (stack) {
|
678
|
+
if ((group = widget.mutations[' ']))
|
679
|
+
for (var j = stack.length; --j > -1;)
|
680
|
+
if (stack[j][1] == group) {
|
681
|
+
stack.splice(j, 1)
|
682
|
+
break;
|
683
|
+
}
|
684
|
+
if ((group = widget.mutations['>']))
|
685
|
+
for (var j = stack.length; --j > -1;)
|
686
|
+
if (stack[j][1] == group) {
|
687
|
+
stack.splice(j, 1)
|
688
|
+
break;
|
689
|
+
}
|
690
|
+
}
|
691
|
+
if (widget.proxies && memo.proxies) {
|
692
|
+
for (var j = memo.proxies.length, group; group = memo.proxies[--j];)
|
693
|
+
if (group[1] == widget.proxies) {
|
694
|
+
memo.proxies.splice(j, 1);
|
695
|
+
break;
|
696
|
+
}
|
697
|
+
}
|
698
|
+
},
|
699
|
+
|
700
|
+
walk: function(element, parent, memo) {
|
701
|
+
var ascendant = parent[0] || parent;
|
702
|
+
/*
|
703
|
+
Retrieve the stack if the render was not triggered from the root of the layout
|
704
|
+
*/
|
705
|
+
if (!memo) memo = {};
|
706
|
+
memo.walking = true;
|
707
|
+
var stack = memo.stack;
|
708
|
+
if (!stack) {
|
709
|
+
this.push(parent, memo);
|
710
|
+
stack = memo.stack;
|
711
|
+
}
|
712
|
+
/*
|
713
|
+
Render the given element (may clone element or translate it to widget)
|
714
|
+
*/
|
715
|
+
var children = LSD.slice(element.childNodes);
|
716
|
+
var ret = this.element(element, parent, memo);
|
717
|
+
if (ret.lsd) var widget = ret;
|
718
|
+
else if (ret.branch) {
|
719
|
+
(memo.branches || (memo.branches = [])).push(ret);
|
720
|
+
} else if (memo.clone) var clone = ret;
|
721
|
+
/*
|
722
|
+
Put away selectors in the stack that should not be matched against element's child nodes
|
723
|
+
*/
|
724
|
+
var group, direct, following;
|
725
|
+
reduce: for (var i = stack.length; group = stack[--i];) {
|
726
|
+
switch (group[0]) {
|
727
|
+
case '+':
|
728
|
+
stack.pop();
|
729
|
+
break;
|
730
|
+
case '~':
|
731
|
+
(following || (following = [])).push(stack.pop());
|
732
|
+
break;
|
733
|
+
case '>':
|
734
|
+
(direct || (direct = [])).push(stack.pop());
|
735
|
+
break;
|
736
|
+
default:
|
737
|
+
break reduce;
|
738
|
+
}
|
739
|
+
}
|
740
|
+
/*
|
741
|
+
Collect mutations that advanced with this element AND are looking for children
|
742
|
+
*/
|
743
|
+
var advanced = memo.advanced;
|
744
|
+
if (advanced) {
|
745
|
+
map: for (var i = 0, group; group = advanced[i]; i++) {
|
746
|
+
switch (group[0]) {
|
747
|
+
case ' ': case '>':
|
748
|
+
advanced.splice(i--, 1);
|
749
|
+
stack.push(group);
|
750
|
+
break;
|
751
|
+
default:
|
752
|
+
break map;
|
753
|
+
}
|
754
|
+
}
|
755
|
+
delete memo.advanced;
|
336
756
|
}
|
757
|
+
delete memo.options;
|
758
|
+
/*
|
759
|
+
Put away reversed insertion direction option, because it does not affect child nodes
|
760
|
+
*/
|
761
|
+
var before = memo.before;
|
762
|
+
if (before) delete memo.before;
|
763
|
+
/*
|
764
|
+
Scan element for microdata
|
765
|
+
*/
|
766
|
+
var itempath = memo.itempath;
|
767
|
+
var scope = LSD.Microdata.element(element, widget || ascendant, itempath && itempath[itempath.length - 1]);
|
768
|
+
if (scope) (itempath || (itempath = memo.itempath = [])).push(scope);
|
769
|
+
if (widget && itempath) widget.itempath = itempath;
|
770
|
+
/*
|
771
|
+
Prepare parent array - first item is a nearest parent widget and second is a direct parent element
|
772
|
+
*/
|
773
|
+
var newParent = [widget || ascendant, clone || (widget && widget.element) || element];
|
774
|
+
var ascendant = parent[0] || parent;
|
775
|
+
/*
|
776
|
+
Iterate children
|
777
|
+
*/
|
778
|
+
var first = memo.first, last = memo.last;
|
779
|
+
if (children.length) this.children(children, newParent, memo);
|
780
|
+
/*
|
781
|
+
Restore reversed insertion direction
|
782
|
+
*/
|
783
|
+
if (before) memo.before = before;
|
784
|
+
/*
|
785
|
+
Put advanced selectors back to the stack
|
786
|
+
*/
|
787
|
+
if (advanced) for (var i = 0; group = advanced[i++];)
|
788
|
+
if (group[0] != '+' || !last) stack.push(group);
|
789
|
+
/*
|
790
|
+
Put back selectors for next siblings
|
791
|
+
*/
|
792
|
+
if (!last) {
|
793
|
+
if (direct) for (var i = 0; group = direct[i++];) stack.push(group);
|
794
|
+
if (following) for (var i = 0; group = following[i++];) stack.push(group);
|
795
|
+
}
|
796
|
+
/*
|
797
|
+
Reduce the microdata path
|
798
|
+
*/
|
799
|
+
if (scope) itempath.pop();
|
800
|
+
/*
|
801
|
+
Notify widget that children are processed
|
802
|
+
*/
|
803
|
+
if (widget) widget.fireEvent('DOMChildNodesRendered')
|
804
|
+
return ret;
|
805
|
+
},
|
806
|
+
|
807
|
+
getOptions: function(memo, parent) {
|
808
|
+
return {
|
809
|
+
lazy: true,
|
810
|
+
context: memo.context || (parent && (parent[0] || parent).options.context) || LSD.Layout.context
|
811
|
+
};
|
812
|
+
},
|
813
|
+
|
814
|
+
getType: function(memo, parent) {
|
815
|
+
var context = memo.context || (parent && (parent[0] || parent).options.context) || LSD.Layout.context;
|
816
|
+
return LSD[LSD.toClassName(context)];
|
337
817
|
}
|
338
818
|
});
|
339
819
|
|
820
|
+
LSD.Layout.rSimpleSource = /^[a-z-_0-9]+$/;
|
821
|
+
/*
|
822
|
+
Combinator methods are used in appendChild/removeChild manipulations.
|
823
|
+
All of those (and more) are also implemented for matching in Slick.
|
824
|
+
|
825
|
+
When the layout is pattern matched, Slick first looks for nodes that
|
826
|
+
satisfy selector expressions with combinator to reuse. But if it does
|
827
|
+
not find those, LSD builds them there.
|
828
|
+
|
829
|
+
Combinators preceeded with exclaimation mark, are Slick's invention.
|
830
|
+
It logically inverts the meaning of combinator, so where a `+` means
|
831
|
+
next node, `!+` means previous node.
|
832
|
+
*/
|
833
|
+
LSD.Layout.Combinators = {
|
834
|
+
/*
|
835
|
+
Insert a node next to the parent
|
836
|
+
*/
|
837
|
+
'+': function(parent, child) {
|
838
|
+
return {parent: parent.parentNode, before: parent.nextSibling};
|
839
|
+
},
|
840
|
+
/*
|
841
|
+
Insert a node previous to the parent
|
842
|
+
*/
|
843
|
+
'!+': function(parent, child) {
|
844
|
+
return {parent: parent.parentNode, before: parent};
|
845
|
+
},
|
846
|
+
/*
|
847
|
+
Insert node as a last node at parent's level
|
848
|
+
*/
|
849
|
+
'~': function(parent, child) {
|
850
|
+
return {parent: parent.parentNode};
|
851
|
+
},
|
852
|
+
/*
|
853
|
+
Inwert node as a first node at parent's level
|
854
|
+
*/
|
855
|
+
'!~': function(parent, child) {
|
856
|
+
return {parent: parent.parentNode, before: parent.parentNode.firstChild};
|
857
|
+
},
|
858
|
+
/*
|
859
|
+
Insert node as a first child
|
860
|
+
*/
|
861
|
+
'^': function(parent, child) {
|
862
|
+
return {before: parent.firstChild}
|
863
|
+
}
|
864
|
+
};
|
865
|
+
|
866
|
+
/*
|
867
|
+
++ and ~~ combinators act as their + and ~ counterparts, but they also look
|
868
|
+
back too. So `++` reads as `next child or previous child`. It makes sense
|
869
|
+
for matching phase.
|
870
|
+
|
871
|
+
Although, when a node is built, there's only one node that can not be inserted
|
872
|
+
previously to the node and next to the same node simultaneously. So they insert
|
873
|
+
ahead, just like `+` and `~`.
|
874
|
+
*/
|
875
|
+
LSD.Layout.Combinators['++'] = LSD.Layout.Combinators['+'];
|
876
|
+
LSD.Layout.Combinators['~~'] = LSD.Layout.Combinators['~'];
|
877
|
+
|
878
|
+
/*
|
879
|
+
When a lazy layout is set to render selector, it creates a promise object first.
|
880
|
+
The promise object holds all variables required to really render the piece,
|
881
|
+
but it postpones the rendering to future. The promise object is used as a proxy
|
882
|
+
definition on widget, giving layout a chance to render other chunks of layout that
|
883
|
+
are not lazy. When other chunks are rendered, they are matched against current
|
884
|
+
proxies on a widget. And when there's a match, proxy (which is really a promise object)
|
885
|
+
uses the matched node to and does not build a new one.
|
886
|
+
|
887
|
+
Then, a `layout.realize()` call sets all promised objects to render if they didnt
|
888
|
+
match any nodes built in other chunks of layout.
|
889
|
+
*/
|
890
|
+
|
891
|
+
LSD.Layout.Promise = function(layout, selector, lsd, parent, options, memo) {
|
892
|
+
selector = selector.replace(LSD.Layout.Promise.rOrderCombinator, function(whole, match) {
|
893
|
+
this.order = match;
|
894
|
+
return "";
|
895
|
+
}.bind(this))
|
896
|
+
var parsed = Slick.parse(selector), expressions = parsed.expressions[0];
|
897
|
+
// makes proxy deep - look into elements
|
898
|
+
this.deep = true;
|
899
|
+
this.layout = layout;
|
900
|
+
this.options = options;
|
901
|
+
this.promise = true;
|
902
|
+
this.lsd = lsd;
|
903
|
+
this[lsd ? 'selector' : 'mutation'] = selector;
|
904
|
+
this.widget = parent[0] || parent;
|
905
|
+
this.element = parent[1] || parent.element || parent.toElement();
|
906
|
+
this.parent = parent;
|
907
|
+
this.attach(memo);
|
908
|
+
};
|
909
|
+
LSD.Layout.Promise.rOrderCombinator = /\s*([+~])\s*$/;
|
910
|
+
|
911
|
+
|
912
|
+
LSD.Layout.Promise.prototype = {
|
913
|
+
attach: function(memo) {
|
914
|
+
this.memo = memo;
|
915
|
+
if ((this.combinator = memo.combinator)) delete memo.combiantor;
|
916
|
+
memo.promised = true;
|
917
|
+
var predecessors = memo.predecessors
|
918
|
+
if (predecessors) {
|
919
|
+
this.predecessors = predecessors.slice(0);
|
920
|
+
this.index = this.predecessors.length;
|
921
|
+
// Look for previous promises that had + combinator and pair them with this promise
|
922
|
+
for (var i = predecessors.length, predecessor; predecessor = predecessors[--i];) {
|
923
|
+
if (predecessor.order == '+' && !predecessor.next) {
|
924
|
+
this.previous = predecessor;
|
925
|
+
this.previous.next = this;
|
926
|
+
break;
|
927
|
+
}
|
928
|
+
}
|
929
|
+
} else this.index = 0;
|
930
|
+
if (this.order) {
|
931
|
+
if (!predecessors) predecessors = memo.predecessors = [];
|
932
|
+
predecessors.push(this);
|
933
|
+
}
|
934
|
+
// promise adds itself to the widget as proxy
|
935
|
+
// so its properties are treated as proxy options.
|
936
|
+
// `callback` method is called like if it was a proxy setting
|
937
|
+
this.widget.addProxy('promise', this);
|
938
|
+
},
|
939
|
+
|
940
|
+
detach: function() {
|
941
|
+
if (this.order) {
|
942
|
+
var predecessors = this.memo && this.memo.predecessors;
|
943
|
+
if (predecessors)
|
944
|
+
for (var i = predecessors.length, predecessor; predecessor = predecessors[--i];)
|
945
|
+
if (predecessor == this) predecessors.splice(i, 1);
|
946
|
+
}
|
947
|
+
this.widget.removeProxy('promise', this)
|
948
|
+
},
|
949
|
+
|
950
|
+
advance: function(memo) {
|
951
|
+
this.advanced = this.layout.render(this.children, this.result.lsd ? this.result : [this.widget, this.result], this.memo)
|
952
|
+
},
|
953
|
+
|
954
|
+
callback: function(child, proxy, memo) {
|
955
|
+
proxy.realize(memo, child)
|
956
|
+
},
|
957
|
+
|
958
|
+
realize: function(memo, result) {
|
959
|
+
this.detach();
|
960
|
+
var stored = this.options.stored;
|
961
|
+
if (stored) delete this.options.stored;
|
962
|
+
if (!result) {
|
963
|
+
if (this.previous && this.previous.result) this.before = this.previous.result.nextSibling;
|
964
|
+
var before = this.before;
|
965
|
+
if (before) {
|
966
|
+
var old = memo.before;
|
967
|
+
memo.before = before;
|
968
|
+
}
|
969
|
+
result = this.layout.selector(this.options, this.parent, memo);
|
970
|
+
if (this.before) memo.before = old;
|
971
|
+
}
|
972
|
+
this.result = result;
|
973
|
+
var predecessors = this.predecessors;
|
974
|
+
if (predecessors)
|
975
|
+
for (var i = predecessors.length, predecessor; predecessor = predecessors[--i];) {
|
976
|
+
var before = predecessor.before;
|
977
|
+
if ((predecessor.order == '+' && predecessor.next == this) || !predecessor.following || predecessor.following.index > this.index) {
|
978
|
+
predecessor.before = result;
|
979
|
+
predecessor.following = this;
|
980
|
+
}
|
981
|
+
}
|
982
|
+
if (this.next) this.next.after = result;
|
983
|
+
if (this.children) this.advance(memo);
|
984
|
+
if (stored && result.store) {
|
985
|
+
for (var name in stored) stored[name].call(this, result);
|
986
|
+
result.store('allocation', stored);
|
987
|
+
delete this.options.stored;
|
988
|
+
}
|
989
|
+
}
|
990
|
+
};
|
991
|
+
|
340
992
|
LSD.Layout.Branch = function(options) {
|
341
993
|
this.options = options;
|
342
|
-
|
343
|
-
|
344
|
-
|
994
|
+
this.id = ++LSD.Layout.Branch.UID;
|
995
|
+
this.$events = Object.clone(this.$events);
|
996
|
+
if (options.superbranch) {
|
997
|
+
options.superbranch.addEvents({
|
345
998
|
check: this.unmatch.bind(this),
|
346
999
|
uncheck: this.match.bind(this)
|
347
1000
|
});
|
1001
|
+
if (!options.superbranch.checked ^ this.options.invert) this.match();
|
1002
|
+
} else if (options.expression || options.show) {
|
1003
|
+
this.match();
|
1004
|
+
} else if (options.template) {
|
1005
|
+
LSD.Template[options.template] = this;
|
348
1006
|
}
|
349
|
-
this.addEvents({
|
350
|
-
check: this.show.bind(this),
|
351
|
-
uncheck: this.hide.bind(this)
|
352
|
-
});
|
353
1007
|
};
|
1008
|
+
LSD.Layout.Branch.UID = 0;
|
1009
|
+
LSD.Template = {};
|
354
1010
|
|
355
1011
|
LSD.Layout.Branch.prototype = Object.append({
|
1012
|
+
branch: true,
|
1013
|
+
getInterpolation: function() {
|
1014
|
+
if (this.interpolation) return this.interpolation;
|
1015
|
+
this.interpolation = LSD.Interpolation.compile(this.options.expression, this, this.options.widget, true);
|
1016
|
+
return this.interpolation;
|
1017
|
+
},
|
356
1018
|
match: function() {
|
357
|
-
if (
|
1019
|
+
if (this.options.expression) {
|
1020
|
+
var value = this.getInterpolation().attach().value;
|
1021
|
+
if ((value == null) ^ this.options.invert) return;
|
1022
|
+
}
|
1023
|
+
this.check();
|
1024
|
+
},
|
1025
|
+
unmatch: function(lazy) {
|
1026
|
+
if (this.options.expression) {
|
1027
|
+
var value = this.interpolation.value;
|
1028
|
+
this.interpolation.detach()
|
1029
|
+
if ((value == null) ^ this.options.invert) return;
|
1030
|
+
}
|
1031
|
+
this.uncheck(lazy);
|
358
1032
|
},
|
359
|
-
|
360
|
-
if (this.checked)
|
1033
|
+
check: function(lazy) {
|
1034
|
+
if (!this.checked) {
|
1035
|
+
this.checked = true;
|
1036
|
+
this.show();
|
1037
|
+
if (!lazy) this.fireEvent('check', arguments);
|
1038
|
+
}
|
1039
|
+
},
|
1040
|
+
uncheck: function(lazy) {
|
1041
|
+
if (this.checked || this.checked == null) {
|
1042
|
+
this.checked = false;
|
1043
|
+
this.hide();
|
1044
|
+
if (!lazy) this.fireEvent('uncheck', arguments);
|
1045
|
+
}
|
361
1046
|
},
|
362
|
-
|
363
|
-
|
1047
|
+
set: function(value) {
|
1048
|
+
this[((value != false && value != null) ^ this.options.invert) ? 'check' : 'uncheck']();
|
364
1049
|
},
|
365
1050
|
show: function() {
|
366
|
-
|
1051
|
+
var layout = this.layout;
|
1052
|
+
if (!layout) return;
|
1053
|
+
if (layout.length) for (var i = 0, child, keyword, depth = 0; child = layout[i]; i++) {
|
1054
|
+
if (child.call) {
|
1055
|
+
if (layout === this.layout) layout = layout.slice(0);
|
1056
|
+
var result = child.call(this);
|
1057
|
+
if (result) {
|
1058
|
+
for (var branch = this; branch; branch = branch.options.parent) branch.dirty = true;
|
1059
|
+
if (result.length) layout.splice.apply(layout, [i, 1].concat(result))
|
1060
|
+
else layout[i] = result;
|
1061
|
+
}
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
var before = this.options.element && this.options.element.nextSibling;
|
1065
|
+
var rendered = this.options.widget.addLayout(this.id, layout, this.options.widget, this.options.options, {before: before});
|
1066
|
+
if (result) this.validate(rendered)
|
1067
|
+
if (rendered) this.layout = rendered;
|
367
1068
|
},
|
368
1069
|
hide: function() {
|
369
|
-
|
1070
|
+
var layout = this.layout;
|
1071
|
+
if (!layout) return;
|
1072
|
+
this.options.widget.removeLayout(this.id, layout, this.options.widget, this.options.options);
|
1073
|
+
},
|
1074
|
+
splice: function(branch, layout, baseline) {
|
1075
|
+
var offset = 0;
|
1076
|
+
if (branch.layout) {
|
1077
|
+
if (!layout) layout = this.layout;
|
1078
|
+
for (var i = 0, child, keyword; child = branch.layout[i]; i++) {
|
1079
|
+
var index = layout.indexOf(child);
|
1080
|
+
if (index > -1) {
|
1081
|
+
var keyword = Element.retrieve(child, 'keyword');
|
1082
|
+
if (keyword && keyword !== true) {
|
1083
|
+
offset += this.splice(keyword, layout);
|
1084
|
+
}
|
1085
|
+
layout.splice(index, 1);
|
1086
|
+
i--;
|
1087
|
+
}
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
return offset;
|
1091
|
+
},
|
1092
|
+
validate: function(layout) {
|
1093
|
+
var validate = true;
|
1094
|
+
for (var index, child, i = 0; child = layout[i]; i++) {
|
1095
|
+
switch ((child = layout[i]).nodeType) {
|
1096
|
+
case 8:
|
1097
|
+
if (validate)
|
1098
|
+
if (index != null) validate = index = null;
|
1099
|
+
else index = i;
|
1100
|
+
var keyword = Element.retrieve(child, 'keyword');
|
1101
|
+
if (keyword && keyword !== true) i += this.splice(keyword, layout, i)
|
1102
|
+
break;
|
1103
|
+
case 3:
|
1104
|
+
if (validate && child.textContent.match(LSD.Layout.rWhitespace)) break;
|
1105
|
+
default:
|
1106
|
+
index = validate = null;
|
1107
|
+
}
|
1108
|
+
}
|
1109
|
+
if (index != null) {
|
1110
|
+
var comment = layout[index];
|
1111
|
+
layout[index] = function() {
|
1112
|
+
return LSD.slice(document.createFragment(this.expand(comment.nodeValue)).childNodes);
|
1113
|
+
};
|
1114
|
+
comment.parentNode.removeChild(comment);
|
1115
|
+
if (this.options.clean) layout = layout[index];
|
1116
|
+
}
|
1117
|
+
return layout;
|
1118
|
+
},
|
1119
|
+
setLayout: function(layout, soft) {
|
1120
|
+
this.layout = layout.push ? this.validate(layout) : layout;
|
1121
|
+
if (this.checked) {
|
1122
|
+
this.show();
|
1123
|
+
} else if (!soft) this.hide();
|
1124
|
+
},
|
1125
|
+
getLayout: function(layout) {
|
1126
|
+
return this.layout;
|
1127
|
+
},
|
1128
|
+
attach: function() {
|
1129
|
+
if ((this.options.expression && !this.options.link) || !this.options.superbranch.checked) this.match(true);
|
1130
|
+
//this.show();
|
1131
|
+
},
|
1132
|
+
detach: function() {
|
1133
|
+
if (this.options.expression && !this.options.link) this.unmatch(true);
|
1134
|
+
this.hide()
|
1135
|
+
},
|
1136
|
+
expand: function(text) {
|
1137
|
+
var depth = 0;
|
1138
|
+
text = text.replace(LSD.Layout.rComment, function(whole, start, end) {
|
1139
|
+
depth += (start ? 1 : -1);
|
1140
|
+
if (depth == !!start) return start ? '<!--' : '-->'
|
1141
|
+
return whole;
|
1142
|
+
});
|
1143
|
+
if (depth) throw "The lazy branch is unbalanced"
|
1144
|
+
return text;
|
370
1145
|
}
|
371
1146
|
}, Events.prototype);
|
372
1147
|
|
1148
|
+
LSD.Layout.rComment = /(\<\!-)|(-\>)/g
|
1149
|
+
LSD.Layout.rWhitespace = /^\s*$/;
|
1150
|
+
|
373
1151
|
LSD.Layout.NodeTypes = {1: 'element', 3: 'textnode', 8: 'comment', 11: 'fragment'};
|
374
|
-
LSD.Layout.NodeNames = Array.
|
1152
|
+
LSD.Layout.NodeNames = Array.object('!doctype', 'a', 'abbr', 'acronym', 'address', 'applet', 'area',
|
375
1153
|
'article', 'aside', 'audio', 'b', 'base', 'bdo', 'big', 'blockquote', 'body', 'br', 'button', 'canvas',
|
376
1154
|
'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command', 'datalist', 'dd', 'del', 'details',
|
377
1155
|
'dfn', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame',
|
378
|
-
'frameset', 'h1', '
|
379
|
-
'keygen', 'kbd', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu', 'meta',
|
380
|
-
'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress',
|
381
|
-
'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike',
|
1156
|
+
'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'iframe',
|
1157
|
+
'img', 'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'link', 'map', 'mark', 'menu', 'meta',
|
1158
|
+
'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'pre', 'progress',
|
1159
|
+
'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike',
|
382
1160
|
'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead',
|
383
1161
|
'time', 'title', 'tr', 'ul', 'var', 'video', 'wbr');
|
384
|
-
LSD.Layout.Inheritable = Array.fast('context', 'interpolation', 'clone', 'lazy');
|
385
1162
|
|
386
|
-
LSD.Layout.
|
1163
|
+
LSD.Layout.Keyword = {
|
1164
|
+
'if': function(expression) {
|
1165
|
+
return {branch: true, expression: expression};
|
1166
|
+
},
|
1167
|
+
'unless': function(expression) {
|
1168
|
+
return {branch: true, expression: expression, invert: true};
|
1169
|
+
},
|
1170
|
+
'elsif': function(expression) {
|
1171
|
+
return {branch: true, expression: expression, link: true};
|
1172
|
+
},
|
1173
|
+
'else': function() {
|
1174
|
+
return {branch: true, link: true};
|
1175
|
+
},
|
1176
|
+
'build': function(expression) {
|
1177
|
+
var options = {layout: {}};
|
1178
|
+
options.layout[parsed.expression] = true;
|
1179
|
+
return options;
|
1180
|
+
},
|
1181
|
+
'template': function(expression) {
|
1182
|
+
return {branch: true, template: expression, clean: true}
|
1183
|
+
},
|
1184
|
+
'end': function(expression) {
|
1185
|
+
return {ends: true}
|
1186
|
+
}
|
1187
|
+
};
|
1188
|
+
|
1189
|
+
LSD.Layout.rExpression = /^\s*([a-z]+)(?:\s(.*?))?\s*$/;
|
387
1190
|
Object.append(LSD.Layout, {
|
388
1191
|
extractKeyword: function(input) {
|
389
|
-
var match = input.match(LSD.Layout.
|
390
|
-
if (match
|
1192
|
+
var match = input.match(LSD.Layout.rExpression);
|
1193
|
+
if (match && LSD.Layout.Keyword[match[1]])
|
1194
|
+
return {keyword: match[1], expression: match[2]};
|
391
1195
|
},
|
392
1196
|
|
393
1197
|
/*
|
@@ -397,31 +1201,45 @@ Object.append(LSD.Layout, {
|
|
397
1201
|
var options = {};
|
398
1202
|
var expressions = (selector.Slick ? selector : Slick.parse(selector)).expressions[0];
|
399
1203
|
var parsed = expressions[0];
|
1204
|
+
if (expressions.length > 1) var order = expressions[expressions.length - 1].combinator;
|
400
1205
|
if (parsed.combinator != ' ') {
|
401
1206
|
if (parsed.combinator == '::') {
|
402
1207
|
if (LSD.Allocations[parsed.tag]) {
|
403
|
-
options.allocation = LSD.Module.Allocations.
|
1208
|
+
var allocation = options.allocation = LSD.Module.Allocations.compile(parsed.tag, parsed.classes, parsed.attributes, parsed.pseudos);
|
1209
|
+
if (allocation.options && allocation.options.source) {
|
1210
|
+
var source = allocation.options.source;
|
1211
|
+
delete allocation.options.source
|
1212
|
+
}
|
404
1213
|
} else {
|
405
1214
|
var relation = (parent[0] || parent).relations[parsed.tag];
|
406
1215
|
if (!relation) throw "Unknown pseudo element ::" + parsed.tag;
|
407
1216
|
var source = relation.getSource();
|
408
|
-
|
409
|
-
|
1217
|
+
}
|
1218
|
+
if (source && (!source.match || !source.match(rSIMPLE_SOURCE)))
|
1219
|
+
Object.merge(options, LSD.Layout.parse(source, parent));
|
410
1220
|
} else options.combinator = parsed.combinator;
|
411
|
-
}
|
1221
|
+
}
|
1222
|
+
if (order && order != ' ') options.order = order;
|
1223
|
+
if (parsed.id) (options.attributes || (options.attributes = {})).id = parsed.id
|
1224
|
+
if (parsed.attributes)
|
1225
|
+
for (var all = parsed.attributes, attribute, i = 0; attribute = all[i++];) {
|
1226
|
+
var value = attribute.value || LSD.Attributes[attribute.key] == 'number' || "";
|
1227
|
+
(options.attributes || (options.attributes = {}))[attribute.key] = value;
|
1228
|
+
}
|
412
1229
|
if (parsed.tag != '*' && parsed.combinator != '::')
|
413
1230
|
if (parsed.tag.indexOf('-') > -1)
|
414
1231
|
options.source = parsed.tag.split('-');
|
415
|
-
else
|
1232
|
+
else {
|
416
1233
|
options.tag = parsed.tag;
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
if (parsed.pseudos)
|
424
|
-
(
|
1234
|
+
var source = LSD.Layout.getSource(options, options.tag);
|
1235
|
+
if (source.push) options.source = source;
|
1236
|
+
}
|
1237
|
+
if (parsed.classes)
|
1238
|
+
for (var all = parsed.classes, pseudo, i = 0; klass = all[i++];)
|
1239
|
+
(options.classes || (options.classes = {}))[klass.value] = true;
|
1240
|
+
if (parsed.pseudos)
|
1241
|
+
for (var all = parsed.pseudos, pseudo, i = 0; pseudo = all[i++];)
|
1242
|
+
(options.pseudos || (options.pseudos = {}))[pseudo.key] = true;
|
425
1243
|
return options;
|
426
1244
|
},
|
427
1245
|
|