torba 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +1 -1
  4. data/lib/torba/remote_sources/npm.rb +7 -1
  5. data/torba.gemspec +2 -3
  6. metadata +4 -72
  7. data/test/acceptance-cli/open_test.rb +0 -101
  8. data/test/acceptance-cli/pack_test.rb +0 -49
  9. data/test/acceptance-cli/show_test.rb +0 -61
  10. data/test/acceptance-cli/verify_test.rb +0 -25
  11. data/test/css_url_to_erb_asset_path_test.rb +0 -139
  12. data/test/fixtures/home_path/01/trumbowyg/icons-2x.png +0 -0
  13. data/test/fixtures/home_path/01/trumbowyg/icons.png +0 -0
  14. data/test/fixtures/home_path/01/trumbowyg/trumbowyg.css.erb +0 -471
  15. data/test/fixtures/home_path/01/trumbowyg/trumbowyg.js +0 -1124
  16. data/test/fixtures/home_path/02/lo_dash/lodash.js +0 -2793
  17. data/test/fixtures/home_path/03/bourbon/_border-image.scss +0 -59
  18. data/test/fixtures/home_path/03/bourbon/_font-source-declaration.scss +0 -43
  19. data/test/fixtures/home_path/03/bourbon/_retina-image.scss +0 -25
  20. data/test/fixtures/torbafiles/01_gh_release.rb +0 -8
  21. data/test/fixtures/torbafiles/01_image_asset_not_specified.rb +0 -7
  22. data/test/fixtures/torbafiles/01_targz.rb +0 -7
  23. data/test/fixtures/torbafiles/01_zip.rb +0 -7
  24. data/test/fixtures/torbafiles/02_npm.rb +0 -1
  25. data/test/fixtures/torbafiles/03_not_existed_assets.rb +0 -5
  26. data/test/fixtures/torbafiles/04_similar_names.rb +0 -2
  27. data/test/import_list_test.rb +0 -53
  28. data/test/manifest_test.rb +0 -142
  29. data/test/package/import_list_test.rb +0 -123
  30. data/test/package/logical_paths_test.rb +0 -28
  31. data/test/package_test.rb +0 -52
  32. data/test/rake_task_test.rb +0 -69
  33. data/test/remote_sources/common_test.rb +0 -62
  34. data/test/remote_sources/get_file_test.rb +0 -12
  35. data/test/remote_sources/github_release_test.rb +0 -42
  36. data/test/remote_sources/npm_test.rb +0 -34
  37. data/test/remote_sources/targz_test.rb +0 -29
  38. data/test/remote_sources/zip_test.rb +0 -26
  39. data/test/test_helper.rb +0 -109
  40. data/test/torba_test.rb +0 -13
