gumdrop 0.8.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. data/.gitignore +3 -0
  2. data/ChangeLog.md +13 -0
  3. data/Gemfile +6 -2
  4. data/Gemfile.lock +43 -3
  5. data/Rakefile +37 -21
  6. data/Readme.md +60 -158
  7. data/bin/gumdrop +2 -1
  8. data/gumdrop.gemspec +11 -4
  9. data/lib/gumdrop/builder.rb +164 -0
  10. data/lib/gumdrop/cli/external.rb +14 -9
  11. data/lib/gumdrop/cli/internal.rb +54 -22
  12. data/lib/gumdrop/cli.rb +21 -0
  13. data/lib/gumdrop/content.rb +195 -132
  14. data/lib/gumdrop/data.rb +185 -0
  15. data/lib/gumdrop/data_providers/csv.rb +18 -0
  16. data/lib/gumdrop/data_providers/pstore.rb +25 -0
  17. data/lib/gumdrop/data_providers/sqlite.rb +43 -0
  18. data/lib/gumdrop/data_providers/xml.rb +15 -0
  19. data/lib/gumdrop/data_providers/yaml_and_json.rb +18 -0
  20. data/lib/gumdrop/data_providers/yamldb.rb +18 -0
  21. data/lib/gumdrop/data_providers/yamldoc.rb +12 -0
  22. data/lib/gumdrop/generator.rb +118 -94
  23. data/lib/gumdrop/renderer.rb +272 -0
  24. data/lib/gumdrop/server.rb +36 -27
  25. data/lib/gumdrop/site.rb +283 -338
  26. data/lib/gumdrop/support/compressor.rb +52 -0
  27. data/lib/gumdrop/support/sprockets.rb +14 -23
  28. data/lib/gumdrop/support/stitch.rb +25 -14
  29. data/lib/gumdrop/support/yaml_doc_template.rb +29 -0
  30. data/lib/gumdrop/util/configurable.rb +44 -0
  31. data/lib/gumdrop/util/core_ex.rb +84 -0
  32. data/lib/gumdrop/util/eventable.rb +35 -0
  33. data/lib/gumdrop/util/hash_object.rb +70 -0
  34. data/lib/gumdrop/util/loggable.rb +44 -0
  35. data/lib/gumdrop/util/pager.rb +51 -0
  36. data/lib/gumdrop/{support → util}/proxy_handler.rb +7 -6
  37. data/lib/gumdrop/util/scanner.rb +47 -0
  38. data/lib/gumdrop/util/site_access.rb +16 -0
  39. data/lib/gumdrop/util/view_helpers.rb +50 -0
  40. data/lib/gumdrop/util/yaml_doc.rb +51 -0
  41. data/lib/gumdrop/version.rb +2 -2
  42. data/lib/gumdrop.rb +40 -77
  43. data/specs/content_spec.rb +83 -73
  44. data/specs/fixtures/expected/data-access/from-csv.html +4 -0
  45. data/specs/fixtures/expected/data-access/from-json.html +6 -0
  46. data/specs/fixtures/expected/data-access/from-sqlite.html +5 -0
  47. data/specs/fixtures/expected/data-access/from-xml.html +4 -0
  48. data/specs/fixtures/expected/data-access/from-yaml.html +5 -0
  49. data/specs/fixtures/expected/data-access/from-yamldb.html +5 -0
  50. data/specs/fixtures/expected/data-access/from-yamldoc.html +2 -0
  51. data/specs/fixtures/expected/gen-with-block/as-file.html +1 -0
  52. data/specs/fixtures/expected/gen-with-block/as-plain.html +1 -0
  53. data/specs/fixtures/expected/gen-with-block/layout-nested.html +1 -0
  54. data/specs/fixtures/expected/gen-with-block/layout-nil.html +1 -0
  55. data/specs/fixtures/expected/gen-with-block/layout-wrap.html +1 -0
  56. data/specs/fixtures/expected/gen-with-inline-render/0-as-file.html +4 -0
  57. data/specs/fixtures/expected/gen-with-inline-render/0-as-plain.html +4 -0
  58. data/specs/fixtures/expected/gen-with-inline-render/0-layout-nil.html +4 -0
  59. data/specs/fixtures/expected/gen-with-inline-render/0-layout-sub.html +4 -0
  60. data/specs/fixtures/expected/gen-with-inline-render/0-layout-wrap.html +4 -0
  61. data/specs/fixtures/expected/gen-with-inline-render/1-as-file.html +4 -0
  62. data/specs/fixtures/expected/gen-with-inline-render/1-as-plain.html +4 -0
  63. data/specs/fixtures/expected/gen-with-inline-render/1-layout-nil.html +4 -0
  64. data/specs/fixtures/expected/gen-with-inline-render/1-layout-sub.html +4 -0
  65. data/specs/fixtures/expected/gen-with-inline-render/1-layout-wrap.html +4 -0
  66. data/specs/fixtures/expected/goodbye.html +1 -0
  67. data/specs/fixtures/expected/hello.html +1 -0
  68. data/specs/fixtures/expected/image.png +0 -0
  69. data/specs/fixtures/expected/index.html +47 -0
  70. data/specs/fixtures/expected/js/sprockets-app.js +7 -0
  71. data/specs/fixtures/expected/js/stitch-app.js +65 -0
  72. data/specs/fixtures/expected/js/stitch-app.min.js +5 -0
  73. data/specs/fixtures/expected/js/stitch-app.min.src.js +65 -0
  74. data/specs/fixtures/expected/js/straight.js +8 -0
  75. data/specs/fixtures/expected/js/test-coffee.js +5 -0
  76. data/specs/fixtures/expected/pages/docs/force-abs.html +2 -0
  77. data/specs/fixtures/expected/pages/docs/index.html +2 -0
  78. data/specs/fixtures/expected/partials/hoisted-data.html +3 -0
  79. data/specs/fixtures/expected/partials/nested.html +7 -0
  80. data/specs/fixtures/expected/partials/params.html +1 -0
  81. data/specs/fixtures/expected/partials/with-layouts.html +1 -0
  82. data/specs/fixtures/expected/robots.txt +1 -0
  83. data/specs/fixtures/expected/test-multi-procs.html +4 -0
  84. data/specs/fixtures/expected/test-nested-layout.html +1 -0
  85. data/specs/fixtures/expected/test-no-layout.html +2 -0
  86. data/specs/fixtures/expected/test-yamldoc.html +1 -0
  87. data/specs/fixtures/expected/test.html +2 -0
  88. data/{templates/backbone → specs/fixtures/source}/Gemfile +8 -4
  89. data/specs/fixtures/source/Gemfile.lock +84 -0
  90. data/specs/fixtures/source/Gumdrop +60 -9
  91. data/specs/fixtures/source/data/csv_data.csv +2 -0
  92. data/specs/fixtures/source/data/json_data.json +13 -0
  93. data/specs/fixtures/source/data/sql_data.sqlite +0 -0
  94. data/specs/fixtures/source/data/xml_data.xml +4 -0
  95. data/specs/fixtures/source/data/yaml_db.yamldb +9 -0
  96. data/specs/fixtures/source/data/yaml_items.yaml +2 -0
  97. data/specs/fixtures/source/data/yamldoc.yamldoc +4 -0
  98. data/specs/fixtures/source/data-access/from-csv.html.erb +4 -0
  99. data/specs/fixtures/source/data-access/from-json.html.erb +6 -0
  100. data/specs/fixtures/source/data-access/from-sqlite.html.erb +5 -0
  101. data/specs/fixtures/source/data-access/from-xml.html.erb +5 -0
  102. data/specs/fixtures/source/data-access/from-yaml.html.erb +5 -0
  103. data/specs/fixtures/source/data-access/from-yamldb.html.erb +5 -0
  104. data/specs/fixtures/source/data-access/from-yamldoc.html.erb +2 -0
  105. data/specs/fixtures/source/gen-with-inline-render.generator +7 -0
  106. data/specs/fixtures/source/goodbye.html.erb +1 -0
  107. data/specs/fixtures/source/hello.html +1 -0
  108. data/specs/fixtures/source/image.png +0 -0
  109. data/specs/fixtures/source/index.html.erb +6 -0
  110. data/specs/fixtures/source/js/sprockets/app.js +2 -0
  111. data/specs/fixtures/source/js/sprockets/view.js.coffee +1 -0
  112. data/specs/fixtures/source/js/stitch/app.js +1 -0
  113. data/specs/fixtures/source/js/stitch/view.js.coffee +1 -0
  114. data/specs/fixtures/source/js/straight.js +8 -0
  115. data/specs/fixtures/source/js/test-coffee.js.coffee +1 -0
  116. data/specs/fixtures/source/layouts/site.layout.erb +1 -0
  117. data/specs/fixtures/source/layouts/sub.layout.erb +1 -0
  118. data/specs/fixtures/source/layouts/wrap.layout.erb +1 -0
  119. data/specs/fixtures/source/pages/_users-nested.html.erb +7 -0
  120. data/specs/fixtures/source/pages/docs/force-abs.html.erb +2 -0
  121. data/specs/fixtures/source/pages/docs/index.html.erb +2 -0
  122. data/specs/fixtures/source/partials/_post_entry.html.erb +4 -0
  123. data/specs/fixtures/source/partials/_provides_name.html.erb +1 -0
  124. data/specs/fixtures/source/partials/_user-params.html.erb +1 -0
  125. data/specs/fixtures/source/partials/_user.html.erb +1 -0
  126. data/specs/fixtures/source/partials/_user_sets_layout.html.erb +1 -0
  127. data/specs/fixtures/source/partials/hoisted-data.html.erb +6 -0
  128. data/specs/fixtures/source/partials/nested.html.erb +1 -0
  129. data/specs/fixtures/source/partials/params.html.erb +1 -0
  130. data/specs/fixtures/source/partials/with-layouts.html.erb +1 -0
  131. data/specs/fixtures/source/test-multi-procs.html.markdown.erb +3 -0
  132. data/specs/fixtures/source/test-nested-layout.html.erb +1 -0
  133. data/specs/fixtures/source/test-no-layout.html.erb +2 -0
  134. data/specs/fixtures/source/test-yamldoc.html.erb.yamldoc +4 -0
  135. data/specs/fixtures/source/test.html.erb +2 -0
  136. data/specs/hash_object_spec.rb +53 -8
  137. data/specs/render_spec.rb +10 -0
  138. data/specs/site_spec.rb +79 -0
  139. data/specs/spec_helper.rb +59 -0
  140. data/templates/blank/Gemfile +0 -4
  141. data/templates/blank/Gumdrop +180 -113
  142. data/templates/default/Gemfile +3 -8
  143. data/templates/default/Gumdrop +166 -95
  144. data/templates/default/ReadMe.md +14 -0
  145. data/templates/default/config.ru +1 -0
  146. data/templates/default/data/news/20120703.yamldoc +14 -0
  147. data/templates/default/data/news/20120704.yamldoc +8 -0
  148. data/templates/{backbone/source/feed.xml.builder.txt → default/source/feed.xml.builder} +4 -4
  149. data/templates/default/source/index.html.erb +10 -1
  150. data/templates/default/source/theme/layout/_sidebar.html +1 -0
  151. data/templates/{backbone/source/theme/templates/site.template.slim → default/source/theme/layout/site.layout.slim} +3 -3
  152. metadata +212 -63
  153. data/lib/gumdrop/context.rb +0 -135
  154. data/lib/gumdrop/data_manager.rb +0 -214
  155. data/lib/gumdrop/support/base_packager.rb +0 -60
  156. data/lib/gumdrop/support/callbacks.rb +0 -30
  157. data/lib/gumdrop/support/hash_object.rb +0 -22
  158. data/lib/gumdrop/view_helpers.rb +0 -25
  159. data/notes.md +0 -347
  160. data/specs/deferred_loader_spec.rb +0 -31
  161. data/specs/fixtures/expected/posts/post1.html +0 -1
  162. data/specs/fixtures/expected/posts/post1.js +0 -14
  163. data/specs/fixtures/expected/posts/post2.html +0 -5
  164. data/specs/fixtures/expected/sub/sub/sub/test.html +0 -5
  165. data/specs/fixtures/expected/sub/sub/sub/test2.html +0 -5
  166. data/specs/fixtures/expected/test.js +0 -14
  167. data/specs/fixtures/source/posts/post1.html.slim +0 -6
  168. data/specs/fixtures/source/posts/post1.js.coffee +0 -3
  169. data/specs/fixtures/source/posts/post2.html.erb +0 -5
  170. data/specs/fixtures/source/sub/sub/sub/test.html +0 -5
  171. data/specs/fixtures/source/sub/sub/sub/test2.html.erb +0 -5
  172. data/specs/fixtures/source/test.js.coffee +0 -4
  173. data/specs/fixtures/source/test.js.erb.coffee +0 -3
  174. data/templates/backbone/Gumdrop +0 -123
  175. data/templates/backbone/Rakefile +0 -38
  176. data/templates/backbone/app/app.js.coffee +0 -49
  177. data/templates/backbone/app/init.js.coffee +0 -10
  178. data/templates/backbone/app/models/.gitkeep +0 -0
  179. data/templates/backbone/app/utils/index.js.coffee +0 -27
  180. data/templates/backbone/app/views/home.js.coffee +0 -17
  181. data/templates/backbone/app/views/styles/home.scss +0 -8
  182. data/templates/backbone/app/views/templates/home.mustache +0 -2
  183. data/templates/backbone/config.ru +0 -20
  184. data/templates/backbone/lib/all.js.coffee +0 -18
  185. data/templates/backbone/lib/backbone.js +0 -1158
  186. data/templates/backbone/lib/hogan.js +0 -509
  187. data/templates/backbone/lib/jquery.js +0 -9266
  188. data/templates/backbone/lib/underscore.js +0 -981
  189. data/templates/backbone/powrc +0 -2
  190. data/templates/backbone/source/default.htaccess.erb +0 -27
  191. data/templates/backbone/source/favicon.ico +0 -0
  192. data/templates/backbone/source/index.html.erb +0 -2
  193. data/templates/backbone/source/theme/screen.css.sass +0 -9
  194. data/templates/backbone/source/theme/scripts/app.js.coffee +0 -4
  195. data/templates/backbone/source/theme/styles/_tools.scss +0 -434
  196. data/templates/backbone/source/theme/templates/app.template.slim +0 -14
  197. data/templates/default/Rakefile +0 -38
  198. data/templates/default/powrc +0 -2
  199. data/templates/default/source/default.htaccess.erb +0 -27
  200. data/templates/default/source/feed.xml.builder.txt +0 -23
  201. data/templates/default/source/theme/templates/site.template.slim +0 -33
