specterjs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/lib/javascript/assertions/assert.coffee +4 -0
  4. data/lib/javascript/specter.js +79 -0
  5. data/lib/rails/specter.rb +8 -0
  6. data/lib/rails/specter/application/app/assets/javascripts/exec.js +32 -0
  7. data/lib/rails/specter/application/app/assets/javascripts/runner.js +65 -0
  8. data/lib/rails/specter/application/app/assets/javascripts/test.js +104 -0
  9. data/lib/rails/specter/application/app/assets/javascripts/vendors/prism.js +724 -0
  10. data/lib/rails/specter/application/app/assets/stylesheets/application.scss +89 -0
  11. data/lib/rails/specter/application/app/assets/stylesheets/test.scss +37 -0
  12. data/lib/rails/specter/application/app/assets/stylesheets/tests.scss +76 -0
  13. data/lib/rails/specter/application/app/assets/stylesheets/variables.scss +11 -0
  14. data/lib/rails/specter/application/app/assets/stylesheets/vendors/prism.css +197 -0
  15. data/lib/rails/specter/application/app/controllers/application_controller.rb +2 -0
  16. data/lib/rails/specter/application/app/controllers/tests_controller.rb +29 -0
  17. data/lib/rails/specter/application/app/models/specter/test.rb +26 -0
  18. data/lib/rails/specter/application/app/views/layouts/application.html.erb +24 -0
  19. data/lib/rails/specter/application/app/views/tests/index.html.erb +19 -0
  20. data/lib/rails/specter/application/app/views/tests/run.html.erb +20 -0
  21. data/lib/rails/specter/application/app/views/tests/show.html.erb +14 -0
  22. data/lib/rails/specter/application/application.rb +13 -0
  23. data/lib/rails/specter/application/config.ru +12 -0
  24. data/lib/rails/specter/engine.rb +14 -0
  25. data/lib/rails/specter/tasks/specter.rake +47 -0
  26. data/lib/rails/version.rb +3 -0
  27. data/specter.gemspec +28 -0
  28. metadata +97 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: de9fb4363dbfc4d222dbec9491d7a624d9063f0b