@@ -1,1124 +0,0 @@
1
- /**
2
- * Trumbowyg v1.1.5 - A lightweight WYSIWYG editor
3
- * Trumbowyg core file
4
- * ------------------------
5
- * @link http://alex-d.github.io/Trumbowyg
6
- * @license MIT
7
- * @author Alexandre Demode (Alex-D)
8
- * Twitter : @AlexandreDemode
9
- * Website : alex-d.fr
10
- */
11
-
12
- jQuery.trumbowyg = {
13
- langs: {
14
- en: {
15
- viewHTML: "View HTML",
16
-
17
- formatting: "Formatting",
18
- p: "Paragraph",
19
- blockquote: "Quote",
20
- code: "Code",
21
- header: "Header",
22
-
23
- bold: "Bold",
24
- italic: "Italic",
25
- strikethrough: "Stroke",
26
- underline: "Underline",
27
-
28
- strong: "Strong",
29
- em: "Emphasis",
30
- del: "Deleted",
31
-
32
- unorderedList: "Unordered list",
33
- orderedList: "Ordered list",
34
-
35
- insertImage: "Insert Image",
36
- insertVideo: "Insert Video",
37
- link: "Link",
38
- createLink: "Insert link",
39
- unlink: "Remove link",
40
-
41
- justifyLeft: "Align Left",
42
- justifyCenter: "Align Center",
43
- justifyRight: "Align Right",
44
- justifyFull: "Align Justify",
45
-
46
- horizontalRule: "Insert horizontal rule",
47
-
48
- fullscreen: "fullscreen",
49
-
50
- close: "Close",
51
-
52
- submit: "Confirm",
53
- reset: "Cancel",
54
-
55
- invalidUrl: "Invalid URL",
56
- required: "Required",
57
- description: "Description",
58
- title: "Title",
59
- text: "Text"
60
- }
61
- },
62
-
63
- // User default options
64
- opts: {},
65
-
66
- btnsGrps: {
67
- design: ['bold', 'italic', 'underline', 'strikethrough'],
68
- semantic: ['strong', 'em', 'del'],
69
- justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
70
- lists: ['unorderedList', 'orderedList']
71
- }
72
- };
73
-
74
-
75
-
76
- (function(window, document, $, undefined){
77
- 'use strict';
78
-
79
- // @param : o are options
80
- // @param : p are params
81
- $.fn.trumbowyg = function(o, p){
82
- if(o === Object(o) || !o){
83
- return this.each(function(){
84
- if(!$(this).data('trumbowyg'))
85
- $(this).data('trumbowyg', new Trumbowyg(this, o));
86
- });
87
- } else if(this.length === 1){
88
- try {
89
- var t = $(this).data('trumbowyg');
90
- switch(o){
91
- // Modal box
92
- case 'openModal':
93
- return t.openModal(p.title, p.content);
94
- case 'closeModal':
95
- return t.closeModal();
96
- case 'openModalInsert':
97
- return t.openModalInsert(p.title, p.fields, p.callback);
98
-
99
- // Selection
100
- case 'saveSelection':
101
- return t.saveSelection();
102
- case 'getSelection':
103
- return t.selection;
104
- case 'getSelectedText':
105
- return t.selection+'';
106
- case 'restoreSelection':
107
- return t.restoreSelection();
108
-
109
- // Destroy
110
- case 'destroy':
111
- return t.destroy();
112
-
113
- // Empty
114
- case 'empty':
115
- return t.empty();
116
-
117
- // Public options
118
- case 'lang':
119
- return t.lang;
120
- case 'duration':
121
- return t.o.duration;
122
-
123
- // HTML
124
- case 'html':
125
- return t.html(p);
126
- }
127
- } catch(e){}
128
- }
129
-
130
- return false;
131
- };
132
-
133
- var Trumbowyg = function(editorElem, opts){
134
- var t = this;
135
- // Get the document of the element. It use to makes the plugin
136
- // compatible on iframes.
137
- t.doc = editorElem.ownerDocument || document;
138
- // jQuery object of the editor
139
- t.$e = $(editorElem);
140
- t.$creator = $(editorElem);
141
-
142
- // Extend with options
143
- opts = $.extend(true, {}, opts, $.trumbowyg.opts);
144
-
145
- // Localization management
146
- if(typeof opts.lang === 'undefined' || typeof $.trumbowyg.langs[opts.lang] === 'undefined')
147
- t.lang = $.trumbowyg.langs.en;
148
- else
149
- t.lang = $.extend(true, {}, $.trumbowyg.langs.en, $.trumbowyg.langs[opts.lang]);
150
-
151
- // Defaults Options
152
- t.o = $.extend(true, {}, {
153
- lang: 'en',
154
- dir: 'ltr',
155
- duration: 200, // Duration of modal box animations
156
-
157
- mobile: false,
158
- tablet: true,
159
- closable: false,
160
- fullscreenable: true,
161
- fixedBtnPane: false,
162
- fixedFullWidth: false,
163
- autogrow: false,
164
-
165
- prefix: 'trumbowyg-',
166
-
167
- // WYSIWYG only
168
- convertLink: true, // TODO
169
- semantic: false,
170
- resetCss: false,
171
-
172
- btns: [
173
- 'viewHTML',
174
- '|', 'formatting',
175
- '|', $.trumbowyg.btnsGrps.design,
176
- '|', 'link',
177
- '|', 'insertImage',
178
- '|', $.trumbowyg.btnsGrps.justify,
179
- '|', $.trumbowyg.btnsGrps.lists,
180
- '|', 'horizontalRule'
181
- ],
182
- btnsAdd: [],
183
-
184
- /**
185
- * When the button is associated to a empty object
186
- * func and title attributs are defined from the button key value
187
- *
188
- * For example
189
- * foo: {}
190
- * is equivalent to :
191
- * foo: {
192
- * func: 'foo',
193
- * title: this.lang.foo
194
- * }
195
- */
196
- btnsDef: {
197
- viewHTML: {
198
- func: 'toggle'
199
- },
200
-
201
- p: {
202
- func: 'formatBlock'
203
- },
204
- blockquote: {
205
- func: 'formatBlock'
206
- },
207
- h1: {
208
- func: 'formatBlock',
209
- title: t.lang.header + ' 1'
210
- },
211
- h2: {
212
- func: 'formatBlock',
213
- title: t.lang.header + ' 2'
214
- },
215
- h3: {
216
- func: 'formatBlock',
217
- title: t.lang.header + ' 3'
218
- },
219
- h4: {
220
- func: 'formatBlock',
221
- title: t.lang.header + ' 4'
222
- },
223
-
224
- bold: {},
225
- italic: {},
226
- underline: {},
227
- strikethrough: {},
228
-
229
- strong: {
230
- func: 'bold'
231
- },
232
- em: {
233
- func: 'italic'
234
- },
235
- del: {
236
- func: 'strikethrough'
237
- },
238
-
239
- createLink: {},
240
- unlink: {},
241
-
242
- insertImage: {},
243
-
244
- justifyLeft: {},
245
- justifyCenter: {},
246
- justifyRight: {},
247
- justifyFull: {},
248
-
249
- unorderedList: {
250
- func: 'insertUnorderedList'
251
- },
252
- orderedList: {
253
- func: 'insertOrderedList'
254
- },
255
-
256
- horizontalRule: {
257
- func: 'insertHorizontalRule'
258
- },
259
-
260
- // Dropdowns
261
- formatting: {
262
- dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4']
263
- },
264
- link: {
265
- dropdown: ['createLink', 'unlink']
266
- }
267
- }
268
- }, opts);
269
-
270
- if(t.o.semantic && !opts.btns)
271
- t.o.btns = [
272
- 'viewHTML',
273
- '|', 'formatting',
274
- '|', $.trumbowyg.btnsGrps.semantic,
275
- '|', 'link',
276
- '|', 'insertImage',
277
- '|', $.trumbowyg.btnsGrps.justify,
278
- '|', $.trumbowyg.btnsGrps.lists,
279
- '|', 'horizontalRule'
280
- ];
281
- else if(opts && opts.btns)
282
- t.o.btns = opts.btns;
283
-
284
- t.init();
285
- };
286
-
287
- Trumbowyg.prototype = {
288
- init: function(){
289
- var t = this;
290
- t.height = t.$e.css('height');
291
-
292
- if(t.isEnabled()){
293
- t.buildEditor(true);
294
- return;
295
- }
296
-
297
- t.buildEditor();
298
- t.buildBtnPane();
299
-
300
- t.fixedBtnPaneEvents();
301
-
302
- t.buildOverlay();
303
- },
304
-
305
- buildEditor: function(disable){
306
- var t = this,
307
- pfx = t.o.prefix,
308
- html = '';
309
-
310
-
311
- if(disable === true){
312
- if(!t.$e.is('textarea')){
313
- var textarea = t.buildTextarea().val(t.$e.val());
314
- t.$e.hide().after(textarea);
315
- }
316
- return;
317
- }
318
-
319
-
320
- t.$box = $('<div/>', {
321
- class: pfx + 'box ' + pfx + t.o.lang + ' trumbowyg'
322
- });
323
-
324
- t.isTextarea = true;
325
- if(t.$e.is('textarea'))
326
- t.$editor = $('<div/>');
327
- else {
328
- t.$editor = t.$e;
329
- t.$e = t.buildTextarea().val(t.$e.val());
330
- t.isTextarea = false;
331
- }
332
-
333
- if(t.$creator.is('[placeholder]'))
334
- t.$editor.attr('placeholder', t.$creator.attr('placeholder'));
335
-
336
- t.$e.hide()
337
- .addClass(pfx + 'textarea');
338
-
339
-
340
- if(t.isTextarea){
341
- html = t.$e.val();
342
- t.$box.insertAfter(t.$e)
343
- .append(t.$editor)
344
- .append(t.$e);
345
- } else {
346
- html = t.$editor.html();
347
- t.$box.insertAfter(t.$editor)
348
- .append(t.$e)
349
- .append(t.$editor);
350
- t.syncCode();
351
- }
352
-
353
- t.$editor.addClass(pfx + 'editor')
354
- .attr('contenteditable', true)
355
- .attr('dir', t.lang._dir || t.o.dir)
356
- .html(html);
357
-
358
- if(t.o.resetCss)
359
- t.$editor.addClass(pfx + 'reset-css');
360
-
361
- if(!t.o.autogrow){
362
- $.each([t.$editor, t.$e], function(i, $el){
363
- $el.css({
364
- height: t.height,
365
- overflow: 'auto'
366
- });
367
- });
368
- }
369
-
370
- if(t.o.semantic){
371
- t.$editor.html(
372
- t.$editor.html()
373
- .replace('<br>', '</p><p>')
374
- .replace('&nbsp;', '')
375
- );
376
- t.semanticCode();
377
- }
378
-
379
-
380
-
381
- t.$editor
382
- .on('dblclick', 'img', function(e){
383
- var $img = $(this);
384
- t.openModalInsert(t.lang.insertImage, {
385
- url: {
386
- label: 'URL',
387
- value: $img.attr('src'),
388
- required: true
389
- },
390
- alt: {
391
- label: 'description',
392
- value: $img.attr('alt')
393
- }
394
- }, function(v){
395
- $img.attr({
396
- src: v.url,
397
- alt: v.alt
398
- });
399
- });
400
- e.stopPropagation();
401
- })
402
- .on('keyup', function(e){
403
- t.semanticCode(false, e.which === 13);
404
- })
405
- .on('focus', function(){
406
- t.$creator.trigger('tbwfocus');
407
- })
408
- .on('blur', function(){
409
- t.syncCode();
410
- t.$creator.trigger('tbwblur');
411
- });
412
- },
413
-
414
-
415
- // Build the Textarea which contain HTML generated code
416
- buildTextarea: function(){
417
- return $('<textarea/>', {
418
- name: this.$e.attr('id'),
419
- height: this.height
420
- });
421
- },
422
-
423
-
424
- // Build button pane, use o.btns and o.btnsAdd options
425
- buildBtnPane: function(){
426
- var t = this,
427
- pfx = t.o.prefix;
428
-
429
- if(t.o.btns === false)
430
- return;
431
-
432
- t.$btnPane = $('<ul/>', {
433
- class: pfx + 'button-pane'
434
- });
435
-
436
- $.each(t.o.btns.concat(t.o.btnsAdd), function(i, btn){
437
- // Managment of group of buttons
438
- try {
439
- var b = btn.split('btnGrp-');
440
- if(b[1] !== undefined)
441
- btn = $.trumbowyg.btnsGrps[b[1]];
442
- } catch(e){}
443
-
444
- if(!$.isArray(btn))
445
- btn = [btn];
446
-
447
- $.each(btn, function(i, b){
448
- try { // Prevent buildBtn error
449
- var $li = $('<li/>');
450
-
451
- if(b === '|') // It's a separator
452
- $li.addClass(pfx + 'separator');
453
- else if(t.isSupportedBtn(b)) // It's a supported button
454
- $li.append(t.buildBtn(b));
455
-
456
- t.$btnPane.append($li);
457
- } catch(e){}
458
- });
459
- });
460
-
461
- // Build right li for fullscreen and close buttons
462
- var $liRight = $('<li/>', {
463
- class: pfx + 'not-disable ' + pfx + 'buttons-right'
464
- });
465
-
466
- // Add the fullscreen button
467
- if(t.o.fullscreenable)
468
- $liRight.append(
469
- t.buildRightBtn('fullscreen')
470
- .on('click', function(){
471
- var cssClass = pfx + 'fullscreen';
472
- t.$box.toggleClass(cssClass);
473
-
474
- if(t.$box.hasClass(cssClass)){
475
- $('body').css('overflow', 'hidden');
476
- $.each([t.$editor, t.$e], function(){
477
- $(this).css({
478
- height: 'calc(100% - 35px)',
479
- overflow: 'auto'
480
- });
481
- });
482
- t.$btnPane.css('width', '100%');
483
- } else {
484
- $('body').css('overflow', 'auto');
485
- t.$box.removeAttr('style');
486
- if(!t.o.autogrow)
487
- $.each([t.$editor, t.$e], function(){
488
- $(this).css('height', t.height);
489
- });
490
- }
491
- $(window).trigger('scroll');
492
- })
493
- );
494
-
495
- // Build and add close button
496
- if(t.o.closable)
497
- $liRight
498
- .append(
499
- t.buildRightBtn('close')
500
- .on('click', function(){
501
- if(t.$box.hasClass(pfx + 'fullscreen'))
502
- $('body').css('overflow', 'auto');
503
- t.destroy();
504
- })
505
- );
506
-
507
-
508
- // Add right li only if isn't empty
509
- if($liRight.not(':empty'))
510
- t.$btnPane.append($liRight);
511
-
512
- t.$box.prepend(t.$btnPane);
513
- },
514
-
515
-
516
- // Build a button and his action
517
- buildBtn: function(n){ // n is name of the button
518
- var t = this,
519
- pfx = t.o.prefix,
520
- btn = t.o.btnsDef[n],
521
- d = btn.dropdown,
522
- textDef = t.lang[n] || n,
523
-
524
- $btn = $('<button/>', {
525
- type: 'button',
526
- class: pfx + n +'-button' + (btn.ico ? ' '+ pfx + btn.ico +'-button' : ''),
527
- text: btn.text || btn.title || textDef,
528
- title: btn.title || btn.text || textDef,
529
- mousedown: function(e){
530
- if(!d || t.$box.find('.'+n+'-'+pfx + 'dropdown').is(':hidden'))
531
- $('body', t.doc).trigger('mousedown');
532
-
533
- if(t.$btnPane.hasClass(pfx + 'disable') && !$(this).hasClass(pfx + 'active') && !$(this).parent().hasClass(pfx + 'not-disable'))
534
- return false;
535
-
536
- t.execCmd((d ? 'dropdown' : false) || btn.func || n,
537
- btn.param || n);
538
-
539
- e.stopPropagation();
540
- e.preventDefault();
541
- }
542
- });
543
-
544
-
545
-
546
- if(d){
547
- $btn.addClass(pfx + 'open-dropdown');
548
- var c = pfx + 'dropdown',
549
- dd = $('<div/>', { // the dropdown
550
- class: n + '-' + c + ' ' + c + ' ' + pfx + 'fixed-top'
551
- });
552
- $.each(d, function(i, def){
553
- if(t.o.btnsDef[def] && t.isSupportedBtn(def))
554
- dd.append(t.buildSubBtn(def));
555
- });
556
- t.$box.append(dd.hide());
557
- }
558
-
559
- return $btn;
560
- },
561
- // Build a button for dropdown menu
562
- // @param n : name of the subbutton
563
- buildSubBtn: function(n){
564
- var t = this,
565
- btnDef = t.o.btnsDef[n];
566
- return $('<button/>', {
567
- type: 'button',
568
- text: btnDef.text || btnDef.title || t.lang[n] || n,
569
- style: btnDef.style || null,
570
- mousedown: function(e){
571
- $('body', t.doc).trigger('mousedown');
572
-
573
- t.execCmd(btnDef.func || n,
574
- btnDef.param || n);
575
-
576
- e.stopPropagation();
577
- }
578
- });
579
- },
580
- // Build a button for right li
581
- // @param n : name of the right button
582
- buildRightBtn: function(n){
583
- return $('<button/>', {
584
- type: 'button',
585
- class: this.o.prefix + n + '-button',
586
- title: this.lang[n],
587
- text: this.lang[n]
588
- });
589
- },
590
- // Check if button is supported
591
- isSupportedBtn: function(btn){
592
- var def = this.o.btnsDef[btn];
593
- return typeof def.isSupported !== 'function' || def.isSupported();
594
- },
595
-
596
- // Build overlay for modal box
597
- buildOverlay: function(){
598
- var t = this;
599
- t.$overlay = $('<div/>', {
600
- class: t.o.prefix + 'overlay'
601
- }).css({
602
- top: t.$btnPane.outerHeight(),
603
- height: (parseInt(t.$editor.outerHeight()) + 1) + 'px'
604
- }).appendTo(t.$box);
605
- return t.$overlay;
606
- },
607
- showOverlay: function(){
608
- var t = this;
609
- $(window).trigger('scroll');
610
- t.$overlay.fadeIn(t.o.duration);
611
- t.$box.addClass(t.o.prefix + 'box-blur');
612
- },
613
- hideOverlay: function(){
614
- var t = this;
615
- t.$overlay.fadeOut(t.o.duration/4);
616
- t.$box.removeClass(t.o.prefix + 'box-blur');
617
- },
618
-
619
- // Management of fixed button pane
620
- fixedBtnPaneEvents: function(){
621
- var t = this,
622
- ffw = t.o.fixedFullWidth;
623
- if(!t.o.fixedBtnPane)
624
- return;
625
-
626
- t.isFixed = false;
627
-
628
- $(window)
629
- .on('scroll resize', function(){
630
- if(!t.$box)
631
- return;
632
-
633
- t.syncCode();
634
-
635
- var s = $(window).scrollTop(), // s is top scroll
636
- o = t.$box.offset().top + 1, // o is offset
637
- toFixed = (s - o > 0) && ((s - o - parseInt(t.height)) < 0),
638
- bp = t.$btnPane,
639
- mt = bp.css('height'),
640
- oh = bp.outerHeight();
641
-
642
- if(toFixed){
643
- if(!t.isFixed){
644
- t.isFixed = true;
645
- bp.css({
646
- position: 'fixed',
647
- top: 0,
648
- left: ffw ? '0' : 'auto',
649
- zIndex: 7
650
- });
651
- $([t.$editor, t.$e]).css({ marginTop: mt });
652
- }
653
- bp.css({
654
- width: ffw ? '100%' : ((parseInt(t.$box.css('width'))-1) + 'px')
655
- });
656
-
657
- $('.' + t.o.prefix + 'fixed-top', t.$box).css({
658
- position: ffw ? 'fixed' : 'absolute',
659
- top: ffw ? oh : parseInt(oh) + (s - o) + 'px',
660
- zIndex: 15
661
- });
662
- } else if(t.isFixed) {
663
- t.isFixed = false;
664
- bp.removeAttr('style');
665
- $([t.$editor, t.$e]).css({ marginTop: 0 });
666
- $('.' + t.o.prefix + 'fixed-top', t.$box).css({
667
- position: 'absolute',
668
- top: oh
669
- });
670
- }
671
- });
672
- },
673
-
674
-
675
-
676
- // Destroy the editor
677
- destroy: function(){
678
- var t = this,
679
- pfx = t.o.prefix,
680
- h = t.height,
681
- html = t.html();
682
-
683
- if(t.isTextarea)
684
- t.$box.after(
685
- t.$e.css({ height: h })
686
- .val(html)
687
- .removeClass(pfx + 'textarea')
688
- .show()
689
- );
690
- else
691
- t.$box.after(
692
- t.$editor
693
- .css({ height: h })
694
- .removeClass(pfx + 'editor')
695
- .removeAttr('contenteditable')
696
- .html(html)
697
- .show()
698
- );
699
-
700
- t.$box.remove();
701
- t.$creator.removeData('trumbowyg');
702
- },
703
-
704
-
705
-
706
- // Empty the editor
707
- empty: function(){
708
- this.$e.val('');
709
- this.syncCode(true);
710
- },
711
-
712
-
713
-
714
- // Function call when click on viewHTML button
715
- toggle: function(){
716
- var t = this,
717
- pfx = t.o.prefix;
718
- t.semanticCode(false, true);
719
- t.$editor.toggle();
720
- t.$e.toggle();
721
- t.$btnPane.toggleClass(pfx + 'disable');
722
- t.$btnPane.find('.'+pfx + 'viewHTML-button').toggleClass(pfx + 'active');
723
- },
724
-
725
- // Open dropdown when click on a button which open that
726
- dropdown: function(name){
727
- var t = this,
728
- d = t.doc,
729
- pfx = t.o.prefix,
730
- $dropdown = t.$box.find('.'+name+'-'+pfx + 'dropdown'),
731
- $btn = t.$btnPane.find('.'+pfx+name+'-button');
732
-
733
- if($dropdown.is(':hidden')){
734
- var o = $btn.offset().left;
735
- $btn.addClass(pfx + 'active');
736
-
737
- $dropdown.css({
738
- position: 'absolute',
739
- top: t.$btnPane.outerHeight(),
740
- left: (t.o.fixedFullWidth && t.isFixed) ? o+'px' : (o - t.$btnPane.offset().left)+'px'
741
- }).show();
742
-
743
- $(window).trigger('scroll');
744
-
745
- $('body', d).on('mousedown', function(){
746
- $('.' + pfx + 'dropdown', d).hide();
747
- $('.' + pfx + 'active', d).removeClass(pfx + 'active');
748
- $('body', d).off('mousedown');
749
- });
750
- } else
751
- $('body', d).trigger('mousedown');
752
- },
753
-
754
-
755
-
756
-
757
- // HTML Code management
758
- html: function(html){
759
- var t = this;
760
- if(html){
761
- t.$e.val(html);
762
- t.syncCode(true);
763
- return t;
764
- } else
765
- return t.$e.val();
766
- },
767
- syncCode: function(force){
768
- var t = this;
769
- if(!force && t.$editor.is(':visible'))
770
- t.$e.val(t.$editor.html());
771
- else
772
- t.$editor.html(t.$e.val());
773
-
774
- if(t.o.autogrow){
775
- t.height = t.$editor.css('height');
776
- t.$e.css({ height: t.height });
777
- }
778
- },
779
-
780
- // Analyse and update to semantic code
781
- // @param force : force to sync code from textarea
782
- // @param full : wrap text nodes in <p>
783
- semanticCode: function(force, full){
784
- var t = this;
785
- t.syncCode(force);
786
-
787
- if(t.o.semantic){
788
- t.semanticTag('b', 'strong');
789
- t.semanticTag('i', 'em');
790
- t.semanticTag('strike', 'del');
791
-
792
- if(full){
793
- // Wrap text nodes in p
794
- t.$editor.contents()
795
- .filter(function(){
796
- // Only non-empty text nodes
797
- return this.nodeType === 3 && $.trim(this.nodeValue).length > 0;
798
- }).wrap('<p></p>').end()
799
-
800
- // Remove all br
801
- .filter('br').remove();
802
-
803
- t.saveSelection();
804
- t.semanticTag('div', 'p');
805
- t.restoreSelection();
806
- }
807
-
808
- t.$e.val(t.$editor.html());
809
- }
810
- },
811
- semanticTag: function(oldTag, newTag){
812
- $(oldTag, this.$editor).each(function(){
813
- $(this).replaceWith(function(){
814
- return '<'+newTag+'>' + $(this).html() + '</'+newTag+'>';
815
- });
816
- });
817
- },
818
-
819
-
820
- // Function call when user click on "Insert Link"
821
- createLink: function(){
822
- var t = this;
823
- t.saveSelection();
824
- t.openModalInsert(t.lang.createLink, {
825
- url: {
826
- label: 'URL',
827
- value: 'http://',
828
- required: true
829
- },
830
- title: {
831
- label: t.lang.title,
832
- value: t.selection
833
- },
834
- text: {
835
- label: t.lang.text,
836
- value: t.selection
837
- }
838
- }, function(v){ // v is value
839
- t.execCmd('createLink', v.url);
840
- var l = $('a[href="'+v.url+'"]:not([title])', t.$box);
841
- if(v.text.length > 0)
842
- l.text(v.text);
843
-
844
- if(v.title.length > 0)
845
- l.attr('title', v.title);
846
-
847
- return true;
848
- });
849
- },
850
- insertImage: function(){
851
- var t = this;
852
- t.saveSelection();
853
- t.openModalInsert(t.lang.insertImage, {
854
- url: {
855
- label: 'URL',
856
- value: 'http://',
857
- required: true
858
- },
859
- alt: {
860
- label: t.lang.description,
861
- value: t.selection
862
- }
863
- }, function(v){ // v are values
864
- t.execCmd('insertImage', v.url);
865
- $('img[src="'+v.url+'"]:not([alt])', t.$box).attr('alt', v.alt);
866
- return true;
867
- });
868
- },
869
-
870
-
871
- /*
872
- * Call method of trumbowyg if exist
873
- * else try to call anonymous function
874
- * and finaly native execCommand
875
- */
876
- execCmd: function(cmd, param){
877
- var t = this;
878
- if(cmd != 'dropdown')
879
- t.$editor.focus();
880
-
881
- try {
882
- t[cmd](param);
883
- } catch(e){
884
- try {
885
- cmd(param, t);
886
- } catch(e2){
887
- //t.$editor.focus();
888
- if(cmd == 'insertHorizontalRule')
889
- param = null;
890
- else if(cmd == 'formatBlock' && (navigator.userAgent.indexOf('MSIE') !== -1 || navigator.appVersion.indexOf('Trident/') > 0))
891
- param = '<' + param + '>';
892
-
893
- t.doc.execCommand(cmd, false, param);
894
- }
895
- }
896
- t.syncCode();
897
- },
898
-
899
-
900
- // Open a modal box
901
- openModal: function(title, content){
902
- var t = this,
903
- pfx = t.o.prefix;
904
-
905
- // No open a modal box when exist other modal box
906
- if($('.' + pfx + 'modal-box', t.$box).size() > 0)
907
- return false;
908
-
909
- t.saveSelection();
910
- t.showOverlay();
911
-
912
- // Disable all btnPane btns
913
- t.$btnPane.addClass(pfx + 'disable');
914
-
915
- // Build out of ModalBox, it's the mask for animations
916
- var $modal = $('<div/>', {
917
- class: pfx + 'modal ' + pfx + 'fixed-top'
918
- }).css({
919
- top: (parseInt(t.$btnPane.css('height')) + 1) + 'px'
920
- }).appendTo(t.$box);
921
-
922
- // Click on overflay close modal by cancelling them
923
- t.$overlay.one('click', function(e){
924
- e.preventDefault();
925
- $modal.trigger(pfx + 'cancel');
926
- });
927
-
928
- // Build the form
929
- var $form = $('<form/>', {
930
- action: '',
931
- html: content
932
- })
933
- .on('submit', function(e){
934
- e.preventDefault();
935
- $modal.trigger(pfx + 'confirm');
936
- })
937
- .on('reset', function(e){
938
- e.preventDefault();
939
- $modal.trigger(pfx + 'cancel');
940
- });
941
-
942
-
943
- // Build ModalBox and animate to show them
944
- var $box = $('<div/>', {
945
- class: pfx + 'modal-box',
946
- html: $form
947
- })
948
- .css({
949
- top: '-' + parseInt(t.$btnPane.outerHeight()) + 'px',
950
- opacity: 0
951
- })
952
- .appendTo($modal)
953
- .animate({
954
- top: 0,
955
- opacity: 1
956
- }, t.o.duration / 2);
957
-
958
-
959
- // Append title
960
- $('<span/>', {
961
- text: title,
962
- class: pfx + 'modal-title'
963
- }).prependTo($box);
964
-
965
-
966
- // Focus in modal box
967
- $box.find('input:first').focus();
968
-
969
-
970
- // Append Confirm and Cancel buttons
971
- t.buildModalBtn('submit', $box);
972
- t.buildModalBtn('reset', $box);
973
-
974
-
975
- $(window).trigger('scroll');
976
-
977
- return $modal;
978
- },
979
- // @param n is name of modal
980
- buildModalBtn: function(n, modal){
981
- var t = this,
982
- pfx = t.o.prefix;
983
-
984
- return $('<button/>', {
985
- class: pfx + 'modal-button ' + pfx + 'modal-' + n,
986
- type: n,
987
- text: t.lang[n] || n
988
- }).appendTo(modal.find('form'));
989
- },
990
- // close current modal box
991
- closeModal: function(){
992
- var t = this,
993
- pfx = t.o.prefix;
994
-
995
- t.$btnPane.removeClass(pfx + 'disable');
996
- t.$overlay.off();
997
-
998
- var $modalBox = $('.' + pfx + 'modal-box', t.$box);
999
-
1000
- $modalBox.animate({
1001
- top: '-' + $modalBox.css('height')
1002
- }, t.o.duration/2, function(){
1003
- $(this).parent().remove();
1004
- t.hideOverlay();
1005
- });
1006
- },
1007
- // Preformated build and management modal
1008
- openModalInsert: function(title, fields, cmd){
1009
- var t = this,
1010
- pfx = t.o.prefix,
1011
- lg = t.lang,
1012
- html = '';
1013
-
1014
- for(var f in fields){
1015
- var fd = fields[f], // field definition
1016
- label = (fd.label === undefined) ? (lg[f] ? lg[f] : f) : (lg[fd.label] ? lg[fd.label] : fd.label);
1017
-
1018
- if(fd.name === undefined)
1019
- fd.name = f;
1020
-
1021
- if(!fd.pattern && f === 'url'){
1022
- fd.pattern = /^(http|https):\/\/([\w~#!:.?+=&%@!\-\/]+)$/;
1023
- fd.patternError = lg.invalidUrl;
1024
- }
1025
-
1026
- html += '<label><input type="'+(fd.type || 'text')+'" name="'+fd.name+'" value="'+(fd.value || '')+'"><span class="'+pfx+'input-infos"><span>'+label+'</span></span></label>';
1027
- }
1028
-
1029
- return t.openModal(title, html)
1030
- .on(pfx + 'confirm', function(){
1031
- var $form = $(this).find('form'),
1032
- valid = true,
1033
- v = {}; // values
1034
-
1035
- for(var f in fields){
1036
- var $field = $('input[name="'+f+'"]', $form);
1037
-
1038
- v[f] = $.trim($field.val());
1039
-
1040
- // Validate value
1041
- if(fields[f].required && v[f] === ''){
1042
- valid = false;
1043
- t.addErrorOnModalField($field, t.lang.required);
1044
- } else if(fields[f].pattern && !fields[f].pattern.test(v[f])){
1045
- valid = false;
1046
- t.addErrorOnModalField($field, fields[f].patternError);
1047
- }
1048
- }
1049
-
1050
- if(valid){
1051
- t.restoreSelection();
1052
-
1053
- if(cmd(v, fields)){
1054
- t.syncCode();
1055
- t.closeModal();
1056
- $(this).off(pfx + 'confirm');
1057
- }
1058
- }
1059
- })
1060
- .one(pfx + 'cancel', function(){
1061
- $(this).off(pfx + 'confirm');
1062
- t.closeModal();
1063
- t.restoreSelection();
1064
- });
1065
- },
1066
- addErrorOnModalField: function($field, err){
1067
- var pfx = this.o.prefix,
1068
- $label = $field.parent();
1069
-
1070
- $field.on('change keyup', function(){
1071
- $label.removeClass(pfx + 'input-error');
1072
- });
1073
- $label
1074
- .addClass(pfx + 'input-error')
1075
- .find('input+span').append(
1076
- $('<span/>', {
1077
- class: pfx +'msg-error',
1078
- text: err
1079
- })
1080
- );
1081
- },
1082
-
1083
-
1084
-
1085
-
1086
- // Selection management
1087
- saveSelection: function(){
1088
- var t = this,
1089
- d = t.doc;
1090
-
1091
- t.selection = null;
1092
- if(window.getSelection){
1093
- var s = window.getSelection();
1094
- if(s.getRangeAt && s.rangeCount)
1095
- t.selection = s.getRangeAt(0);
1096
- } else if(d.selection && d.selection.createRange)
1097
- t.selection = d.selection.createRange();
1098
- },
1099
- restoreSelection: function(){
1100
- var t = this,
1101
- range = t.selection;
1102
-
1103
- if(range){
1104
- if(window.getSelection){
1105
- var s = window.getSelection();
1106
- s.removeAllRanges();
1107
- s.addRange(range);
1108
- } else if(t.doc.selection && range.select)
1109
- range.select();
1110
- }
1111
- },
1112
-
1113
-
1114
-
1115
- // Return true if must enable Trumbowyg on this mobile device
1116
- isEnabled: function(){
1117
- var exprTablet = new RegExp("(iPad|webOS)"),
1118
- exprMobile = new RegExp("(iPhone|iPod|Android|BlackBerry|Windows Phone|ZuneWP7)"),
1119
- ua = navigator.userAgent;
1120
-
1121
- return (this.o.tablet === true && exprTablet.test(ua)) || (this.o.mobile === true && exprMobile.test(ua));
1122
- }
1123
- };
1124
- })(window, document, jQuery);