kss-rails 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,4 +4,9 @@
4
4
 
5
5
  # 1.0.1
6
6
 
7
- * Added kss/application layout to the KSS::ApplicationController
7
+ * Added kss/application layout to the KSS::ApplicationController
8
+
9
+ # 1.0.2
10
+
11
+ * Highlight source code blocks (jankeesvw)
12
+ * Raise an error suggesting to find missing section if not found (jankeesvw)
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in kss-server.gemspec
4
3
  gemspec
@@ -1,7 +1,7 @@
1
1
  # This class scans your stylesheets for pseudo classes, then inserts a new CSS
2
2
  # rule with the same properties, but named 'psuedo-class-{{name}}'.
3
3
  #
4
- # Supported pseudo classes: hover, disabled.
4
+ # Supported pseudo classes: hover, disabled, active, visited.
5
5
  #
6
6
  # Example:
7
7
  #
@@ -9,16 +9,16 @@
9
9
  # => a.pseudo-class-hover{ color:blue; }
10
10
  class KssStateGenerator
11
11
  constructor: ->
12
- hover = /:hover/
13
- disabled = /:disabled/
14
- active = /:active/
12
+ pseudos = /(\:hover|\:disabled|\:active|\:visited)/g
15
13
 
16
14
  try
17
15
  for stylesheet in document.styleSheets
18
16
  idxs = []
19
17
  for rule, idx in stylesheet.cssRules
20
- if rule.type is CSSRule.STYLE_RULE and (hover.test(rule.selectorText) or disabled.test(rule.selectorText) or active.test(rule.selectorText))
21
- @insertRule(rule.cssText.replace(':', '.pseudo-class-'))
18
+ if (rule.type == CSSRule.STYLE_RULE) && pseudos.test(rule.selectorText)
19
+ replaceRule = (matched, stuff) ->
20
+ return ".pseudo-class-" + matched.replace(':', '')
21
+ @insertRule(rule.cssText.replace(pseudos, replaceRule))
22
22
 
23
23
  # Takes a given style and attaches it to the current page.
24
24
  #