@@ -1,509 +0,0 @@
1
- /*
2
- * Copyright 2011 Twitter, Inc.
3
- * Licensed under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License.
5
- * You may obtain a copy of the License at
6
- *
7
- * http://www.apache.org/licenses/LICENSE-2.0
8
- *
9
- * Unless required by applicable law or agreed to in writing, software
10
- * distributed under the License is distributed on an "AS IS" BASIS,
11
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- * See the License for the specific language governing permissions and
13
- * limitations under the License.
14
- */
15
-
16
- var HoganTemplate = (function () {
17
-
18
- function constructor(text) {
19
- this.text = text;
20
- }
21
-
22
- constructor.prototype = {
23
- // render: replaced by generated code.
24
- r: function (context, partials) { return ''; },
25
-
26
- // variable escaping
27
- v: hoganEscape,
28
-
29
- render: function render(context, partials) {
30
- return this.r(context, partials);
31
- },
32
-
33
- // tries to find a partial in the curent scope and render it
34
- rp: function(name, context, partials, indent) {
35
- var partial = partials[name];
36
-
37
- if (!partial) {
38
- return '';
39
- }
40
-
41
- return partial.render(context, partials);
42
- },
43
-
44
- // render a section
45
- rs: function(context, partials, section) {
46
- var buf = '',
47
- tail = context[context.length - 1];
48
-
49
- if (!isArray(tail)) {
50
- buf = section(context, partials);
51
- return buf;
52
- }
53
-
54
- for (var i = 0; i < tail.length; i++) {
55
- context.push(tail[i]);
56
- buf += section(context, partials);
57
- context.pop();
58
- }
59
- return buf;
60
- },
61
-
62
- // maybe start a section
63
- s: function(val, ctx, partials, inverted, start, end) {
64
- var pass;
65
-
66
- if (isArray(val) && val.length === 0) {
67
- return false;
68
- }
69
-
70
- if (!inverted && typeof val == 'function') {
71
- val = this.ls(val, ctx, partials, start, end);
72
- }
73
-
74
- pass = (val === '') || !!val;
75
-
76
- if (!inverted && pass && ctx) {
77
- ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
78
- }
79
-
80
- return pass;
81
- },
82
-
83
- // find values with dotted names
84
- d: function(key, ctx, partials, returnFound) {
85
-
86
- var names = key.split('.'),
87
- val = this.f(names[0], ctx, partials, returnFound),
88
- cx = null;
89
-
90
- if (key === '.' && isArray(ctx[ctx.length - 2])) {
91
- return ctx[ctx.length - 1];
92
- }
93
-
94
- for (var i = 1; i < names.length; i++) {
95
- if (val && typeof val == 'object' && names[i] in val) {
96
- cx = val;
97
- val = val[names[i]];
98
- } else {
99
- val = '';
100
- }
101
- }
102
-
103
- if (returnFound && !val) {
104
- return false;
105
- }
106
-
107
- if (!returnFound && typeof val == 'function') {
108
- ctx.push(cx);
109
- val = this.lv(val, ctx, partials);
110
- ctx.pop();
111
- }
112
-
113
- return val;
114
- },
115
-
116
- // find values with normal names
117
- f: function(key, ctx, partials, returnFound) {
118
- var val = false,
119
- v = null,
120
- found = false;
121
-
122
- for (var i = ctx.length - 1; i >= 0; i--) {
123
- v = ctx[i];
124
- if (v && typeof v == 'object' && key in v) {
125
- val = v[key];
126
- found = true;
127
- break;
128
- }
129
- }
130
-
131
- if (!found) {
132
- return (returnFound) ? false : "";
133
- }
134
-
135
- if (!returnFound && typeof val == 'function') {
136
- val = this.lv(val, ctx, partials);
137
- }
138
-
139
- return val;
140
- },
141
-
142
- // higher order templates
143
- ho: function(val, cx, partials, text) {
144
- var t = val.call(cx, text, function(t) {
145
- return Hogan.compile(t).render(cx);
146
- });
147
- var s = Hogan.compile(t.toString()).render(cx, partials);
148
- this.b = s;
149
- return false;
150
- },
151
-
152
- // higher order template result buffer
153
- b: '',
154
-
155
- // lambda replace section
156
- ls: function(val, ctx, partials, start, end) {
157
- var cx = ctx[ctx.length - 1],
158
- t = val.call(cx);
159
-
160
- if (val.length > 0) {
161
- return this.ho(val, cx, partials, this.text.substring(start, end));
162
- }
163
-
164
- if (typeof t == 'function') {
165
- return this.ho(t, cx, partials, this.text.substring(start, end));
166
- }
167
- return t;
168
- },
169
-
170
- // lambda replace variable
171
- lv: function(val, ctx, partials) {
172
- var cx = ctx[ctx.length - 1];
173
- return Hogan.compile(val.call(cx).toString()).render(cx, partials);
174
- }
175
- };
176
-
177
- var rAmp = /&/g, rLt = /</g, rGt = />/g, rApos =/\'/g,
178
- rQuot = /\"/g, hChars =/[&<>\"\']/;
179
- function hoganEscape(str) {
180
- var s = String(str === null ? '' : str);
181
- return hChars.test(s) ? s.replace(rAmp,'&amp;')
182
- .replace(rLt,'&lt;').replace(rGt,'&gt;')
183
- .replace(rApos,'&#39;').replace(rQuot, '&quot;') : s;
184
- }
185
-
186
- var isArray = Array.isArray || function(a) {
187
- return Object.prototype.toString.call(a) === '[object Array]';
188
- };
189
-
190
- return constructor;
191
- })();
192
-
193
- var Hogan = (function () {
194
-
195
- // Setup regex assignments
196
- // remove whitespace according to Mustache spec
197
- var rIsWhitespace = /\S/,
198
- rQuot = /\"/g,
199
- rNewline = /\n/g,
200
- rCr = /\r/g,
201
- rSlash = /\\/g,
202
- tagTypes = {
203
- '#': 1, '^': 2, '/': 3, '!': 4, '>': 5,
204
- '<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
205
- };
206
-
207
- function scan(text) {
208
- var len = text.length,
209
- IN_TEXT = 0,
210
- IN_TAG_TYPE = 1,
211
- IN_TAG = 2,
212
- state = IN_TEXT,
213
- tagType = null,
214
- tag = null,
215
- buf = '',
216
- tokens = [],
217
- seenTag = false,
218
- i = 0,
219
- lineStart = 0,
220
- otag = '{{',
221
- ctag = '}}';
222
-
223
- function addBuf() {
224
- if (buf.length > 0) {
225
- tokens.push(new String(buf));
226
- buf = '';
227
- }
228
- }
229
-
230
- function lineIsWhitespace() {
231
- var isAllWhitespace = true;
232
- for (var j = lineStart; j < tokens.length; j++) {
233
- isAllWhitespace =
234
- (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
235
- (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
236
- if (!isAllWhitespace) {
237
- return false;
238
- }
239
- }
240
-
241
- return isAllWhitespace;
242
- }
243
-
244
- function filterLine(haveSeenTag, noNewLine) {
245
- addBuf();
246
- if (haveSeenTag && lineIsWhitespace()) {
247
- for (var j = lineStart; j < tokens.length; j++) {
248
- if (!tokens[j].tag) {
249
- tokens.splice(j, 1);
250
- }
251
- }
252
- } else if (!noNewLine) {
253
- tokens.push({tag:'\n'});
254
- }
255
-
256
- seenTag = false;
257
- lineStart = tokens.length;
258
- }
259
-
260
- function changeDelimiters(text, index) {
261
- var close = '=' + ctag,
262
- closeIndex = text.indexOf(close, index),
263
- delimiters = trim(text.substring(text.indexOf('=', index) + 1,
264
- closeIndex)).split(' ');
265
- otag = delimiters[0];
266
- ctag = delimiters[1];
267
- return closeIndex + close.length - 1;
268
- }
269
-
270
- for (i = 0; i < len; i++) {
271
- if (state == IN_TEXT) {
272
- if (tagChange(otag, text, i)) {
273
- --i;
274
- addBuf();
275
- state = IN_TAG_TYPE;
276
- } else {
277
- if (text[i] == '\n') {
278
- filterLine(seenTag);
279
- } else {
280
- buf += text[i];
281
- }
282
- }
283
- } else if (state == IN_TAG_TYPE) {
284
- i += otag.length - 1;
285
- tag = tagTypes[text[i + 1]];
286
- tagType = tag ? text[i + 1] : '_v';
287
- seenTag = i;
288
- if (tagType == '=') {
289
- i = changeDelimiters(text, i);
290
- state = IN_TEXT;
291
- } else {
292
- if (tag) {
293
- i++;
294
- }
295
- state = IN_TAG;
296
- }
297
- } else {
298
- if (tagChange(ctag, text, i)) {
299
- i += ctag.length - 1;
300
- tokens.push({tag: tagType, n: trim(buf),
301
- i: (tagType == '/') ? seenTag - 1 : i + 1});
302
- buf = '';
303
- state = IN_TEXT;
304
- if (tagType == '{') {
305
- i++;
306
- }
307
- } else {
308
- buf += text[i];
309
- }
310
- }
311
- }
312
-
313
- filterLine(seenTag, true);
314
-
315
- return tokens;
316
- }
317
-
318
- function trim(s) {
319
- if (s.trim) {
320
- return s.trim();
321
- }
322
-
323
- return s.replace(/^\s*|\s*$/g, '');
324
- }
325
-
326
- function tagChange(tag, text, index) {
327
- if (text[index] != tag[0]) {
328
- return false;
329
- }
330
-
331
- for (var i = 1, l = tag.length; i < l; i++) {
332
- if (text[index + i] != tag[i]) {
333
- return false;
334
- }
335
- }
336
-
337
- return true;
338
- }
339
-
340
- function buildTree(tokens, kind, stack, customTags) {
341
- var instructions = [],
342
- opener = null,
343
- token = null;
344
-
345
- while (tokens.length > 0) {
346
- token = tokens.shift();
347
- if (token.tag == '#' || token.tag == '^' ||
348
- isOpener(token, customTags)) {
349
- stack.push(token);
350
- token.nodes = buildTree(tokens, token.tag, stack, customTags);
351
- instructions.push(token);
352
- } else if (token.tag == '/') {
353
- if (stack.length === 0) {
354
- throw new Error('Closing tag without opener: /' + token.n);
355
- }
356
- opener = stack.pop();
357
- if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
358
- throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
359
- }
360
- opener.end = token.i;
361
- return instructions;
362
- } else {
363
- instructions.push(token);
364
- }
365
- }
366
-
367
- if (stack.length > 0) {
368
- throw new Error('missing closing tag: ' + stack.pop().n);
369
- }
370
-
371
- return instructions;
372
- }
373
-
374
- function isOpener(token, tags) {
375
- for (var i = 0, l = tags.length; i < l; i++) {
376
- if (tags[i].o == token.n) {
377
- token.tag = '#';
378
- return true;
379
- }
380
- }
381
- }
382
-
383
- function isCloser(close, open, tags) {
384
- for (var i = 0, l = tags.length; i < l; i++) {
385
- if (tags[i].c == close && tags[i].o == open) {
386
- return true;
387
- }
388
- }
389
- }
390
-
391
- function generate(tree, text, options) {
392
- var code = 'var c = [cx];var b = "";var _ = this;' +
393
- walk(tree) + 'return b;';
394
- if (options.asString) {
395
- return 'function(cx,p){' + code + ';};';
396
- }
397
-
398
- var template = new HoganTemplate(text);
399
- template.r = new Function('cx', 'p', code);
400
- return template;
401
- }
402
-
403
- function esc(s) {
404
- return s.replace(rSlash, '\\\\')
405
- .replace(rQuot, '\\\"')
406
- .replace(rNewline, '\\n')
407
- .replace(rCr, '\\r');
408
- }
409
-
410
- function chooseMethod(s) {
411
- return (~s.indexOf('.')) ? 'd' : 'f';
412
- }
413
-
414
- function walk(tree) {
415
- var code = '';
416
- for (var i = 0, l = tree.length; i < l; i++) {
417
- var tag = tree[i].tag;
418
- if (tag == '#') {
419
- code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
420
- tree[i].i, tree[i].end);
421
- } else if (tag == '^') {
422
- code += invertedSection(tree[i].nodes, tree[i].n,
423
- chooseMethod(tree[i].n));
424
- } else if (tag == '<' || tag == '>') {
425
- code += partial(tree[i].n);
426
- } else if (tag == '{' || tag == '&') {
427
- code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
428
- } else if (tag == '\n') {
429
- code += text('\n');
430
- } else if (tag == '_v') {
431
- code += variable(tree[i].n, chooseMethod(tree[i].n));
432
- } else if (tag === undefined) {
433
- code += text(tree[i]);
434
- }
435
- }
436
- return code;
437
- }
438
-
439
- function section(nodes, id, method, start, end) {
440
- return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
441
- 'c,p,0,' + start + ',' + end + ')){' +
442
- 'b += _.rs(c,p,' +
443
- 'function(c,p){ var b = "";' +
444
- walk(nodes) +
445
- 'return b;});c.pop();}' +
446
- 'else{b += _.b; _.b = ""};';
447
- }
448
-
449
- function invertedSection(nodes, id, method) {
450
- return 'if (!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0)){' +
451
- walk(nodes) +
452
- '};';
453
- }
454
-
455
- function partial(id) {
456
- return 'b += _.rp("' + esc(id) + '",c[c.length - 1],p);';
457
- }
458
-
459
- function tripleStache(id, method) {
460
- return 'b += (_.' + method + '("' + esc(id) + '",c,p,0));';
461
- }
462
-
463
- function variable(id, method) {
464
- return 'b += (_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
465
- }
466
-
467
- function text(id) {
468
- return 'b += "' + esc(id) + '";';
469
- }
470
-
471
- return ({
472
- scan: scan,
473
-
474
- parse: function(tokens, options) {
475
- options = options || {};
476
- return buildTree(tokens, '', [], options.sectionTags || []);
477
- },
478
-
479
- cache: {},
480
-
481
- compile: function(text, options) {
482
- // options
483
- //
484
- // asString: false (default)
485
- //
486
- // sectionTags: [{o: '_foo', c: 'foo'}]
487
- // An array of object with o and c fields that indicate names for custom
488
- // section tags. The example above allows parsing of {{_foo}}{{/foo}}.
489
- //
490
- options = options || {};
491
-
492
- var t = this.cache[text];
493
- if (t) {
494
- return t;
495
- }
496
- t = generate(this.parse(scan(text), options), text, options);
497
- return this.cache[text] = t;
498
- }
499
- });
500
- })();
501
-
502
- // Export the hogan constructor for Node.js and CommonJS.
503
- if (typeof module !== 'undefined' && module.exports) {
504
- module.exports = Hogan;
505
- module.exports.Template = HoganTemplate;
506
- } else if (typeof exports !== 'undefined') {
507
- exports.Hogan = Hogan;
508
- exports.HoganTemplate = HoganTemplate;
509
- }