4
+ data.tar.gz: ca404510a8ea2942318f8c9bed350546fc660268
5
+ SHA512:
6
+ metadata.gz: 6caba7c26f8e95409d936272cc91ddae951928cddad61429fe43e23365944163766429029d4294b677c2c3c7fcec61e040fd0707ec1c7a9678ca2f04c58d42ea
7
+ data.tar.gz: faebc4e0230cbd2eefb7b840825199bc6e2cee5034dbefe988c009ac90ab10a8b6d98f0b078d97cd6528c6ae5a39e5058ba756567198137669ae08627e1d8ddc
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ node_modules
3
+ .git
4
+ .byebug_history
5
+ *.log
6
+ server.pid
7
+
@@ -0,0 +1,4 @@
1
+ Specter.assertions.register 'assert', (result, message = "No message given") ->
2
+ if result != true
3
+ throw new AssertionException(message)
4
+
@@ -0,0 +1,79 @@
1
+ window.Specter = {}
2
+
3
+ Specter.compare = function(real, expected) {
4
+ Specter.removeBlankNode(real)
5
+ Specter.removeBlankNode(expected)
6
+
7
+ Specter.forceTextNodeOnEmptyNode(real)
8
+ Specter.forceTextNodeOnEmptyNode(expected)
9
+
10
+ Specter.compare.element(real, expected)
11
+ Specter.compare.attributes(real, expected)
12
+ Specter.compare.textContent(real, expected)
13
+
14
+ for (i = 0; i < real.childNodes.length; i++) {
15
+ Specter.compare(real.childNodes[i], expected.childNodes[i]);
16
+ }
17
+
18
+ return true;
19
+ }
20
+
21
+ Specter.compare.attributes = function(node, expected) {
22
+ if ((node.attributes || []).length !== (expected.attributes || []).length) {
23
+ throw new Specter.Error(node, expected, "Not the same number of attributes expected")
24
+ }
25
+
26
+ Array.prototype.forEach.call(expected.attributes || [], function(attribute) {
27
+ if (node.getAttribute(attribute.name) !== attribute.value) {
28
+ throw new Specter.Error(node, expected, "Attributes do not match")
29
+ }
30
+ })
31
+ }
32
+
33
+ Specter.compare.element = function(node, expected) {
34
+ if (node.nodeName !== expected.nodeName) {
35
+ throw new Specter.Error(node, expected, "Unexpected element")
36
+ }
37
+
38
+ if (node.childNodes.length !== expected.childNodes.length) {
39
+ throw new Specter.Error(node, expected, "Not same amount of children")
40
+ }
41
+ }
42
+
43
+ Specter.compare.textContent = function(node, expected) {
44
+ if (node.nodeType === Element.TEXT_NODE) {
45
+ if (node.textContent.trim() !== expected.textContent.trim()) {
46
+ throw new Specter.Error(node.parentElement, expected.parentElement, "The text content is not identical")
47
+ }
48
+ }
49
+ }
50
+
51
+ Specter.forceTextNodeOnEmptyNode = function(node) {
52
+ if (node.nodeType === Element.ELEMENT_NODE && node.childNodes.length === 0) {
53
+ node.appendChild(document.createTextNode(''))
54
+ }
55
+ }
56
+
57
+ Specter.removeBlankNode = function(node) {
58
+ Array.prototype.forEach.call(node.childNodes || [], function(node) {
59
+ if (node.textContent.length === 0 || /^\s*$/.test(node.textContent)) {
60
+ node.remove();
61
+ }
62
+ })
63
+ }
64
+
65
+ Specter.Error = function(node, expected, message) {
66
+ this.expected = expected
67
+
68
+ this.node = function() {
69
+ return node
70
+ }
71
+
72
+ this.message = function() {
73
+ return message
74
+ }
75
+
76
+ this.toString = function() {
77
+ return this.message()
78
+ }
79
+ }
@@ -0,0 +1,8 @@
1
+ module Specter
2
+
3
+ end
4
+
5
+ if defined?(Rails)
6
+ require 'specter/engine'
7
+ end
8
+
@@ -0,0 +1,32 @@
1
+ test.setup()
2
+ var run = function() {
3
+ test.execute()
4
+
5
+ var wrapper = document.getElementById('SpecterTestWrapper')
6
+ var real = wrapper.children[0]
7
+ var expected = wrapper.children[1]
8
+ expected.remove()
9
+
10
+ try {
11
+ Specter.compare(real, expected)
12
+ if (window.parent !== undefined) {
13
+ window.parent.postMessage({
14
+ status: 'success'
15
+ }, '*')
16
+ } else {
17
+ console.log("Test successfully complete")
18
+ }
19
+ } catch(e) {
20
+ if (window.parent !== undefined) {
21
+ window.parent.postMessage({
22
+ status: 'error',
23
+ error: e.toString(),
24
+ html: e.node().outerHTML
25
+ }, '*')
26
+ } else {
27
+ console.log(e)
28
+ }
29
+ }
30
+ }
31
+
32
+ window.setTimeout(run, 50)
@@ -0,0 +1,65 @@
1
+ class Runner {
2
+ constructor(button, tests) {
3
+ if (!button || tests.length === 0) {
4
+ return;
5
+ }
6
+
7
+
8
+ this.button = button
9
+ this.tests = tests
10
+
11
+ this.button.addEventListener('click', this.start.bind(this));
12
+ window.addEventListener('message', this.received.bind(this), false);
13
+ }
14
+
15
+ start() {
16
+ if (this.button.dataset.running !== 'false') {
17
+ return
18
+ }
19
+
20
+ this.button.dataset.running = 'true'
21
+ this.count = this.tests.length
22
+
23
+ for (var i = 0; i < this.count; i++) {
24
+ var test = this.tests[i]
25
+ test.querySelector('div.status').textContent = ''
26
+ test.dataset.status = 'started'
27
+ var iframe = document.createElement('iframe')
28
+ iframe.src = test.dataset.run
29
+ iframe.style.display = 'none'
30
+ document.body.appendChild(iframe)
31
+
32
+ test.iframe = iframe
33
+ iframe.contentWindow.listElement = test
34
+ }
35
+ }
36
+
37
+ received(message) {
38
+ var test = message.source.listElement
39
+
40
+ if (message.data.status === 'success') {
41
+ test.dataset.status = 'success'
42
+ var status = test.querySelector('div.status')
43
+ status.textContent = status.dataset.success
44
+ }
45
+ else {
46
+ test.dataset.status = 'failed'
47
+ var status = test.querySelector('div.status')
48
+ status.textContent = status.dataset.error
49
+ var span = test.querySelector('div > span')
50
+ span.textContent = message.data.error
51
+ }
52
+
53
+ this.count -= 1
54
+ message.source.listElement.iframe.remove()
55
+
56
+ if (this.count <= 0) {
57
+ this.button.dataset.running = 'false'
58
+ }
59
+ }
60
+ }
61
+
62
+ (function(window, document) {
63
+ window.Runner = new Runner(document.querySelector('#Run'), document.querySelectorAll('.test'))
64
+
65
+ })(window, document)
@@ -0,0 +1,104 @@
1
+ //= require 'vendors/prism.js'
2
+
3
+ (function(window, document) {
4
+ class Test {
5
+ constructor(element) {
6
+ window.addEventListener('message', this.received.bind(this), false);
7
+ }
8
+
9
+ received(message) {
10
+ var result = this.readableOutput(message.source.document.querySelector('#SpecterTestWrapper > div').innerHTML)
11
+ var container = document.querySelector('#Test section.result')
12
+ var header = container.querySelector('header')
13
+ var code = container.querySelector('pre.real > code')
14
+ var pres = container.querySelectorAll('pre')
15
+ var line = this.lineNumber(result, message.data.html)
16
+
17
+ code.textContent = result
18
+
19
+ if (message.data.status == 'error') {
20
+ Array.prototype.forEach.call(pres, function(pre) {
21
+ pre.dataset.line = line
22
+ }, this)
23
+
24
+ header.classList.add('error')
25
+ header.querySelector('div.status').textContent = "❌"
26
+ header.querySelector('div.message').textContent = message.data.error
27
+ }
28
+ else {
29
+ header.classList.add('success')
30
+ header.querySelector('div.status').textContent = "✓"
31
+ header.querySelector('div.message').textContent = "The test has passed"
32
+ container.querySelector('pre.expected').remove()
33
+ }
34
+ Array.prototype.forEach.call(pres, function(pre) {
35
+ this.parse(pre)
36
+ }, this)
37
+ }
38
+
39
+ readableOutput(str) {
40
+ return str.replace(/></gi, ">\n<").trim()
41
+ }
42
+
43
+ lineNumber(code, str) {
44
+ var num = 1
45
+
46
+ code.split("\n").forEach(function(text, line) {
47
+ if (str == text) {
48
+ num = line
49
+ }
50
+ })
51
+
52
+ return num
53
+ }
54
+
55
+ parse(pre) {
56
+ var code = pre.querySelector('code')
57
+ var line = parseInt(pre.dataset.line)
58
+
59
+ Prism.highlightElement(code)
60
+
61
+ if (line > 0) {
62
+ this.showErrorOnLineNumber(code, line)
63
+ }
64
+ }
65
+
66
+ showErrorOnLineNumber(code, lineNumber) {
67
+ var elements = []
68
+ var error = document.createElement('span')
69
+ var count = 0
70
+
71
+ for (var i = 0; i < code.childNodes.length && count <= lineNumber; i++) {
72
+ var node = code.childNodes[i]
73
+
74
+ if (count == lineNumber) {
75
+ elements.push(node)
76
+ }
77
+
78
+ if (node.nodeType === node.TEXT_NODE && node.textContent === "\n") {
79
+ count += 1
80
+ }
81
+ }
82
+
83
+ if (elements.length === 0) {
84
+ return
85
+ }
86
+
87
+ error.classList.add('error')
88
+ code.insertBefore(error, elements[0])
89
+
90
+ for (var i = 0; i < elements.length; i++) {
91
+ error.appendChild(elements[i])
92
+ }
93
+
94
+ code.querySelector('span.line-numbers-rows').children[lineNumber].classList.add('error')
95
+
96
+ }
97
+ }
98
+
99
+ var element = document.getElementById('Test')
100
+ if (element) {
101
+ window.Test = new Test(element)
102
+ }
103
+
104
+ })(window, document)
@@ -0,0 +1,724 @@
1
+ /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript&plugins=line-numbers */
2
+ var _self = (typeof window !== 'undefined')
3
+ ? window // if in browser
4
+ : (
5
+ (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
6
+ ? self // if in worker
7
+ : {} // if in node js
8
+ );
9
+
10
+ /**
11
+ * Prism: Lightweight, robust, elegant syntax highlighting
12
+ * MIT license http://www.opensource.org/licenses/mit-license.php/
13
+ * @author Lea Verou http://lea.verou.me
14
+ */
15
+
16
+ var Prism = (function(){
17
+
18
+ // Private helper vars
19
+ var lang = /\blang(?:uage)?-(\w+)\b/i;
20
+ var uniqueId = 0;
21
+
22
+ var _ = _self.Prism = {
23
+ util: {
24
+ encode: function (tokens) {
25
+ if (tokens instanceof Token) {
26
+ return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
27
+ } else if (_.util.type(tokens) === 'Array') {
28
+ return tokens.map(_.util.encode);
29
+ } else {
30
+ return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');
31
+ }
32
+ },
33
+
34
+ type: function (o) {
35
+ return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
36
+ },
37
+
38
+ objId: function (obj) {
39
+ if (!obj['__id']) {
40
+ Object.defineProperty(obj, '__id', { value: ++uniqueId });
41
+ }
42
+ return obj['__id'];
43
+ },
44
+
45
+ // Deep clone a language definition (e.g. to extend it)
46
+ clone: function (o) {
47
+ var type = _.util.type(o);
48
+
49
+ switch (type) {
50
+ case 'Object':
51
+ var clone = {};
52
+
53
+ for (var key in o) {
54
+ if (o.hasOwnProperty(key)) {
55
+ clone[key] = _.util.clone(o[key]);
56
+ }
57
+ }
58
+
59
+ return clone;
60
+
61
+ case 'Array':
62
+ // Check for existence for IE8
63
+ return o.map && o.map(function(v) { return _.util.clone(v); });
64
+ }
65
+
66
+ return o;
67
+ }
68
+ },
69
+
70
+ languages: {
71
+ extend: function (id, redef) {
72
+ var lang = _.util.clone(_.languages[id]);
73
+
74
+ for (var key in redef) {
75
+ lang[key] = redef[key];
76
+ }
77
+
78
+ return lang;
79
+ },
80
+
81
+ /**
82
+ * Insert a token before another token in a language literal
83
+ * As this needs to recreate the object (we cannot actually insert before keys in object literals),
84
+ * we cannot just provide an object, we need anobject and a key.
85
+ * @param inside The key (or language id) of the parent
86
+ * @param before The key to insert before. If not provided, the function appends instead.
87
+ * @param insert Object with the key/value pairs to insert
88
+ * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
89
+ */
90
+ insertBefore: function (inside, before, insert, root) {
91
+ root = root || _.languages;
92
+ var grammar = root[inside];
93
+
94
+ if (arguments.length == 2) {
95
+ insert = arguments[1];
96
+
97
+ for (var newToken in insert) {
98
+ if (insert.hasOwnProperty(newToken)) {
99
+ grammar[newToken] = insert[newToken];
100
+ }
101
+ }
102
+
103
+ return grammar;
104
+ }
105
+
106
+ var ret = {};
107
+
108
+ for (var token in grammar) {
109
+
110
+ if (grammar.hasOwnProperty(token)) {
111
+
112
+ if (token == before) {
113
+
114
+ for (var newToken in insert) {
115
+
116
+ if (insert.hasOwnProperty(newToken)) {
117
+ ret[newToken] = insert[newToken];
118
+ }
119
+ }
120
+ }
121
+
122
+ ret[token] = grammar[token];
123
+ }
124
+ }
125
+
126
+ // Update references in other language definitions
127
+ _.languages.DFS(_.languages, function(key, value) {
128
+ if (value === root[inside] && key != inside) {
129
+ this[key] = ret;
130
+ }
131
+ });
132
+
133
+ return root[inside] = ret;
134
+ },
135
+
136
+ // Traverse a language definition with Depth First Search
137
+ DFS: function(o, callback, type, visited) {
138
+ visited = visited || {};
139
+ for (var i in o) {
140
+ if (o.hasOwnProperty(i)) {
141
+ callback.call(o, i, o[i], type || i);
142
+
143
+ if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) {
144
+ visited[_.util.objId(o[i])] = true;
145
+ _.languages.DFS(o[i], callback, null, visited);
146
+ }
147
+ else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) {
148
+ visited[_.util.objId(o[i])] = true;
149
+ _.languages.DFS(o[i], callback, i, visited);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ },
155
+ plugins: {},
156
+
157
+ highlightAll: function(async, callback) {
158
+ var env = {
159
+ callback: callback,
160
+ selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
161
+ };
162
+
163
+ _.hooks.run("before-highlightall", env);
164
+
165
+ var elements = env.elements || document.querySelectorAll(env.selector);
166
+
167
+ for (var i=0, element; element = elements[i++];) {
168
+ _.highlightElement(element, async === true, env.callback);
169
+ }
170
+ },
171
+
172
+ highlightElement: function(element, async, callback) {
173
+ // Find language
174
+ var language, grammar, parent = element;
175
+
176
+ while (parent && !lang.test(parent.className)) {
177
+ parent = parent.parentNode;
178
+ }
179
+
180
+ if (parent) {
181
+ language = (parent.className.match(lang) || [,''])[1];
182
+ grammar = _.languages[language];
183
+ }
184
+
185
+ // Set language on the element, if not present
186
+ element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
187
+
188
+ // Set language on the parent, for styling
189
+ parent = element.parentNode;
190
+
191
+ if (/pre/i.test(parent.nodeName)) {
192
+ parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
193
+ }
194
+
195
+ var code = element.textContent;
196
+
197
+ var env = {
198
+ element: element,
199
+ language: language,
200
+ grammar: grammar,
201
+ code: code
202
+ };
203
+
204
+ if (!code || !grammar) {
205
+ _.hooks.run('complete', env);
206
+ return;
207
+ }
208
+
209
+ _.hooks.run('before-highlight', env);
210
+
211
+ if (async && _self.Worker) {
212
+ var worker = new Worker(_.filename);
213
+
214
+ worker.onmessage = function(evt) {
215
+ env.highlightedCode = evt.data;
216
+
217
+ _.hooks.run('before-insert', env);
218
+
219
+ env.element.innerHTML = env.highlightedCode;
220
+
221
+ callback && callback.call(env.element);
222
+ _.hooks.run('after-highlight', env);
223
+ _.hooks.run('complete', env);
224
+ };
225
+
226
+ worker.postMessage(JSON.stringify({
227
+ language: env.language,
228
+ code: env.code,
229
+ immediateClose: true
230
+ }));
231
+ }
232
+ else {
233
+ env.highlightedCode = _.highlight(env.code, env.grammar, env.language);
234
+
235
+ _.hooks.run('before-insert', env);
236
+
237
+ env.element.innerHTML = env.highlightedCode;
238
+
239
+ callback && callback.call(element);
240
+
241
+ _.hooks.run('after-highlight', env);
242
+ _.hooks.run('complete', env);
243
+ }
244
+ },
245
+
246
+ highlight: function (text, grammar, language) {
247
+ var tokens = _.tokenize(text, grammar);
248
+ return Token.stringify(_.util.encode(tokens), language);
249
+ },
250
+
251
+ tokenize: function(text, grammar, language) {
252
+ var Token = _.Token;
253
+
254
+ var strarr = [text];
255
+
256
+ var rest = grammar.rest;
257
+
258
+ if (rest) {
259
+ for (var token in rest) {
260
+ grammar[token] = rest[token];
261
+ }
262
+
263
+ delete grammar.rest;
264
+ }
265
+
266
+ tokenloop: for (var token in grammar) {
267
+ if(!grammar.hasOwnProperty(token) || !grammar[token]) {
268
+ continue;
269
+ }
270
+
271
+ var patterns = grammar[token];
272
+ patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];
273
+
274
+ for (var j = 0; j < patterns.length; ++j) {
275
+ var pattern = patterns[j],
276
+ inside = pattern.inside,
277
+ lookbehind = !!pattern.lookbehind,
278
+ greedy = !!pattern.greedy,
279
+ lookbehindLength = 0,
280
+ alias = pattern.alias;
281
+
282
+ pattern = pattern.pattern || pattern;
283
+
284
+ for (var i=0; i<strarr.length; i++) { // Don’t cache length as it changes during the loop
285
+
286
+ var str = strarr[i];
287
+
288
+ if (strarr.length > text.length) {
289
+ // Something went terribly wrong, ABORT, ABORT!
290
+ break tokenloop;
291
+ }
292
+
293
+ if (str instanceof Token) {
294
+ continue;
295
+ }
296
+
297
+ pattern.lastIndex = 0;
298
+
299
+ var match = pattern.exec(str),
300
+ delNum = 1;
301
+
302
+ // Greedy patterns can override/remove up to two previously matched tokens
303
+ if (!match && greedy && i != strarr.length - 1) {
304
+ // Reconstruct the original text using the next two tokens
305
+ var nextToken = strarr[i + 1].matchedStr || strarr[i + 1],
306
+ combStr = str + nextToken;
307
+
308
+ if (i < strarr.length - 2) {
309
+ combStr += strarr[i + 2].matchedStr || strarr[i + 2];
310
+ }
311
+
312
+ // Try the pattern again on the reconstructed text
313
+ pattern.lastIndex = 0;
314
+ match = pattern.exec(combStr);
315
+ if (!match) {
316
+ continue;
317
+ }
318
+
319
+ var from = match.index + (lookbehind ? match[1].length : 0);
320
+ // To be a valid candidate, the new match has to start inside of str
321
+ if (from >= str.length) {
322
+ continue;
323
+ }
324
+ var to = match.index + match[0].length,
325
+ len = str.length + nextToken.length;
326
+
327
+ // Number of tokens to delete and replace with the new match
328
+ delNum = 3;
329
+
330
+ if (to <= len) {
331
+ if (strarr[i + 1].greedy) {
332
+ continue;
333
+ }
334
+ delNum = 2;
335
+ combStr = combStr.slice(0, len);
336
+ }
337
+ str = combStr;
338
+ }
339
+
340
+ if (!match) {
341
+ continue;
342
+ }
343
+
344
+ if(lookbehind) {
345
+ lookbehindLength = match[1].length;
346
+ }
347
+
348
+ var from = match.index + lookbehindLength,
349
+ match = match[0].slice(lookbehindLength),
350
+ to = from + match.length,
351
+ before = str.slice(0, from),
352
+ after = str.slice(to);
353
+
354
+ var args = [i, delNum];
355
+
356
+ if (before) {
357
+ args.push(before);
358
+ }
359
+
360
+ var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
361
+
362
+ args.push(wrapped);
363
+
364
+ if (after) {
365
+ args.push(after);
366
+ }
367
+
368
+ Array.prototype.splice.apply(strarr, args);
369
+ }
370
+ }
371
+ }
372
+
373
+ return strarr;
374
+ },
375
+
376
+ hooks: {
377
+ all: {},
378
+
379
+ add: function (name, callback) {
380
+ var hooks = _.hooks.all;
381
+
382
+ hooks[name] = hooks[name] || [];
383
+
384
+ hooks[name].push(callback);
385
+ },
386
+
387
+ run: function (name, env) {
388
+ var callbacks = _.hooks.all[name];
389
+
390
+ if (!callbacks || !callbacks.length) {
391
+ return;
392
+ }
393
+
394
+ for (var i=0, callback; callback = callbacks[i++];) {
395
+ callback(env);
396
+ }
397
+ }
398
+ }
399
+ };
400
+
401
+ var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
402
+ this.type = type;
403
+ this.content = content;
404
+ this.alias = alias;
405
+ // Copy of the full string this token was created from
406
+ this.matchedStr = matchedStr || null;
407
+ this.greedy = !!greedy;
408
+ };
409
+
410
+ Token.stringify = function(o, language, parent) {
411
+ if (typeof o == 'string') {
412
+ return o;
413
+ }
414
+
415
+ if (_.util.type(o) === 'Array') {
416
+ return o.map(function(element) {
417
+ return Token.stringify(element, language, o);
418
+ }).join('');
419
+ }
420
+
421
+ var env = {
422
+ type: o.type,
423
+ content: Token.stringify(o.content, language, parent),
424
+ tag: 'span',
425
+ classes: ['token', o.type],
426
+ attributes: {},
427
+ language: language,
428
+ parent: parent
429
+ };
430
+
431
+ if (env.type == 'comment') {
432
+ env.attributes['spellcheck'] = 'true';
433
+ }
434
+
435
+ if (o.alias) {
436
+ var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
437
+ Array.prototype.push.apply(env.classes, aliases);
438
+ }
439
+
440
+ _.hooks.run('wrap', env);
441
+
442
+ var attributes = '';
443
+
444
+ for (var name in env.attributes) {
445
+ attributes += (attributes ? ' ' : '') + name + '="' + (env.attributes[name] || '') + '"';
446
+ }
447
+
448
+ return '<' + env.tag + ' class="' + env.classes.join(' ') + '" ' + attributes + '>' + env.content + '</' + env.tag + '>';
449
+
450
+ };
451
+
452
+ if (!_self.document) {
453
+ if (!_self.addEventListener) {
454
+ // in Node.js
455
+ return _self.Prism;
456
+ }
457
+ // In worker
458
+ _self.addEventListener('message', function(evt) {
459
+ var message = JSON.parse(evt.data),
460
+ lang = message.language,
461
+ code = message.code,
462
+ immediateClose = message.immediateClose;
463
+
464
+ _self.postMessage(_.highlight(code, _.languages[lang], lang));
465
+ if (immediateClose) {
466
+ _self.close();
467
+ }
468
+ }, false);
469
+
470
+ return _self.Prism;
471
+ }
472
+
473
+ //Get current script and highlight
474
+ var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();
475
+
476
+ if (script) {
477
+ _.filename = script.src;
478
+
479
+ if (document.addEventListener && !script.hasAttribute('data-manual')) {
480
+ document.addEventListener('DOMContentLoaded', _.highlightAll);
481
+ }
482
+ }
483
+
484
+ return _self.Prism;
485
+
486
+ })();
487
+
488
+ if (typeof module !== 'undefined' && module.exports) {
489
+ module.exports = Prism;
490
+ }
491
+
492
+ // hack for components to work correctly in node.js
493
+ if (typeof global !== 'undefined') {
494
+ global.Prism = Prism;
495
+ }
496
+ ;
497
+ Prism.languages.markup = {
498
+ 'comment': /<!--[\w\W]*?-->/,
499
+ 'prolog': /<\?[\w\W]+?\?>/,
500
+ 'doctype': /<!DOCTYPE[\w\W]+?>/,
501
+ 'cdata': /<!\[CDATA\[[\w\W]*?]]>/i,
502
+ 'tag': {
503
+ pattern: /<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,
504
+ inside: {
505
+ 'tag': {
506
+ pattern: /^<\/?[^\s>\/]+/i,
507
+ inside: {
508
+ 'punctuation': /^<\/?/,
509
+ 'namespace': /^[^\s>\/:]+:/
510
+ }
511
+ },
512
+ 'attr-value': {
513
+ pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,
514
+ inside: {
515
+ 'punctuation': /[=>"']/
516
+ }
517
+ },
518
+ 'punctuation': /\/?>/,
519
+ 'attr-name': {
520
+ pattern: /[^\s>\/]+/,
521
+ inside: {
522
+ 'namespace': /^[^\s>\/:]+:/
523
+ }
524
+ }
525
+
526
+ }
527
+ },
528
+ 'entity': /&#?[\da-z]{1,8};/i
529
+ };
530
+
531
+ // Plugin to make entity title show the real entity, idea by Roman Komarov
532
+ Prism.hooks.add('wrap', function(env) {
533
+
534
+ if (env.type === 'entity') {
535
+ env.attributes['title'] = env.content.replace(/&amp;/, '&');
536
+ }
537
+ });
538
+
539
+ Prism.languages.xml = Prism.languages.markup;
540
+ Prism.languages.html = Prism.languages.markup;
541
+ Prism.languages.mathml = Prism.languages.markup;
542
+ Prism.languages.svg = Prism.languages.markup;
543
+
544
+ Prism.languages.css = {
545
+ 'comment': /\/\*[\w\W]*?\*\//,
546
+ 'atrule': {
547
+ pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i,
548
+ inside: {
549
+ 'rule': /@[\w-]+/
550
+ // See rest below
551
+ }
552
+ },
553
+ 'url': /url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
554
+ 'selector': /[^\{\}\s][^\{\};]*?(?=\s*\{)/,
555
+ 'string': /("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,
556
+ 'property': /(\b|\B)[\w-]+(?=\s*:)/i,
557
+ 'important': /\B!important\b/i,
558
+ 'function': /[-a-z0-9]+(?=\()/i,
559
+ 'punctuation': /[(){};:]/
560
+ };
561
+
562
+ Prism.languages.css['atrule'].inside.rest = Prism.util.clone(Prism.languages.css);
563
+
564
+ if (Prism.languages.markup) {
565
+ Prism.languages.insertBefore('markup', 'tag', {
566
+ 'style': {
567
+ pattern: /(<style[\w\W]*?>)[\w\W]*?(?=<\/style>)/i,
568
+ lookbehind: true,
569
+ inside: Prism.languages.css,
570
+ alias: 'language-css'
571
+ }
572
+ });
573
+
574
+ Prism.languages.insertBefore('inside', 'attr-value', {
575
+ 'style-attr': {
576
+ pattern: /\s*style=("|').*?\1/i,
577
+ inside: {
578
+ 'attr-name': {
579
+ pattern: /^\s*style/i,
580
+ inside: Prism.languages.markup.tag.inside
581
+ },
582
+ 'punctuation': /^\s*=\s*['"]|['"]\s*$/,
583
+ 'attr-value': {
584
+ pattern: /.+/i,
585
+ inside: Prism.languages.css
586
+ }
587
+ },
588
+ alias: 'language-css'
589
+ }
590
+ }, Prism.languages.markup.tag);
591
+ };
592
+ Prism.languages.clike = {
593
+ 'comment': [
594
+ {
595
+ pattern: /(^|[^\\])\/\*[\w\W]*?\*\//,
596
+ lookbehind: true
597
+ },
598
+ {
599
+ pattern: /(^|[^\\:])\/\/.*/,
600
+ lookbehind: true
601
+ }
602
+ ],
603
+ 'string': {
604
+ pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
605
+ greedy: true
606
+ },
607
+ 'class-name': {
608
+ pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,
609
+ lookbehind: true,
610
+ inside: {
611
+ punctuation: /(\.|\\)/
612
+ }
613
+ },
614
+ 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,
615
+ 'boolean': /\b(true|false)\b/,
616
+ 'function': /[a-z0-9_]+(?=\()/i,
617
+ 'number': /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,
618
+ 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,
619
+ 'punctuation': /[{}[\];(),.:]/
620
+ };
621
+
622
+ Prism.languages.javascript = Prism.languages.extend('clike', {
623
+ 'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,
624
+ 'number': /\b-?(0x[\dA-Fa-f]+|0b[01]+|0o[0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/,
625
+ // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
626
+ 'function': /[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i
627
+ });
628
+
629
+ Prism.languages.insertBefore('javascript', 'keyword', {
630
+ 'regex': {
631
+ pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/,
632
+ lookbehind: true,
633
+ greedy: true
634
+ }
635
+ });
636
+
637
+ Prism.languages.insertBefore('javascript', 'class-name', {
638
+ 'template-string': {
639
+ pattern: /`(?:\\\\|\\?[^\\])*?`/,
640
+ greedy: true,
641
+ inside: {
642
+ 'interpolation': {
643
+ pattern: /\$\{[^}]+\}/,
644
+ inside: {
645
+ 'interpolation-punctuation': {
646
+ pattern: /^\$\{|\}$/,
647
+ alias: 'punctuation'
648
+ },
649
+ rest: Prism.languages.javascript
650
+ }
651
+ },
652
+ 'string': /[\s\S]+/
653
+ }
654
+ }
655
+ });
656
+
657
+ if (Prism.languages.markup) {
658
+ Prism.languages.insertBefore('markup', 'tag', {
659
+ 'script': {
660
+ pattern: /(<script[\w\W]*?>)[\w\W]*?(?=<\/script>)/i,
661
+ lookbehind: true,
662
+ inside: Prism.languages.javascript,
663
+ alias: 'language-javascript'
664
+ }
665
+ });
666
+ }
667
+
668
+ Prism.languages.js = Prism.languages.javascript;
669
+ (function() {
670
+
671
+ if (typeof self === 'undefined' || !self.Prism || !self.document) {
672
+ return;
673
+ }
674
+
675
+ Prism.hooks.add('complete', function (env) {
676
+ if (!env.code) {
677
+ return;
678
+ }
679
+
680
+ // works only for <code> wrapped inside <pre> (not inline)
681
+ var pre = env.element.parentNode;
682
+ var clsReg = /\s*\bline-numbers\b\s*/;
683
+ if (
684
+ !pre || !/pre/i.test(pre.nodeName) ||
685
+ // Abort only if nor the <pre> nor the <code> have the class
686
+ (!clsReg.test(pre.className) && !clsReg.test(env.element.className))
687
+ ) {
688
+ return;
689
+ }
690
+
691
+ if (env.element.querySelector(".line-numbers-rows")) {
692
+ // Abort if line numbers already exists
693
+ return;
694
+ }
695
+
696
+ if (clsReg.test(env.element.className)) {
697
+ // Remove the class "line-numbers" from the <code>
698
+ env.element.className = env.element.className.replace(clsReg, '');
699
+ }
700
+ if (!clsReg.test(pre.className)) {
701
+ // Add the class "line-numbers" to the <pre>
702
+ pre.className += ' line-numbers';
703
+ }
704
+
705
+ var match = env.code.match(/\n(?!$)/g);
706
+ var linesNum = match ? match.length + 1 : 1;
707
+ var lineNumbersWrapper;
708
+
709
+ var lines = new Array(linesNum + 1);
710
+ lines = lines.join('<span></span>');
711
+
712
+ lineNumbersWrapper = document.createElement('span');
713
+ lineNumbersWrapper.className = 'line-numbers-rows';
714
+ lineNumbersWrapper.innerHTML = lines;
715
+
716
+ if (pre.hasAttribute('data-start')) {
717
+ pre.style.counterReset = 'linenumber ' + (parseInt(pre.getAttribute('data-start'), 10) - 1);
718
+ }
719
+
720
+ env.element.appendChild(lineNumbersWrapper);
721
+
722
+ });
723
+
724
+ }());