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.
Files changed (110) hide show
  1. data/Packages/Sheet.js/Source/SheetParser.Value.js +1 -1
  2. data/Packages/lsd/Source/Action/Clone.js +2 -12
  3. data/Packages/lsd/Source/Action/Delete.js +1 -7
  4. data/Packages/lsd/Source/Action/Display.js +7 -7
  5. data/Packages/lsd/Source/Action/Edit.js +2 -2
  6. data/Packages/lsd/Source/Action/Invoke.js +2 -2
  7. data/Packages/lsd/Source/Action/Submit.js +2 -0
  8. data/Packages/lsd/Source/Action/Toggle.js +4 -0
  9. data/Packages/lsd/Source/Action/Update.js +2 -1
  10. data/Packages/lsd/Source/Action.js +4 -5
  11. data/Packages/lsd/Source/Document.js +10 -7
  12. data/Packages/lsd/Source/LSD.js +22 -127
  13. data/Packages/lsd/Source/Layer.js +4 -4
  14. data/Packages/lsd/Source/Layout.js +1077 -259
  15. data/Packages/lsd/Source/Mixin/Animation.js +1 -1
  16. data/Packages/lsd/Source/Mixin/Choice.js +2 -2
  17. data/Packages/lsd/Source/Mixin/Command.js +26 -8
  18. data/Packages/lsd/Source/Mixin/ContentEditable.js +2 -1
  19. data/Packages/lsd/Source/{Trait → Mixin}/Date.js +21 -11
  20. data/Packages/lsd/Source/{Trait/Menu.js → Mixin/Details.js} +6 -1
  21. data/Packages/lsd/Source/Mixin/Draggable.js +1 -1
  22. data/Packages/lsd/Source/Mixin/Fieldset.js +51 -49
  23. data/Packages/lsd/Source/Mixin/Focusable.js +38 -39
  24. data/Packages/lsd/Source/Mixin/Invokable.js +13 -14
  25. data/Packages/lsd/Source/Mixin/List.js +9 -17
  26. data/Packages/lsd/Source/Mixin/Placeholder.js +2 -7
  27. data/Packages/lsd/Source/Mixin/Request.js +5 -3
  28. data/Packages/lsd/Source/Mixin/Resizable.js +3 -3
  29. data/Packages/lsd/Source/Mixin/Resource.js +1 -1
  30. data/Packages/lsd/Source/Mixin/Root.js +17 -1
  31. data/Packages/lsd/Source/Mixin/Scrollable.js +1 -1
  32. data/Packages/lsd/Source/{Trait → Mixin}/Slider.js +0 -0
  33. data/Packages/lsd/Source/Mixin/Sortable.js +2 -2
  34. data/Packages/lsd/Source/Mixin/Submittable.js +2 -1
  35. data/Packages/lsd/Source/Mixin/Target.js +11 -6
  36. data/Packages/lsd/Source/Mixin/Touchable.js +2 -2
  37. data/Packages/lsd/Source/Mixin/Unselectable.js +1 -1
  38. data/Packages/lsd/Source/Mixin/Uploader.js +11 -13
  39. data/Packages/lsd/Source/Mixin/Validity.js +35 -9
  40. data/Packages/lsd/Source/Mixin/Value.js +5 -3
  41. data/Packages/lsd/Source/Module/Accessories/Attributes.js +90 -89
  42. data/Packages/lsd/Source/Module/Accessories/Chain.js +40 -25
  43. data/Packages/lsd/Source/Module/Accessories/Element.js +59 -58
  44. data/Packages/lsd/Source/Module/Accessories/Events.js +25 -10
  45. data/Packages/lsd/Source/Module/Accessories/Options.js +11 -13
  46. data/Packages/lsd/Source/Module/Accessories/States.js +42 -5
  47. data/Packages/lsd/Source/Module/Accessories/Styles.js +1 -1
  48. data/Packages/lsd/Source/Module/Accessories/Tag.js +21 -7
  49. data/Packages/lsd/Source/Module/Ambient/Allocations.js +178 -64
  50. data/Packages/lsd/Source/Module/Ambient/DOM.js +98 -55
  51. data/Packages/lsd/Source/Module/Ambient/Expectations.js +57 -8
  52. data/Packages/lsd/Source/Module/Ambient/Interpolations.js +147 -0
  53. data/Packages/lsd/Source/Module/Ambient/Layout.js +52 -15
  54. data/Packages/lsd/Source/Module/Ambient/Proxies.js +44 -36
  55. data/Packages/lsd/Source/Module/Ambient/Relations.js +3 -1
  56. data/Packages/lsd/Source/Module/Ambient/Selectors.js +3 -3
  57. data/Packages/lsd/Source/Module/Ambient.js +2 -2
  58. data/Packages/lsd/Source/Module/Graphics/Layers.js +1 -1
  59. data/Packages/lsd/Source/Module/Graphics/Render.js +1 -1
  60. data/Packages/lsd/Source/Relation.js +19 -20
  61. data/Packages/lsd/Source/Sheet.js +4 -5
  62. data/Packages/lsd/Source/{Behavior.js → Tools/Behavior.js} +0 -0
  63. data/Packages/lsd/Source/Tools/Command.js +190 -0
  64. data/Packages/lsd/Source/Tools/Helpers.js +109 -0
  65. data/Packages/lsd/Source/Tools/Interpolation.js +351 -0
  66. data/Packages/lsd/Source/Tools/Microdata.js +75 -0
  67. data/Packages/lsd/Source/Tools/Object.js +192 -0
  68. data/Packages/lsd/Source/Tools/Position.js +208 -0
  69. data/Packages/lsd/Source/Tools/Require.js +76 -0
  70. data/Packages/lsd/Source/Type.js +2 -2
  71. data/Packages/lsd/package.yml +11 -7
  72. data/Packages/lsd-mobile/Source/Body/Dialog.js +2 -2
  73. data/Packages/lsd-mobile/Source/Input/Date.js +9 -84
  74. data/Packages/lsd-native/Source/Input/Checkbox.js +4 -4
  75. data/Packages/lsd-native/Source/Input/Date.js +46 -6
  76. data/Packages/lsd-native/Source/Input/Radio.js +1 -4
  77. data/Packages/lsd-native/Source/Input.js +5 -6
  78. data/Packages/lsd-widgets/Source/Body/Dialog.js +9 -6
  79. data/Packages/lsd-widgets/Source/Body/Page.js +1 -1
  80. data/Packages/lsd-widgets/Source/Body.js +1 -1
  81. data/Packages/lsd-widgets/Source/Button.js +1 -1
  82. data/Packages/lsd-widgets/Source/Form.js +1 -1
  83. data/Packages/lsd-widgets/Source/Input/Checkbox.js +2 -2
  84. data/Packages/lsd-widgets/Source/Input/Date.js +45 -16
  85. data/Packages/lsd-widgets/Source/Input/File.js +2 -2
  86. data/Packages/lsd-widgets/Source/Input/HTML.js +2 -2
  87. data/Packages/lsd-widgets/Source/Input/Radio.js +1 -1
  88. data/Packages/lsd-widgets/Source/Input/Range.js +2 -2
  89. data/Packages/lsd-widgets/Source/Input/Submit.js +1 -1
  90. data/Packages/lsd-widgets/Source/Input.js +2 -11
  91. data/Packages/lsd-widgets/Source/Label.js +4 -8
  92. data/Packages/lsd-widgets/Source/Menu/List.js +3 -3
  93. data/Packages/lsd-widgets/Source/Menu.js +2 -2
  94. data/Packages/lsd-widgets/Source/Progress.js +1 -1
  95. data/Packages/lsd-widgets/Source/Select.js +7 -6
  96. data/Packages/lsd-widgets/Source/Table/Calendar.js +41 -15
  97. data/Packages/lsd-widgets/Source/Table.js +7 -2
  98. data/Packages/mootools-ext/Source/Core/Class.Mixin.js +43 -38
  99. data/Packages/mootools-ext/Source/Core/Class.States.js +26 -22
  100. data/Packages/mootools-ext/Source/Element/Element.from.js +1 -1
  101. data/Packages/mootools-ext/Source/Element/Properties/Item.js +1 -2
  102. data/Packages/mootools-ext/Source/Request/Request.Statuses.js +9 -7
  103. data/Packages/mootools-ext/Source/Types/{FastArray.js → Object.Array.js} +8 -14
  104. data/Packages/mootools-ext/package.yml +1 -2
  105. data/lib/lsd.rake +28 -0
  106. metadata +15 -11
  107. data/Packages/lsd/Source/Command.js +0 -135
  108. data/Packages/lsd/Source/Interpolation.js +0 -103
  109. data/Packages/lsd/Source/Module/Ambient/Container.js +0 -56
  110. data/Packages/mootools-ext/Source/Element/Element.onDispose.js +0 -36