@@ -0,0 +1,858 @@
1
+ /**
2
+ * Copyright 2012 Craig Campbell
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * Rainbow is a simple code syntax highlighter
17
+ *
18
+ * @preserve @version 1.1.8
19
+ * @url rainbowco.de
20
+ */
21
+ window['Rainbow'] = (function() {
22
+
23
+ /**
24
+ * array of replacements to process at the end
25
+ *
26
+ * @type {Object}
27
+ */
28
+ var replacements = {},
29
+
30
+ /**
31
+ * an array of start and end positions of blocks to be replaced
32
+ *
33
+ * @type {Object}
34
+ */
35
+ replacement_positions = {},
36
+
37
+ /**
38
+ * an array of the language patterns specified for each language
39
+ *
40
+ * @type {Object}
41
+ */
42
+ language_patterns = {},
43
+
44
+ /**
45
+ * an array of languages and whether they should bypass the default patterns
46
+ *
47
+ * @type {Object}
48
+ */
49
+ bypass_defaults = {},
50
+
51
+ /**
52
+ * processing level
53
+ *
54
+ * replacements are stored at this level so if there is a sub block of code
55
+ * (for example php inside of html) it runs at a different level
56
+ *
57
+ * @type {number}
58
+ */
59
+ CURRENT_LEVEL = 0,
60
+
61
+ /**
62
+ * constant used to refer to the default language
63
+ *
64
+ * @type {number}
65
+ */
66
+ DEFAULT_LANGUAGE = 0,
67
+
68
+ /**
69
+ * used as counters so we can selectively call setTimeout
70
+ * after processing a certain number of matches/replacements
71
+ *
72
+ * @type {number}
73
+ */
74
+ match_counter = 0,
75
+
76
+ /**
77
+ * @type {number}
78
+ */
79
+ replacement_counter = 0,
80
+
81
+ /**
82
+ * @type {null|string}
83
+ */
84
+ global_class,
85
+
86
+ /**
87
+ * @type {null|Function}
88
+ */
89
+ onHighlight;
90
+
91
+ /**
92
+ * cross browser get attribute for an element
93
+ *
94
+ * @see http://stackoverflow.com/questions/3755227/cross-browser-javascript-getattribute-method
95
+ *
96
+ * @param {Node} el
97
+ * @param {string} attr attribute you are trying to get
98
+ * @returns {string|number}
99
+ */
100
+ function _attr(el, attr, attrs, i) {
101
+ var result = (el.getAttribute && el.getAttribute(attr)) || 0;
102
+
103
+ if (!result) {
104
+ attrs = el.attributes;
105
+
106
+ for (i = 0; i < attrs.length; ++i) {
107
+ if (attrs[i].nodeName === attr) {
108
+ return attrs[i].nodeValue;
109
+ }
110
+ }
111
+ }
112
+
113
+ return result;
114
+ }
115
+
116
+ /**
117
+ * adds a class to a given code block
118
+ *
119
+ * @param {Element} el
120
+ * @param {string} class_name class name to add
121
+ * @returns void
122
+ */
123
+ function _addClass(el, class_name) {
124
+ el.className += el.className ? ' ' + class_name : class_name;
125
+ }
126
+
127
+ /**
128
+ * checks if a block has a given class
129
+ *
130
+ * @param {Element} el
131
+ * @param {string} class_name class name to check for
132
+ * @returns {boolean}
133
+ */
134
+ function _hasClass(el, class_name) {
135
+ return (' ' + el.className + ' ').indexOf(' ' + class_name + ' ') > -1;
136
+ }
137
+
138
+ /**
139
+ * gets the language for this block of code
140
+ *
141
+ * @param {Element} block
142
+ * @returns {string|null}
143
+ */
144
+ function _getLanguageForBlock(block) {
145
+
146
+ // if this doesn't have a language but the parent does then use that
147
+ // this means if for example you have: <pre data-language="php">
148
+ // with a bunch of <code> blocks inside then you do not have
149
+ // to specify the language for each block
150
+ var language = _attr(block, 'data-language') || _attr(block.parentNode, 'data-language');
151
+
152
+ // this adds support for specifying language via a css class
153
+ // you can use the Google Code Prettify style: <pre class="lang-php">
154
+ // or the HTML5 style: <pre><code class="language-php">
155
+ if (!language) {
156
+ var pattern = /\blang(?:uage)?-(\w+)/,
157
+ match = block.className.match(pattern) || block.parentNode.className.match(pattern);
158
+
159
+ if (match) {
160
+ language = match[1];
161
+ }
162
+ }
163
+
164
+ return language;
165
+ }
166
+
167
+ /**
168
+ * makes sure html entities are always used for tags
169
+ *
170
+ * @param {string} code
171
+ * @returns {string}
172
+ */
173
+ function _htmlEntities(code) {
174
+ return code.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/&(?![\w\#]+;)/g, '&amp;');
175
+ }
176
+
177
+ /**
178
+ * determines if a new match intersects with an existing one
179
+ *
180
+ * @param {number} start1 start position of existing match
181
+ * @param {number} end1 end position of existing match
182
+ * @param {number} start2 start position of new match
183
+ * @param {number} end2 end position of new match
184
+ * @returns {boolean}
185
+ */
186
+ function _intersects(start1, end1, start2, end2) {
187
+ if (start2 >= start1 && start2 < end1) {
188
+ return true;
189
+ }
190
+
191
+ return end2 > start1 && end2 < end1;
192
+ }
193
+
194
+ /**
195
+ * determines if two different matches have complete overlap with each other
196
+ *
197
+ * @param {number} start1 start position of existing match
198
+ * @param {number} end1 end position of existing match
199
+ * @param {number} start2 start position of new match
200
+ * @param {number} end2 end position of new match
201
+ * @returns {boolean}
202
+ */
203
+ function _hasCompleteOverlap(start1, end1, start2, end2) {
204
+
205
+ // if the starting and end positions are exactly the same
206
+ // then the first one should stay and this one should be ignored
207
+ if (start2 == start1 && end2 == end1) {
208
+ return false;
209
+ }
210
+
211
+ return start2 <= start1 && end2 >= end1;
212
+ }
213
+
214
+ /**
215
+ * determines if the match passed in falls inside of an existing match
216
+ * this prevents a regex pattern from matching inside of a bigger pattern
217
+ *
218
+ * @param {number} start - start position of new match
219
+ * @param {number} end - end position of new match
220
+ * @returns {boolean}
221
+ */
222
+ function _matchIsInsideOtherMatch(start, end) {
223
+ for (var key in replacement_positions[CURRENT_LEVEL]) {
224
+ key = parseInt(key, 10);
225
+
226
+ // if this block completely overlaps with another block
227
+ // then we should remove the other block and return false
228
+ if (_hasCompleteOverlap(key, replacement_positions[CURRENT_LEVEL][key], start, end)) {
229
+ delete replacement_positions[CURRENT_LEVEL][key];
230
+ delete replacements[CURRENT_LEVEL][key];
231
+ }
232
+
233
+ if (_intersects(key, replacement_positions[CURRENT_LEVEL][key], start, end)) {
234
+ return true;
235
+ }
236
+ }
237
+
238
+ return false;
239
+ }
240
+
241
+ /**
242
+ * takes a string of code and wraps it in a span tag based on the name
243
+ *
244
+ * @param {string} name name of the pattern (ie keyword.regex)
245
+ * @param {string} code block of code to wrap
246
+ * @returns {string}
247
+ */
248
+ function _wrapCodeInSpan(name, code) {
249
+ return '<span class="' + name.replace(/\./g, ' ') + (global_class ? ' ' + global_class : '') + '">' + code + '</span>';
250
+ }
251
+
252
+ /**
253
+ * finds out the position of group match for a regular expression
254
+ *
255
+ * @see http://stackoverflow.com/questions/1985594/how-to-find-index-of-groups-in-match
256
+ *
257
+ * @param {Object} match
258
+ * @param {number} group_number
259
+ * @returns {number}
260
+ */
261
+ function _indexOfGroup(match, group_number) {
262
+ var index = 0,
263
+ i;
264
+
265
+ for (i = 1; i < group_number; ++i) {
266
+ if (match[i]) {
267
+ index += match[i].length;
268
+ }
269
+ }
270
+
271
+ return index;
272
+ }
273
+
274
+ /**
275
+ * matches a regex pattern against a block of code
276
+ * finds all matches that should be processed and stores the positions
277
+ * of where they should be replaced within the string
278
+ *
279
+ * this is where pretty much all the work is done but it should not
280
+ * be called directly
281
+ *
282
+ * @param {RegExp} pattern
283
+ * @param {string} code
284
+ * @returns void
285
+ */
286
+ function _processPattern(regex, pattern, code, callback)
287
+ {
288
+ var match = regex.exec(code);
289
+
290
+ if (!match) {
291
+ return callback();
292
+ }
293
+
294
+ ++match_counter;
295
+
296
+ // treat match 0 the same way as name
297
+ if (!pattern['name'] && typeof pattern['matches'][0] == 'string') {
298
+ pattern['name'] = pattern['matches'][0];
299
+ delete pattern['matches'][0];
300
+ }
301
+
302
+ var replacement = match[0],
303
+ start_pos = match.index,
304
+ end_pos = match[0].length + start_pos,
305
+
306
+ /**
307
+ * callback to process the next match of this pattern
308
+ */
309
+ processNext = function() {
310
+ var nextCall = function() {
311
+ _processPattern(regex, pattern, code, callback);
312
+ };
313
+
314
+ // every 100 items we process let's call set timeout
315
+ // to let the ui breathe a little
316
+ return match_counter % 100 > 0 ? nextCall() : setTimeout(nextCall, 0);
317
+ };
318
+
319
+ // if this is not a child match and it falls inside of another
320
+ // match that already happened we should skip it and continue processing
321
+ if (_matchIsInsideOtherMatch(start_pos, end_pos)) {
322
+ return processNext();
323
+ }
324
+
325
+ /**
326
+ * callback for when a match was successfully processed
327
+ *
328
+ * @param {string} replacement
329
+ * @returns void
330
+ */
331
+ var onMatchSuccess = function(replacement) {
332
+ // if this match has a name then wrap it in a span tag
333
+ if (pattern['name']) {
334
+ replacement = _wrapCodeInSpan(pattern['name'], replacement);
335
+ }
336
+
337
+ // console.log('LEVEL', CURRENT_LEVEL, 'replace', match[0], 'with', replacement, 'at position', start_pos, 'to', end_pos);
338
+
339
+ // store what needs to be replaced with what at this position
340
+ if (!replacements[CURRENT_LEVEL]) {
341
+ replacements[CURRENT_LEVEL] = {};
342
+ replacement_positions[CURRENT_LEVEL] = {};
343
+ }
344
+
345
+ replacements[CURRENT_LEVEL][start_pos] = {
346
+ 'replace': match[0],
347
+ 'with': replacement
348
+ };
349
+
350
+ // store the range of this match so we can use it for comparisons
351
+ // with other matches later
352
+ replacement_positions[CURRENT_LEVEL][start_pos] = end_pos;
353
+
354
+ // process the next match
355
+ processNext();
356
+ },
357
+
358
+ // if this pattern has sub matches for different groups in the regex
359
+ // then we should process them one at a time by rerunning them through
360
+ // this function to generate the new replacement
361
+ //
362
+ // we run through them backwards because the match position of earlier
363
+ // matches will not change depending on what gets replaced in later
364
+ // matches
365
+ group_keys = keys(pattern['matches']),
366
+
367
+ /**
368
+ * callback for processing a sub group
369
+ *
370
+ * @param {number} i
371
+ * @param {Array} group_keys
372
+ * @param {Function} callback
373
+ */
374
+ processGroup = function(i, group_keys, callback) {
375
+ if (i >= group_keys.length) {
376
+ return callback(replacement);
377
+ }
378
+
379
+ var processNextGroup = function() {
380
+ processGroup(++i, group_keys, callback);
381
+ },
382
+ block = match[group_keys[i]];
383
+
384
+ // if there is no match here then move on
385
+ if (!block) {
386
+ return processNextGroup();
387
+ }
388
+
389
+ var group = pattern['matches'][group_keys[i]],
390
+ language = group['language'],
391
+
392
+ /**
393
+ * process group is what group we should use to actually process
394
+ * this match group
395
+ *
396
+ * for example if the subgroup pattern looks like this
397
+ * 2: {
398
+ * 'name': 'keyword',
399
+ * 'pattern': /true/g
400
+ * }
401
+ *
402
+ * then we use that as is, but if it looks like this
403
+ *
404
+ * 2: {
405
+ * 'name': 'keyword',
406
+ * 'matches': {
407
+ * 'name': 'special',
408
+ * 'pattern': /whatever/g
409
+ * }
410
+ * }
411
+ *
412
+ * we treat the 'matches' part as the pattern and keep
413
+ * the name around to wrap it with later
414
+ */
415
+ process_group = group['name'] && group['matches'] ? group['matches'] : group,
416
+
417
+ /**
418
+ * takes the code block matched at this group, replaces it
419
+ * with the highlighted block, and optionally wraps it with
420
+ * a span with a name
421
+ *
422
+ * @param {string} block
423
+ * @param {string} replace_block
424
+ * @param {string|null} match_name
425
+ */
426
+ _replaceAndContinue = function(block, replace_block, match_name) {
427
+ replacement = _replaceAtPosition(_indexOfGroup(match, group_keys[i]), block, match_name ? _wrapCodeInSpan(match_name, replace_block) : replace_block, replacement);
428
+ processNextGroup();
429
+ };
430
+
431
+ // if this is a sublanguage go and process the block using that language
432
+ if (language) {
433
+ return _highlightBlockForLanguage(block, language, function(code) {
434
+ _replaceAndContinue(block, code);
435
+ });
436
+ }
437
+
438
+ // if this is a string then this match is directly mapped to selector
439
+ // so all we have to do is wrap it in a span and continue
440
+ if (typeof group === 'string') {
441
+ return _replaceAndContinue(block, block, group);
442
+ }
443
+
444
+ // the process group can be a single pattern or an array of patterns
445
+ // _processCodeWithPatterns always expects an array so we convert it here
446
+ _processCodeWithPatterns(block, process_group.length ? process_group : [process_group], function(code) {
447
+ _replaceAndContinue(block, code, group['matches'] ? group['name'] : 0);
448
+ });
449
+ };
450
+
451
+ processGroup(0, group_keys, onMatchSuccess);
452
+ }
453
+
454
+ /**
455
+ * should a language bypass the default patterns?
456
+ *
457
+ * if you call Rainbow.extend() and pass true as the third argument
458
+ * it will bypass the defaults
459
+ */
460
+ function _bypassDefaultPatterns(language)
461
+ {
462
+ return bypass_defaults[language];
463
+ }
464
+
465
+ /**
466
+ * returns a list of regex patterns for this language
467
+ *
468
+ * @param {string} language
469
+ * @returns {Array}
470
+ */
471
+ function _getPatternsForLanguage(language) {
472
+ var patterns = language_patterns[language] || [],
473
+ default_patterns = language_patterns[DEFAULT_LANGUAGE] || [];
474
+
475
+ return _bypassDefaultPatterns(language) ? patterns : patterns.concat(default_patterns);
476
+ }
477
+
478
+ /**
479
+ * substring replace call to replace part of a string at a certain position
480
+ *
481
+ * @param {number} position the position where the replacement should happen
482
+ * @param {string} replace the text we want to replace
483
+ * @param {string} replace_with the text we want to replace it with
484
+ * @param {string} code the code we are doing the replacing in
485
+ * @returns {string}
486
+ */
487
+ function _replaceAtPosition(position, replace, replace_with, code) {
488
+ var sub_string = code.substr(position);
489
+ return code.substr(0, position) + sub_string.replace(replace, replace_with);
490
+ }
491
+
492
+ /**
493
+ * sorts an object by index descending
494
+ *
495
+ * @param {Object} object
496
+ * @return {Array}
497
+ */
498
+ function keys(object) {
499
+ var locations = [],
500
+ replacement,
501
+ pos;
502
+
503
+ for(var location in object) {
504
+ if (object.hasOwnProperty(location)) {
505
+ locations.push(location);
506
+ }
507
+ }
508
+
509
+ // numeric descending
510
+ return locations.sort(function(a, b) {
511
+ return b - a;
512
+ });
513
+ }
514
+
515
+ /**
516
+ * processes a block of code using specified patterns
517
+ *
518
+ * @param {string} code
519
+ * @param {Array} patterns
520
+ * @returns void
521
+ */
522
+ function _processCodeWithPatterns(code, patterns, callback)
523
+ {
524
+ // we have to increase the level here so that the
525
+ // replacements will not conflict with each other when
526
+ // processing sub blocks of code
527
+ ++CURRENT_LEVEL;
528
+
529
+ // patterns are processed one at a time through this function
530
+ function _workOnPatterns(patterns, i)
531
+ {
532
+ // still have patterns to process, keep going
533
+ if (i < patterns.length) {
534
+ return _processPattern(patterns[i]['pattern'], patterns[i], code, function() {
535
+ _workOnPatterns(patterns, ++i);
536
+ });
537
+ }
538
+
539
+ // we are done processing the patterns
540
+ // process the replacements and update the DOM
541
+ _processReplacements(code, function(code) {
542
+
543
+ // when we are done processing replacements
544
+ // we are done at this level so we can go back down
545
+ delete replacements[CURRENT_LEVEL];
546
+ delete replacement_positions[CURRENT_LEVEL];
547
+ --CURRENT_LEVEL;
548
+ callback(code);
549
+ });
550
+ }
551
+
552
+ _workOnPatterns(patterns, 0);
553
+ }
554
+
555
+ /**
556
+ * process replacements in the string of code to actually update the markup
557
+ *
558
+ * @param {string} code the code to process replacements in
559
+ * @param {Function} onComplete what to do when we are done processing
560
+ * @returns void
561
+ */
562
+ function _processReplacements(code, onComplete) {
563
+
564
+ /**
565
+ * processes a single replacement
566
+ *
567
+ * @param {string} code
568
+ * @param {Array} positions
569
+ * @param {number} i
570
+ * @param {Function} onComplete
571
+ * @returns void
572
+ */
573
+ function _processReplacement(code, positions, i, onComplete) {
574
+ if (i < positions.length) {
575
+ ++replacement_counter;
576
+ var pos = positions[i],
577
+ replacement = replacements[CURRENT_LEVEL][pos];
578
+ code = _replaceAtPosition(pos, replacement['replace'], replacement['with'], code);
579
+
580
+ // process next function
581
+ var next = function() {
582
+ _processReplacement(code, positions, ++i, onComplete);
583
+ };
584
+
585
+ // use a timeout every 250 to not freeze up the UI
586
+ return replacement_counter % 250 > 0 ? next() : setTimeout(next, 0);
587
+ }
588
+
589
+ onComplete(code);
590
+ }
591
+
592
+ var string_positions = keys(replacements[CURRENT_LEVEL]);
593
+ _processReplacement(code, string_positions, 0, onComplete);
594
+ }
595
+
596
+ /**
597
+ * takes a string of code and highlights it according to the language specified
598
+ *
599
+ * @param {string} code
600
+ * @param {string} language
601
+ * @param {Function} onComplete
602
+ * @returns void
603
+ */
604
+ function _highlightBlockForLanguage(code, language, onComplete) {
605
+ var patterns = _getPatternsForLanguage(language);
606
+ _processCodeWithPatterns(_htmlEntities(code), patterns, onComplete);
607
+ }
608
+
609
+ /**
610
+ * highlight an individual code block
611
+ *
612
+ * @param {Array} code_blocks
613
+ * @param {number} i
614
+ * @returns void
615
+ */
616
+ function _highlightCodeBlock(code_blocks, i, onComplete) {
617
+ if (i < code_blocks.length) {
618
+ var block = code_blocks[i],
619
+ language = _getLanguageForBlock(block);
620
+
621
+ if (!_hasClass(block, 'rainbow') && language) {
622
+ language = language.toLowerCase();
623
+
624
+ _addClass(block, 'rainbow');
625
+
626
+ return _highlightBlockForLanguage(block.innerHTML, language, function(code) {
627
+ block.innerHTML = code;
628
+
629
+ // reset the replacement arrays
630
+ replacements = {};
631
+ replacement_positions = {};
632
+
633
+ // if you have a listener attached tell it that this block is now highlighted
634
+ if (onHighlight) {
635
+ onHighlight(block, language);
636
+ }
637
+
638
+ // process the next block
639
+ setTimeout(function() {
640
+ _highlightCodeBlock(code_blocks, ++i, onComplete);
641
+ }, 0);
642
+ });
643
+ }
644
+ return _highlightCodeBlock(code_blocks, ++i, onComplete);
645
+ }
646
+
647
+ if (onComplete) {
648
+ onComplete();
649
+ }
650
+ }
651
+
652
+ /**
653
+ * start highlighting all the code blocks
654
+ *
655
+ * @returns void
656
+ */
657
+ function _highlight(node, onComplete) {
658
+
659
+ // the first argument can be an Event or a DOM Element
660
+ // I was originally checking instanceof Event but that makes it break
661
+ // when using mootools
662
+ //
663
+ // @see https://github.com/ccampbell/rainbow/issues/32
664
+ //
665
+ node = node && typeof node.getElementsByTagName == 'function' ? node : document;
666
+
667
+ var pre_blocks = node.getElementsByTagName('pre'),
668
+ code_blocks = node.getElementsByTagName('code'),
669
+ i,
670
+ final_blocks = [];
671
+
672
+ // @see http://stackoverflow.com/questions/2735067/how-to-convert-a-dom-node-list-to-an-array-in-javascript
673
+ // we are going to process all <code> blocks
674
+ for (i = 0; i < code_blocks.length; ++i) {
675
+ final_blocks.push(code_blocks[i]);
676
+ }
677
+
678
+ // loop through the pre blocks to see which ones we should add
679
+ for (i = 0; i < pre_blocks.length; ++i) {
680
+
681
+ // if the pre block has no code blocks then process it directly
682
+ if (!pre_blocks[i].getElementsByTagName('code').length) {
683
+ final_blocks.push(pre_blocks[i]);
684
+ }
685
+ }
686
+
687
+ _highlightCodeBlock(final_blocks, 0, onComplete);
688
+ }
689
+
690
+ /**
691
+ * public methods
692
+ */
693
+ return {
694
+
695
+ /**
696
+ * extends the language pattern matches
697
+ *
698
+ * @param {*} language name of language
699
+ * @param {*} patterns array of patterns to add on
700
+ * @param {boolean|null} bypass if true this will bypass the default language patterns
701
+ */
702
+ extend: function(language, patterns, bypass) {
703
+
704
+ // if there is only one argument then we assume that we want to
705
+ // extend the default language rules
706
+ if (arguments.length == 1) {
707
+ patterns = language;
708
+ language = DEFAULT_LANGUAGE;
709
+ }
710
+
711
+ bypass_defaults[language] = bypass;
712
+ language_patterns[language] = patterns.concat(language_patterns[language] || []);
713
+ },
714
+
715
+ /**
716
+ * call back to let you do stuff in your app after a piece of code has been highlighted
717
+ *
718
+ * @param {Function} callback
719
+ */
720
+ onHighlight: function(callback) {
721
+ onHighlight = callback;
722
+ },
723
+
724
+ /**
725
+ * method to set a global class that will be applied to all spans
726
+ *
727
+ * @param {string} class_name
728
+ */
729
+ addClass: function(class_name) {
730
+ global_class = class_name;
731
+ },
732
+
733
+ /**
734
+ * starts the magic rainbow
735
+ *
736
+ * @returns void
737
+ */
738
+ color: function() {
739
+
740
+ // if you want to straight up highlight a string you can pass the string of code,
741
+ // the language, and a callback function
742
+ if (typeof arguments[0] == 'string') {
743
+ return _highlightBlockForLanguage(arguments[0], arguments[1], arguments[2]);
744
+ }
745
+
746
+ // if you pass a callback function then we rerun the color function
747
+ // on all the code and call the callback function on complete
748
+ if (typeof arguments[0] == 'function') {
749
+ return _highlight(0, arguments[0]);
750
+ }
751
+
752
+ // otherwise we use whatever node you passed in with an optional
753
+ // callback function as the second parameter
754
+ _highlight(arguments[0], arguments[1]);
755
+ }
756
+ };
757
+ }) ();
758
+
759
+ /**
760
+ * adds event listener to start highlighting
761
+ */
762
+ (function() {
763
+ if (window.addEventListener) {
764
+ return window.addEventListener('load', Rainbow.color, false);
765
+ }
766
+ window.attachEvent('onload', Rainbow.color);
767
+ }) ();
768
+
769
+ // When using Google closure compiler in advanced mode some methods
770
+ // get renamed. This keeps a public reference to these methods so they can
771
+ // still be referenced from outside this library.
772
+ Rainbow["onHighlight"] = Rainbow.onHighlight;
773
+ Rainbow["addClass"] = Rainbow.addClass;
774
+
775
+ /**
776
+ * HTML patterns
777
+ *
778
+ * @author Craig Campbell
779
+ * @version 1.0.7
780
+ */
781
+ Rainbow.extend('html', [
782
+ {
783
+ 'name': 'source.php.embedded',
784
+ 'matches': {
785
+ 2: {
786
+ 'language': 'php'
787
+ }
788
+ },
789
+ 'pattern': /&lt;\?=?(?!xml)(php)?([\s\S]*?)(\?&gt;)/gm
790
+ },
791
+ {
792
+ 'name': 'source.css.embedded',
793
+ 'matches': {
794
+ 0: {
795
+ 'language': 'css'
796
+ }
797
+ },
798
+ 'pattern': /&lt;style(.*?)&gt;([\s\S]*?)&lt;\/style&gt;/gm
799
+ },
800
+ {
801
+ 'name': 'source.js.embedded',
802
+ 'matches': {
803
+ 0: {
804
+ 'language': 'javascript'
805
+ }
806
+ },
807
+ 'pattern': /&lt;script(?! src)(.*?)&gt;([\s\S]*?)&lt;\/script&gt;/gm
808
+ },
809
+ {
810
+ 'name': 'comment.html',
811
+ 'pattern': /&lt;\!--[\S\s]*?--&gt;/g
812
+ },
813
+ {
814
+ 'matches': {
815
+ 1: 'support.tag.open',
816
+ 2: 'support.tag.close'
817
+ },
818
+ 'pattern': /(&lt;)|(\/?\??&gt;)/g
819
+ },
820
+ {
821
+ 'name': 'support.tag',
822
+ 'matches': {
823
+ 1: 'support.tag',
824
+ 2: 'support.tag.special',
825
+ 3: 'support.tag-name'
826
+ },
827
+ 'pattern': /(&lt;\??)(\/|\!?)(\w+)/g
828
+ },
829
+ {
830
+ 'matches': {
831
+ 1: 'support.attribute'
832
+ },
833
+ 'pattern': /([a-z-]+)(?=\=)/gi
834
+ },
835
+ {
836
+ 'matches': {
837
+ 1: 'support.operator',
838
+ 2: 'string.quote',
839
+ 3: 'string.value',
840
+ 4: 'string.quote'
841
+ },
842
+ 'pattern': /(=)('|")(.*?)(\2)/g
843
+ },
844
+ {
845
+ 'matches': {
846
+ 1: 'support.operator',
847
+ 2: 'support.value'
848
+ },
849
+ 'pattern': /(=)([a-zA-Z\-0-9]*)\b/g
850
+ },
851
+ {
852
+ 'matches': {
853
+ 1: 'support.attribute'
854
+ },
855
+ 'pattern': /\s(\w+)(?=\s|&gt;)(?![\s\S]*&lt;)/g
856
+ }
857
+ ], true);
858
+
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Blackboard theme
3
+ *
4
+ * Adapted from Domenico Carbotta's TextMate theme of the same name
5
+ *
6
+ * @author Domenico Carbotta
7
+ * @author Craig Campbell
8
+ * @version 1.0.2
9
+ */
10
+ pre {
11
+ background: #0B1022;
12
+ word-wrap: break-word;
13
+ margin: 0px;
14
+ padding: 0px;
15
+ padding: 10px;
16
+ color: #fff;
17
+ font-size: 14px;
18
+ margin-bottom: 20px;
19
+ }
20
+
21
+ pre, code {
22
+ font-family: 'Monaco', courier, monospace;
23
+ }
24
+
25
+ pre .comment {
26
+ color: #727272;
27
+ }
28
+
29
+ pre .constant {
30
+ color: #D8FA3C;
31
+ }
32
+
33
+ pre .storage {
34
+ color: #FBDE2D;
35
+ }
36
+
37
+ pre .string, pre .comment.docstring {
38
+ color: #61CE3C;
39
+ }
40
+
41
+ pre .string.regexp, pre .support.tag.script, pre .support.tag.style {
42
+ color: #fff;
43
+ }
44
+
45
+ pre .keyword, pre .selector {
46
+ color: #FBDE2D;
47
+ }
48
+
49
+ pre .inherited-class {
50
+ font-style: italic;
51
+ }
52
+
53
+ pre .entity {
54
+ color: #FF6400;
55
+ }
56
+
57
+ pre .support, *[data-language="c"] .function.call {
58
+ color: #8DA6CE;
59
+ }
60
+
61
+ pre .variable.global, pre .variable.class, pre .variable.instance {
62
+ color: #FF6400;
63
+ }
@@ -1,39 +1,8 @@
1
- /*----------------------------------------------------------------------------
2
- @group Global Reset
3
- ----------------------------------------------------------------------------*/
4
- * {
5
- padding:0;
6
- margin:0;
7
- }
8
- h1, h2, h3, h4, h5, h6, p, pre, blockquote, label, ul, ol, dl, fieldset, address { margin:1em 0; }
9
- li, dd { margin-left:5%; }
10
- fieldset { padding: .5em; }
11
- select option{ padding:0 5px; }
12
-
13
- .access{ display:none; } /* For accessibility related elements */
14
- .clear{ clear:both; height:0px; font-size:0px; line-height:0px; overflow:hidden; }
15
- a{ outline:none; }
16
- a img{ border:none; }
17
-
18
- .clearfix:after {
19
- content: ".";
20
- display: block;
21
- height: 0;
22
- clear: both;
23
- visibility: hidden;
1
+ .kss-body {
2
+ margin: 0;
24
3
  }
25
- * html .clearfix {height: 1%;}
26
- .clearfix {display:inline-block;}
27
- .clearfix {display: block;}
28
-
29
- /* @end */
30
4
 
31
- body{
32
- font-family:Helvetica, Arial, sans-serif;
33
- font-size:14px;
34
- }
35
-
36
- header{
5
+ .kss-header{
37
6
  padding:10px;
38
7
 
39
8
  font-size:16px;
@@ -43,24 +12,24 @@ header{
43
12
  border-bottom:1px solid #ddd;
44
13
  }
45
14
 
46
- #wrapper{
15
+ #kss_wrapper{
47
16
  padding-left:200px;
48
17
  padding-right: 25px;
49
18
  }
50
19
 
51
- nav[role=main]{
20
+ nav.kss-nav{
52
21
  float:left;
53
22
  margin-left:-200px;
54
23
  width:160px;
55
24
  }
56
- nav ul{
25
+ nav.kss-nav>ul{
57
26
  margin-left:10px;
58
27
  }
59
- nav li{
28
+ nav.kss-nav>ul>li{
60
29
  list-style-type:none;
61
30
  margin:10px 0;
62
31
  }
63
- nav li a{
32
+ nav.kss-nav>ul>li>a{
64
33
  text-decoration:none;
65
34
  color:#666;
66
35
  }
@@ -137,6 +106,4 @@ h1.styleguide {
137
106
  font-weight: normal;
138
107
  color: #222; }
139
108
 
140
- /* @end */
141
-
142
- //= require application
109
+ /* @end */
@@ -7,6 +7,11 @@ module Kss
7
7
  raise ArgumentError, "Missing block" unless block_given?
8
8
 
9
9
  @section = styleguide.section(section)
10
+
11
+ if !@section.raw
12
+ raise "KSS styleguide section is nil, is section '#{section}' defined in your css?"
13
+ end
14
+
10
15
  content = capture(&block)
11
16
  render 'kss/shared/styleguide_block', :section => @section, :example_html => content
12
17
  end
@@ -1,2 +1,2 @@
1
1
  <h1>Welcome!</h1>
2
- <p>This is an example <a href="/styleguide">styleguide</a>. To customize this page, create a file at <code>app/views/kss/home/index.html.erb</code>.</p>
2
+ <p>This is an example <a href="./styleguide">styleguide</a>. To customize this page, create a file at <code>app/views/kss/home/index.html.erb</code>.</p>
@@ -13,10 +13,12 @@
13
13
  <div class="styleguide-element">
14
14
  <%= example_html.html_safe %>
15
15
  </div>
16
+ <pre><code data-language="html"><%= example_html.strip %></code></pre>
16
17
  <% section.modifiers.each do |modifier| %>
17
18
  <div class="styleguide-element styleguide-modifier">
18
19
  <span class="styleguide-modifier-name"><%= modifier.name %></span>
19
20
  <%= example_html.gsub('$modifier_class', modifier.class_name).html_safe %>
20
21
  </div>
22
+ <pre><code data-language="html"><%= example_html.gsub('$modifier_class', modifier.class_name).strip %></code></pre>
21
23
  <% end %>
22
24
  </div>
@@ -1,17 +1,17 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  Gem::Specification.new do |gem|
3
- gem.authors = ["Garrett Bjerkhoel"]
4
- gem.email = ["me@garrettbjerkhoel.com"]
3
+ gem.authors = ['Garrett Bjerkhoel']
4
+ gem.email = ['me@garrettbjerkhoel.com']
5
5
  gem.description = %q{Rails 3 engine to provide a living styleguide from Kyle Neath's KSS.}
6
6
  gem.summary = %q{Rails 3 engine to provide a living styleguide from Kyle Neath's KSS.}
7
- gem.homepage = "https://github.com/dewski/kss-rails"
7
+ gem.homepage = 'https://github.com/dewski/kss-rails'
8
8
 
9
9
  gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
10
10
  gem.files = `git ls-files`.split("\n")
11
11
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
- gem.name = "kss-rails"
13
- gem.require_paths = ["lib"]
14
- gem.version = '1.0.1'
12
+ gem.name = 'kss-rails'
13
+ gem.require_paths = ['lib']
14
+ gem.version = '1.0.2'
15
15
 
16
16
  gem.add_dependency 'kss'
17
17
  gem.add_dependency 'rails', '>= 3.0.0'
@@ -5,15 +5,16 @@
5
5
  <title>KSS Styleguide</title>
6
6
  <%= stylesheet_link_tag 'kss' %>
7
7
  <%= stylesheet_link_tag 'application' %>
8
+ <%= stylesheet_link_tag 'blackboard' %>
8
9
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
10
  </head>
10
- <body>
11
- <header>
11
+ <body class="kss-body">
12
+ <header class="kss-header">
12
13
  KSS Styleguide Example
13
14
  </header>
14
15
 
15
- <div id="wrapper">
16
- <nav role="main">
16
+ <div id="kss_wrapper">
17
+ <nav role="main" class="kss-nav">
17
18
  <ul>
18
19
  <li><%= link_to 'Home', kss.root_path %></li>
19
20
  <li><%= link_to 'Styleguide', kss.styleguide_path %></li>
@@ -24,5 +25,6 @@
24
25
  </div><!-- /#wrapper -->
25
26
 
26
27
  <%= javascript_include_tag 'kss' %>
28
+ <%= javascript_include_tag 'rainbow' %>
27
29
  </body>
28
30
  </html>
@@ -1,12 +1,3 @@
1
1
  <%= styleguide_block '1.1' do -%>
2
2
  <button class="$modifier_class">Example Button</button>
3
- <% end -%>
4
- <p>This block above was created with a simple template call:</p>
5
- <pre><code>&lt;%= styleguide_block &#x27;1.1&#x27; do %&gt;
6
- &lt;button class=&quot;$modifier_class&quot;&gt;Example Button&lt;/button&gt;
7
- &lt;% end %&gt;</code></pre>
8
- <p>
9
- Take a look at the source code of this Rails engine for more details. The goal
10
- is to remove the pain from creating a styleguide — document your CSS, have
11
- example HTML in your templates and automate as much as possible.
12
- </p>
3
+ <% end -%>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kss-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-18 00:00:00.000000000Z
12
+ date: 2013-02-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: kss
16
- requirement: &70160073644480 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70160073644480
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rails
27
- requirement: &70160073643780 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,7 +37,12 @@ dependencies:
32
37
  version: 3.0.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70160073643780
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
36
46
  description: Rails 3 engine to provide a living styleguide from Kyle Neath's KSS.
37
47
  email:
38
48
  - me@garrettbjerkhoel.com
@@ -47,6 +57,8 @@ files:
47
57
  - README.md
48
58
  - Rakefile
49
59
  - app/assets/javascripts/kss.coffee
60
+ - app/assets/javascripts/rainbow.js
61
+ - app/assets/stylesheets/blackboard.css
50
62
  - app/assets/stylesheets/kss.css.scss
51
63
  - app/controllers/kss/application_controller.rb
52
64
  - app/controllers/kss/home_controller.rb
@@ -81,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
93
  version: '0'
82
94
  requirements: []
83
95
  rubyforge_project:
84
- rubygems_version: 1.8.10
96
+ rubygems_version: 1.8.23
85
97
  signing_key:
86
98
  specification_version: 3
87
99
  summary: Rails 3 engine to provide a living styleguide from Kyle Neath's KSS.