@@ -0,0 +1,190 @@
1
+ /*
2
+ ---
3
+
4
+ script: Checkbox.js
5
+
6
+ description: A triggerable interaction abstraction
7
+
8
+ license: Public domain (http://unlicense.org).
9
+
10
+ authors: Yaroslaff Fedin
11
+
12
+ requires:
13
+ - LSD
14
+ - Ext/States
15
+
16
+ provides:
17
+ - LSD.Command
18
+
19
+ ...
20
+ */
21
+
22
+ LSD.Command = function(document, options) {
23
+ this.setOptions(options);
24
+ this.widgets = [];
25
+ this.$events = Object.clone(this.$events);
26
+ if (document) {
27
+ this.document = document;
28
+ if (!this.document.commands) this.document.commands = {};
29
+ this.document.commands[this.options.id] = this;
30
+ }
31
+ if (this.options.type) this.setType(this.options.type);
32
+ };
33
+
34
+ LSD.Command.prototype = Object.append(new Options, new Events, new States, {
35
+ options: {
36
+ id: null,
37
+ action: null
38
+ },
39
+
40
+ click: function() {
41
+ this.fireEvent('click', arguments);
42
+ },
43
+
44
+ attach: function(widget) {
45
+ widget.fireEvent('register', ['command', this]);
46
+ if (this.disabled) widget.states.add('disabled');
47
+ if (!this.bound) this.bound = {}
48
+ if (!this.bound.enable) this.bound.enable = this.enable.bind(this);
49
+ if (!this.bound.disable) this.bound.disable = this.disable.bind(this);
50
+ if (this.type && this.type != 'command') {
51
+ widget.states.set('checked');
52
+ if (this.checked) widget.states.add('checked');
53
+ if (!this.bound.check) this.bound.check = this.check.bind(this);
54
+ if (!this.bound.uncheck) this.bound.uncheck = this.uncheck.bind(this);
55
+ widget.addEvent('check', this.bound.check);
56
+ widget.addEvent('uncheck', this.bound.uncheck);
57
+ if (widget.checked) this.check();
58
+ }
59
+ widget.addEvent('disable', this.bound.disable);
60
+ widget.addEvent('enable', this.bound.enable);
61
+ if (widget.disabled) this.disable();
62
+ this.widgets.push(widget);
63
+ return this;
64
+ },
65
+
66
+ detach: function(widget) {
67
+ widget.fireEvent('unregister', ['command', this]);
68
+ if (this.disabled) widget.states.remove('disabled');
69
+ if (this.type && this.type != 'command') {
70
+ widget.states.unset('checked');
71
+ if (this.checked) widget.states.remove('checked');
72
+ widget.removeEvent('check', this.bound.check);
73
+ widget.removeEvent('uncheck', this.bound.uncheck);
74
+ }
75
+ widget.removeEvent('disable', this.bound.disable);
76
+ widget.removeEvent('enable', this.bound.enable);
77
+ this.widgets.erase(widget);
78
+ return this;
79
+ },
80
+
81
+ check: function() {
82
+ for (var i = 0, widget; widget = this.widgets[i++];) widget.states.add('checked');
83
+ },
84
+
85
+ uncheck: function() {
86
+ for (var i = 0, widget; widget = this.widgets[i++];) widget.states.remove('checked');
87
+ },
88
+
89
+ disable: function() {
90
+ for (var i = 0, widget; widget = this.widgets[i++];) widget.disable.add('disabled');
91
+ },
92
+
93
+ enable: function() {
94
+ for (var i = 0, widget; widget = this.widgets[i++];) widget.enable.remove('disabled');
95
+ },
96
+
97
+ setType: function(type, unset) {
98
+ if (this.type == type) return;
99
+ if (this.type) this.unsetType(type);
100
+ this.type = type;
101
+ switch (type) {
102
+ case "checkbox":
103
+ /*
104
+ Checkbox commands are useful when you need to track and toggle
105
+ state of some linked object.
106
+
107
+ Provide your custom logic hooking on *check* and *uncheck*
108
+ state transitions. Use *checked* property to get the current state.
109
+
110
+ Examples:
111
+ - Button that toggles visibility of a sidebar
112
+ - Context menu item that shows or hides line numbers in editor
113
+ */
114
+ this.events = {
115
+ click: function() {
116
+ this.toggle();
117
+ }
118
+ };
119
+ break;
120
+
121
+
122
+ /*
123
+ Radio groupping is a way to links commands together to allow
124
+ only one in the group be active at the moment of time.
125
+
126
+ Activation (*check*ing) of the commands deactivates all
127
+ other commands in a radiogroup.
128
+
129
+ Examples:
130
+ - Tabs on top of a content window
131
+ - Select box with a dropdown menu
132
+ */
133
+ case "radio":
134
+ var name = this.options.radiogroup;
135
+ if (name) {
136
+ var groups = this.document.radiogroups;
137
+ if (!groups) groups = this.document.radiogroups = {};
138
+ var group = groups[name];
139
+ if (!group) group = groups[name] = [];
140
+ group.push(this);
141
+ this.group = group;
142
+ }
143
+ this.events = {
144
+ click: function() {
145
+ this.check.apply(this, arguments);
146
+ },
147
+ check: function() {
148
+ if (group) group.each(function(sibling) {
149
+ if (sibling != this) {
150
+ sibling.uncheck();
151
+ if (sibling.widgets) sibling.widgets.each(function(widget) {
152
+ widget.unclick();
153
+ })
154
+ }
155
+ }, this);
156
+ }
157
+ };
158
+ }
159
+ if (this.events) {
160
+ this.addEvents(this.events);
161
+ if (!this.bound) this.bound = {};
162
+ if (!this.bound.check) this.bound.check = this.check.bind(this);
163
+ if (!this.bound.uncheck) this.bound.uncheck = this.uncheck.bind(this);
164
+ for (var i = 0, widget; widget = this.widgets[i++];) {
165
+ widget.states.set('checked');
166
+ if (this.checked) widget.states.add('checked');
167
+ widget.addEvent('check', this.bound.check);
168
+ widget.addEvent('uncheck', this.bound.uncheck);
169
+ }
170
+ }
171
+ },
172
+
173
+ unsetType: function() {
174
+ if (this.events) {
175
+ if (this.type != 'command') {
176
+ for (var i = 0, widget; widget = this.widgets[i++];) {
177
+ widget.states.unset('checked');
178
+ if (this.checked) widget.states.remove('checked');
179
+ widget.removeEvent('check', this.bound.check);
180
+ widget.removeEvent('uncheck', this.bound.uncheck);
181
+ }
182
+ }
183
+ this.removeEvents(this.events);
184
+ delete this.events;
185
+ }
186
+ delete this.type;
187
+ }
188
+ });
189
+
190
+ LSD.Command.prototype.addStates('disabled', 'checked');
@@ -0,0 +1,109 @@
1
+ /*
2
+ ---
3
+
4
+ script: Helpers.js
5
+
6
+ description: Some useful functions that are used internally
7
+
8
+ license: Public domain (http://unlicense.org).
9
+
10
+ authors: Yaroslaff Fedin
11
+
12
+ requires:
13
+ - LSD
14
+
15
+ provides:
16
+ - LSD.Helpers
17
+
18
+ ...
19
+ */
20
+
21
+ Object.append(LSD, {
22
+ toLowerCase: function(lowercased) {
23
+ return function(string) {
24
+ return (lowercased[string]) || (lowercased[string] = string.toLowerCase())
25
+ }
26
+ }(LSD.lowercased = {}),
27
+
28
+ capitalize: function(capitalized) {
29
+ return function(string) {
30
+ return (capitalized[string]) || (capitalized[string] = string.capitalize())
31
+ }
32
+ }(LSD.capitalized = {}),
33
+
34
+ toClassName: function(classnamed) {
35
+ return function(string) {
36
+ return (classnamed[string]) || (classnamed[string] = string.replace(/(^|-|_)([a-z])/g, function(a, b, c) { return (b == '-' ? '.' : '') + c.toUpperCase()}))
37
+ }
38
+ }(LSD.classnamed = {}),
39
+
40
+ uid: function(object) {
41
+ if (object.lsd) return object.lsd;
42
+ if (object.localName) return $uid(object);
43
+ return (object.lsd = ++LSD.UID);
44
+ },
45
+
46
+ position: function(box, size, x, y) {
47
+ var position = {x: 0, y: 0};
48
+
49
+ switch (x) {
50
+ case "left":
51
+ position.x = 0;
52
+ case "right":
53
+ position.x = box.width - size.width;
54
+ case "center":
55
+ position.x = (box.width - size.width) / 2;
56
+ }
57
+ switch (y) {
58
+ case "top":
59
+ position.y = 0;
60
+ case "bottom":
61
+ position.y = box.height - size.height;
62
+ case "center":
63
+ position.y = (box.height- size.height) / 2;
64
+ }
65
+ return position;
66
+ },
67
+
68
+ UID: 0,
69
+
70
+ slice: (Browser.ie ? function(list, start) {
71
+ for (var i = start || 0, j = list.length, ary = []; i < j; i++) ary.push(list[i]);
72
+ return ary;
73
+ } : function(list, start) {
74
+ return Array.prototype.slice.call(list, start || 0);
75
+ }),
76
+
77
+ reverseMerge: function(object, another) {
78
+ for (var name in another) {
79
+ var old = object[name], value = another[name];
80
+ if (old !== value) {
81
+ if (typeof old == 'undefined') {
82
+ if (!value.nodeType && !value.$family) value = Object.clone(value)
83
+ object[name] = value;
84
+ } else if (!old.nodeType && !old.$family) {
85
+ LSD.reverseMerge(old, value);
86
+ }
87
+ }
88
+ }
89
+ return object;
90
+ }
91
+ });
92
+
93
+ if (!window.console) window.console = {};
94
+ if (!window.console.log) window.console.log = function() {};
95
+ ['log', 'error', 'warn', 'info', 'dir'].each(function(method) {
96
+ try {
97
+ LSD[method] = function() {
98
+ try {
99
+ (console[method] || console.log).apply(console.arguments);
100
+ } catch(e){}
101
+ }
102
+ } catch(e) {};
103
+ });
104
+
105
+ (function(toString) {
106
+ Type.isEnumerable = function(item){
107
+ return (item != null && !item.localName && !item.nodeType && toString.call(item) != '[object Function]' && typeof item.length == 'number');
108
+ };
109
+ })(Object.prototype.toString);
@@ -0,0 +1,351 @@
1
+ /*
2
+ ---
3
+
4
+ script: Interpolation.js
5
+
6
+ description: Variable piece of html that can be asynchronously replaced with content
7
+
8
+ license: Public domain (http://unlicense.org).
9
+
10
+ authors: Yaroslaff Fedin
11
+
12
+ requires:
13
+ - LSD
14
+ - Sheet/SheetParser.Value
15
+
16
+ provides:
17
+ - LSD.Interpolation
18
+
19
+ ...
20
+ */
21
+
22
+ !function() {
23
+ LSD.Interpolation = function(input, output, source) {
24
+ this.input = input;
25
+ this.output = output;
26
+ this.source = source;
27
+ };
28
+
29
+ LSD.Interpolation.Function = function(input, output, source, name) {
30
+ this.input = input;
31
+ this.output = output;
32
+ this.source = source;
33
+ this.name = name;
34
+ this.args = Array.prototype.slice.call(input, 0);
35
+ };
36
+
37
+ LSD.Interpolation.Selector = function(input, output, source) {
38
+ this.input = input;
39
+ this.output = output;
40
+ this.source = source;
41
+ this.input = input.replace(R_SELECTOR_CONTEXT, function(whole, match) {
42
+ switch (match) {
43
+ case "$":
44
+ this.element = this.source.toElement();
45
+ return '';
46
+ case "$$":
47
+ this.element = this.source.toElement().ownerDocument.body;
48
+ return '';
49
+ }
50
+ }.bind(this));
51
+ this.collection = [];
52
+ if (!source || !source.lsd) throw "Selector should be applied on widgets";
53
+ };
54
+
55
+ LSD.Interpolation.prototype = {
56
+ interpolation: true,
57
+
58
+ set: function(value) {
59
+ this.value = this.process ? this.process(value) : value;
60
+ this.onSet(this.value);
61
+ },
62
+
63
+ onSet: function(value) {
64
+ if (value == null && this.placeholder) value = this.placeholder;
65
+ if (this.output) this.update(value);
66
+ if (this.parent) this.parent.set();
67
+ },
68
+
69
+ attach: function() {
70
+ return this.fetch(true);
71
+ },
72
+
73
+ detach: function() {
74
+ return this.fetch(false);
75
+ },
76
+
77
+ fetch: function(state) {
78
+ if (!this.setter) this.setter = this.set.bind(this);
79
+ (this.source.call ? this.source : this.request).call(this, this.input, this.setter, this.source, state);
80
+ return this;
81
+ },
82
+
83
+ request: function(input, callback, source, state) {
84
+ return this.source[state ? 'addInterpolation' : 'removeInterpolation'](input, callback);
85
+ },
86
+
87
+ update: function(value) {
88
+ var output = this.output;
89
+ if (!output) return;
90
+ if (output.branch) {
91
+ output.set(value);
92
+ } else if (output.call) {
93
+ output(value !== null);
94
+ } else {
95
+ if (value == null) value = '';
96
+ switch (output.nodeType) {
97
+ case 1:
98
+ if (output.lsd) output.write(value)
99
+ else output.innerHTML = value;
100
+ break;
101
+ case 3:
102
+ output.nodeValue = value;
103
+ break;
104
+ case 8:
105
+ }
106
+ }
107
+ }
108
+ };
109
+
110
+ LSD.Interpolation.Function.prototype = Object.append({}, LSD.Interpolation.prototype, {
111
+ fetch: function(state) {
112
+ for (var i = 0, j = this.args.length, arg; i < j; i++) {
113
+ if ((arg = this.args[i]) == null) continue;
114
+ if (!arg.interpolation) {
115
+ arg = LSD.Interpolation.compile(this.args[i], null, this.source);
116
+ if (!arg.parent) {
117
+ arg.parent = this;
118
+ if (arg.value == null) var stop = true;
119
+ }
120
+ }
121
+ this.args[i] = arg;
122
+ if (arg.interpolation) arg.fetch(state);
123
+ }
124
+ if (!stop) this.set();
125
+ return this;
126
+ },
127
+
128
+ execute: function() {
129
+ for (var i = 0, args = [], j = this.args.length, arg; i < j; i++)
130
+ if ((arg = this.args[i]) && arg.interpolation && arg.value == null) return null;
131
+ else args[i] = (arg && typeof arg.value != 'undefined') ? arg.value : arg;
132
+ if (this.name) {
133
+ return functions[this.name].apply(functions, args)
134
+ } else {
135
+ return args[0];
136
+ }
137
+ },
138
+
139
+ process: function() {
140
+ return this.execute();
141
+ }
142
+ });
143
+
144
+ LSD.Interpolation.Selector.prototype = Object.append({}, LSD.Interpolation.prototype, {
145
+ request: function(input, callback, state) {
146
+ return (this.element || this.source)[state ? 'watch' : 'unwatch'](input, callback);
147
+ },
148
+
149
+ set: function(node, state) {
150
+ if (this.filter && !this.filter(node)) return;
151
+ if (state) {
152
+ this.collection.push(node);
153
+ } else {
154
+ var index = this.collection.indexOf(node);
155
+ if (index > -1) this.collection.splice(index, 1);
156
+ }
157
+ this.value = this.collection.length ? this.collection : 0;
158
+ this.onSet(this.value);
159
+ }
160
+ });
161
+
162
+ // Set up helpers
163
+ var functions = LSD.Interpolation.Functions = {
164
+ count: function(elements) {
165
+ return elements.push ? elements.length : +!!elements
166
+ },
167
+
168
+ pluralize: function(count, singular, plural) {
169
+ var value = (count == 1) ? singular : (plural || (singular.pluralize()));
170
+ var index = value.indexOf('%');
171
+ if (index > -1) {
172
+ return value.substr(0, index) + count + value.substr(index + 1, value.length - index - 1);
173
+ } else {
174
+ return count + ' ' + value;
175
+ }
176
+ }
177
+ };
178
+
179
+ // Import all string prototype methods as helpers (first argument is translates to string)
180
+ for (var name in String.prototype)
181
+ if (!functions[name] && String.prototype[name].call && name.charAt(0) != '$')
182
+ functions[name] = (function(name) {
183
+ return function(a, b) {
184
+ return String(a)[name](b);
185
+ }
186
+ })(name);
187
+
188
+ // Import all number prototype methods as helpers (first argument is translates to number)
189
+ for (var name in Number.prototype)
190
+ if (!functions[name] && Number.prototype[name].call && name.charAt(0) != '$')
191
+ functions[name] = (function(name) {
192
+ return function(a, b) {
193
+ return Number(a)[name](b);
194
+ }
195
+ })(name);
196
+
197
+ var operators = {
198
+ '*': 1,
199
+ '/': 1,
200
+ '+': 2,
201
+ '-': 2,
202
+ '>': 4,
203
+ '<': 4,
204
+ '^': 4,
205
+ '&': 4,
206
+ '|': 4,
207
+ '>=': 4,
208
+ '<=': 4,
209
+ '==': 4,
210
+ '!=': 4,
211
+ '===': 4,
212
+ '!==': 4,
213
+ '&&': 5,
214
+ '||': 5
215
+ };
216
+ for (var operator in operators)
217
+ functions[operator] = new Function('left', 'right', 'return left ' + operator + ' right');
218
+
219
+ var R_TRANSLATE = SheetParser.Value.tokenize;
220
+ var R_FIND = /\\?\{([^{}]+)\}/g;
221
+ var R_VARIABLE = /^[a-z0-9][a-z_\-0-9.\[\]]*$/ig;
222
+ var R_SELECTOR_CONTEXT = /^\s*([$]+)\s*/
223
+ var parsed = {};
224
+ var combinators = Array.object('+', '>', '!+', '++', '!~', '~~', '&', '&&', '$', '$$');
225
+
226
+ Object.append(LSD.Interpolation, {
227
+ translate: function(value) {
228
+ var cached = parsed[name];
229
+ if (cached) return cached;
230
+ var found, result = [], matched = [], scope = result, text, stack = [], operator, selector;
231
+ var names = R_TRANSLATE.names;
232
+ while (found = R_TRANSLATE.exec(value)) matched.push(found);
233
+ for (var i = 0, last = matched.length - 1; found = matched[i]; i++) {
234
+ if ((text = found[names._arguments])) {
235
+ var args = LSD.Interpolation.translate(text);
236
+ for (var j = 0, bit; bit = args[j]; j++) if (bit && bit.length == 1) args[j] = bit[0];
237
+ if ((text = found[names['function']])) {
238
+ scope.push({type: 'function', name: text, value: args.push ? args : [args]});
239
+ } else {
240
+ scope.push(args);
241
+ }
242
+ } else if ((text = (found[names.dstring] || found[names.sstring]))) {
243
+ scope.push(text);
244
+ } else if ((text = (found[names.number]))) {
245
+ scope.push(parseFloat(text));
246
+ } else if ((text = found[names.operator])) {
247
+ if (!selector) {
248
+ previous = stack[stack.length - 1];
249
+ if (left) left = null;
250
+ if (previous) {
251
+ operator = {type: 'function', name: text, index: i, scope: scope, precedence: operators[text]};
252
+ stack.push(operator);
253
+ if (previous.precedence > operator.precedence) {
254
+ scope = previous.scope;
255
+ var left = scope[scope.length - 1];
256
+ if (left.value) {
257
+ if (left.value[1] != null) {
258
+ scope = operator.value = [left.value[1]];
259
+ left.value[1] = operator;
260
+ }
261
+ } else throw "Right part is missing for " + left.name + " operator";
262
+ }
263
+ }
264
+ if (!left) {
265
+ var left = scope.pop();
266
+ if (left == null) {
267
+ if (combinators[text]) {
268
+ selector = {type: 'selector', value: text};
269
+ scope.push(selector);
270
+ } else throw "Left part is missing for " + text + " operator";
271
+ } else {
272
+ var operator = {type: 'function', name: text, index: i, scope: scope, precedence: operators[text]};
273
+ operator.value = [left];
274
+ stack.push(operator);
275
+ scope.push(operator);
276
+ scope = operator.value;
277
+ }
278
+ }
279
+ } else {
280
+ selector.value += ' ' + text;
281
+ text = null;
282
+ }
283
+ } else if ((text = found[names.token])) {
284
+ if (!selector && text.match(R_VARIABLE)) {
285
+ scope.push({type: 'token', name: text});
286
+ } else {
287
+ if (!selector) {
288
+ selector = {type: 'selector', value: text};
289
+ scope.push(selector);
290
+ } else {
291
+ selector.value += ' ' + text;
292
+ text = null;
293
+ }
294
+ }
295
+ }
296
+ if (!operator && text && stack.length) {
297
+ var pop = stack[stack.length - 1]
298
+ if (pop && pop.scope) scope = pop.scope;
299
+ }
300
+ operator = null;
301
+ };
302
+ return (parsed[value] = (result.length == 1 ? result[0] : result));
303
+ },
304
+
305
+ compile: function(object, output, source, translate) {
306
+ if (translate) object = LSD.Interpolation.translate(object);
307
+ switch (object.type) {
308
+ case 'token':
309
+ var Klass = LSD.Interpolation;
310
+ var value = object.name;
311
+ break;
312
+ case 'function':
313
+ var Klass = LSD.Interpolation.Function;
314
+ var name = object.name;
315
+ var value = object.value;
316
+ break;
317
+ case 'selector':
318
+ var Klass = LSD.Interpolation.Selector;
319
+ var value = object.value;
320
+ break;
321
+ default:
322
+ if (object.push) {
323
+ var Klass = LSD.Interpolation.Function
324
+ var value = object;
325
+ } else {
326
+ return object;
327
+ }
328
+ }
329
+ return new Klass(value, output, source, name);
330
+ },
331
+
332
+ textnode: function(textnode, widget, callback) {
333
+ var node = textnode, content = node.textContent, finder, length = content.length;
334
+ for (var match, index, last, next, compiled; match = R_FIND.exec(content);) {
335
+ last = index || 0
336
+ var index = match.index + match[0].length;
337
+ expression = node;
338
+ var cut = index - last;
339
+ if (cut < node.textContent.length) node = node.splitText(index - last);
340
+ if ((cut = (match.index - last))) expression = expression.splitText(cut);
341
+ if (!callback || callback === true) callback = widget;
342
+ compiled = LSD.Interpolation.compile(match[1], expression, callback, true);
343
+ compiled.placeholder = match[0];
344
+ compiled.attach();
345
+ Element.store(expression, 'interpolation', compiled);
346
+ last = index;
347
+ }
348
+ }
349
+ })
350
+
351
+ }();