flashgrid-ext 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +1 -0
- data/flashgrid-ext.gemspec +23 -0
- data/lib/flashgrid/ext.rb +8 -0
- data/lib/flashgrid/ext/version.rb +5 -0
- data/vendor/assets/javascripts/animate.js +82 -0
- data/vendor/assets/javascripts/calendar.js +6097 -0
- data/vendor/assets/javascripts/carousel.js +196 -0
- data/vendor/assets/javascripts/editor.js +2580 -0
- data/vendor/assets/javascripts/hover.js +84 -0
- data/vendor/assets/javascripts/inline_editor.js +764 -0
- data/vendor/assets/javascripts/input_mask.js +339 -0
- data/vendor/assets/javascripts/scrollspy.js +144 -0
- data/vendor/assets/javascripts/sort.js +1417 -0
- data/vendor/assets/javascripts/time_ago.js +187 -0
- data/vendor/assets/stylesheets/animate.css.scss +2319 -0
- data/vendor/assets/stylesheets/calendar.css.scss +373 -0
- data/vendor/assets/stylesheets/carousel.css.scss +153 -0
- data/vendor/assets/stylesheets/editor.css.scss +355 -0
- data/vendor/assets/stylesheets/inline_editor.css.scss +353 -0
- data/vendor/assets/stylesheets/panel.css.scss +156 -0
- data/vendor/assets/stylesheets/sort.css.scss +25 -0
- metadata +98 -0
@@ -0,0 +1,196 @@
|
|
1
|
+
+function ($) {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
// CAROUSEL CLASS DEFINITION
|
5
|
+
// =========================
|
6
|
+
|
7
|
+
var Carousel = function (element, options) {
|
8
|
+
this.$element = $(element)
|
9
|
+
this.$indicators = this.$element.find('.carousel-indicators')
|
10
|
+
this.options = options
|
11
|
+
this.paused =
|
12
|
+
this.sliding =
|
13
|
+
this.interval =
|
14
|
+
this.$active =
|
15
|
+
this.$items = null
|
16
|
+
|
17
|
+
this.options.pause == 'hover' && this.$element
|
18
|
+
.on('mouseenter', $.proxy(this.pause, this))
|
19
|
+
.on('mouseleave', $.proxy(this.cycle, this))
|
20
|
+
}
|
21
|
+
|
22
|
+
Carousel.DEFAULTS = {
|
23
|
+
interval: 5000,
|
24
|
+
pause: 'hover',
|
25
|
+
wrap: true
|
26
|
+
}
|
27
|
+
|
28
|
+
Carousel.prototype.cycle = function (e) {
|
29
|
+
e || (this.paused = false)
|
30
|
+
|
31
|
+
this.interval && clearInterval(this.interval)
|
32
|
+
|
33
|
+
this.options.interval
|
34
|
+
&& !this.paused
|
35
|
+
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
|
36
|
+
|
37
|
+
return this
|
38
|
+
}
|
39
|
+
|
40
|
+
Carousel.prototype.getActiveIndex = function () {
|
41
|
+
this.$active = this.$element.find('.item.active')
|
42
|
+
this.$items = this.$active.parent().children()
|
43
|
+
|
44
|
+
return this.$items.index(this.$active)
|
45
|
+
}
|
46
|
+
|
47
|
+
Carousel.prototype.to = function (pos) {
|
48
|
+
var that = this
|
49
|
+
var activeIndex = this.getActiveIndex()
|
50
|
+
|
51
|
+
if (pos > (this.$items.length - 1) || pos < 0) return
|
52
|
+
|
53
|
+
if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
|
54
|
+
if (activeIndex == pos) return this.pause().cycle()
|
55
|
+
|
56
|
+
return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
|
57
|
+
}
|
58
|
+
|
59
|
+
Carousel.prototype.pause = function (e) {
|
60
|
+
e || (this.paused = true)
|
61
|
+
|
62
|
+
if (this.$element.find('.next, .prev').length && $.support.transition) {
|
63
|
+
this.$element.trigger($.support.transition.end)
|
64
|
+
this.cycle(true)
|
65
|
+
}
|
66
|
+
|
67
|
+
this.interval = clearInterval(this.interval)
|
68
|
+
|
69
|
+
return this
|
70
|
+
}
|
71
|
+
|
72
|
+
Carousel.prototype.next = function () {
|
73
|
+
if (this.sliding) return
|
74
|
+
return this.slide('next')
|
75
|
+
}
|
76
|
+
|
77
|
+
Carousel.prototype.prev = function () {
|
78
|
+
if (this.sliding) return
|
79
|
+
return this.slide('prev')
|
80
|
+
}
|
81
|
+
|
82
|
+
Carousel.prototype.slide = function (type, next) {
|
83
|
+
var $active = this.$element.find('.item.active')
|
84
|
+
var $next = next || $active[type]()
|
85
|
+
var isCycling = this.interval
|
86
|
+
var direction = type == 'next' ? 'left' : 'right'
|
87
|
+
var fallback = type == 'next' ? 'first' : 'last'
|
88
|
+
var that = this
|
89
|
+
|
90
|
+
if (!$next.length) {
|
91
|
+
if (!this.options.wrap) return
|
92
|
+
$next = this.$element.find('.item')[fallback]()
|
93
|
+
}
|
94
|
+
|
95
|
+
if ($next.hasClass('active')) return this.sliding = false
|
96
|
+
|
97
|
+
var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
|
98
|
+
this.$element.trigger(e)
|
99
|
+
if (e.isDefaultPrevented()) return
|
100
|
+
|
101
|
+
this.sliding = true
|
102
|
+
|
103
|
+
isCycling && this.pause()
|
104
|
+
|
105
|
+
if (this.$indicators.length) {
|
106
|
+
this.$indicators.find('.active').removeClass('active')
|
107
|
+
this.$element.one('slid.bs.carousel', function () {
|
108
|
+
var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
|
109
|
+
$nextIndicator && $nextIndicator.addClass('active')
|
110
|
+
})
|
111
|
+
}
|
112
|
+
|
113
|
+
if ($.support.transition && this.$element.hasClass('slide')) {
|
114
|
+
$next.addClass(type)
|
115
|
+
$next[0].offsetWidth // force reflow
|
116
|
+
$active.addClass(direction)
|
117
|
+
$next.addClass(direction)
|
118
|
+
$active
|
119
|
+
.one($.support.transition.end, function () {
|
120
|
+
$next.removeClass([type, direction].join(' ')).addClass('active')
|
121
|
+
$active.removeClass(['active', direction].join(' '))
|
122
|
+
that.sliding = false
|
123
|
+
setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
|
124
|
+
})
|
125
|
+
.emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
|
126
|
+
} else {
|
127
|
+
$active.removeClass('active')
|
128
|
+
$next.addClass('active')
|
129
|
+
this.sliding = false
|
130
|
+
this.$element.trigger('slid.bs.carousel')
|
131
|
+
}
|
132
|
+
|
133
|
+
isCycling && this.cycle()
|
134
|
+
|
135
|
+
return this
|
136
|
+
}
|
137
|
+
|
138
|
+
|
139
|
+
// CAROUSEL PLUGIN DEFINITION
|
140
|
+
// ==========================
|
141
|
+
|
142
|
+
var old = $.fn.carousel
|
143
|
+
|
144
|
+
$.fn.carousel = function (option) {
|
145
|
+
return this.each(function () {
|
146
|
+
var $this = $(this)
|
147
|
+
var data = $this.data('bs.carousel')
|
148
|
+
var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
149
|
+
var action = typeof option == 'string' ? option : options.slide
|
150
|
+
|
151
|
+
if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
|
152
|
+
if (typeof option == 'number') data.to(option)
|
153
|
+
else if (action) data[action]()
|
154
|
+
else if (options.interval) data.pause().cycle()
|
155
|
+
})
|
156
|
+
}
|
157
|
+
|
158
|
+
$.fn.carousel.Constructor = Carousel
|
159
|
+
|
160
|
+
|
161
|
+
// CAROUSEL NO CONFLICT
|
162
|
+
// ====================
|
163
|
+
|
164
|
+
$.fn.carousel.noConflict = function () {
|
165
|
+
$.fn.carousel = old
|
166
|
+
return this
|
167
|
+
}
|
168
|
+
|
169
|
+
|
170
|
+
// CAROUSEL DATA-API
|
171
|
+
// =================
|
172
|
+
|
173
|
+
$(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
|
174
|
+
var $this = $(this), href
|
175
|
+
var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|
176
|
+
var options = $.extend({}, $target.data(), $this.data())
|
177
|
+
var slideIndex = $this.attr('data-slide-to')
|
178
|
+
if (slideIndex) options.interval = false
|
179
|
+
|
180
|
+
$target.carousel(options)
|
181
|
+
|
182
|
+
if (slideIndex = $this.attr('data-slide-to')) {
|
183
|
+
$target.data('bs.carousel').to(slideIndex)
|
184
|
+
}
|
185
|
+
|
186
|
+
e.preventDefault()
|
187
|
+
})
|
188
|
+
|
189
|
+
$(window).on('load', function () {
|
190
|
+
$('[data-ride="carousel"]').each(function () {
|
191
|
+
var $carousel = $(this)
|
192
|
+
$carousel.carousel($carousel.data())
|
193
|
+
})
|
194
|
+
})
|
195
|
+
|
196
|
+
}(jQuery);
|
@@ -0,0 +1,2580 @@
|
|
1
|
+
(function (factory) {
|
2
|
+
/* global define */
|
3
|
+
if (typeof define === 'function' && define.amd) {
|
4
|
+
// AMD. Register as an anonymous module.
|
5
|
+
define(['jquery', 'codemirror'], factory);
|
6
|
+
} else {
|
7
|
+
// Browser globals: jQuery, CodeMirror
|
8
|
+
factory(window.jQuery, window.CodeMirror);
|
9
|
+
}
|
10
|
+
}(function ($, CodeMirror) {
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
// Array.prototype.reduce fallback
|
15
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
|
16
|
+
if ('function' !== typeof Array.prototype.reduce) {
|
17
|
+
Array.prototype.reduce = function (callback, optInitialValue) {
|
18
|
+
var idx, value, length = this.length >>> 0, isValueSet = false;
|
19
|
+
if (1 < arguments.length) {
|
20
|
+
value = optInitialValue;
|
21
|
+
isValueSet = true;
|
22
|
+
}
|
23
|
+
for (idx = 0; length > idx; ++idx) {
|
24
|
+
if (this.hasOwnProperty(idx)) {
|
25
|
+
if (isValueSet) {
|
26
|
+
value = callback(value, this[idx], idx, this);
|
27
|
+
} else {
|
28
|
+
value = this[idx];
|
29
|
+
isValueSet = true;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
if (!isValueSet) {
|
34
|
+
throw new TypeError('Reduce of empty array with no initial value');
|
35
|
+
}
|
36
|
+
return value;
|
37
|
+
};
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* object which check platform/agent
|
42
|
+
*/
|
43
|
+
var agent = {
|
44
|
+
bMac: navigator.appVersion.indexOf('Mac') > -1,
|
45
|
+
bMSIE: navigator.userAgent.indexOf('MSIE') > -1,
|
46
|
+
bFF: navigator.userAgent.indexOf('Firefox') > -1,
|
47
|
+
bCodeMirror: !!CodeMirror
|
48
|
+
};
|
49
|
+
|
50
|
+
/**
|
51
|
+
* func utils (for high-order func's arg)
|
52
|
+
*/
|
53
|
+
var func = (function () {
|
54
|
+
var eq = function (elA) {
|
55
|
+
return function (elB) {
|
56
|
+
return elA === elB;
|
57
|
+
};
|
58
|
+
};
|
59
|
+
|
60
|
+
var eq2 = function (elA, elB) {
|
61
|
+
return elA === elB;
|
62
|
+
};
|
63
|
+
|
64
|
+
var fail = function () {
|
65
|
+
return false;
|
66
|
+
};
|
67
|
+
|
68
|
+
var not = function (f) {
|
69
|
+
return function () {
|
70
|
+
return !f.apply(f, arguments);
|
71
|
+
};
|
72
|
+
};
|
73
|
+
|
74
|
+
var self = function (a) {
|
75
|
+
return a;
|
76
|
+
};
|
77
|
+
|
78
|
+
return {
|
79
|
+
eq: eq,
|
80
|
+
eq2: eq2,
|
81
|
+
fail: fail,
|
82
|
+
not: not,
|
83
|
+
self: self
|
84
|
+
};
|
85
|
+
})();
|
86
|
+
|
87
|
+
/**
|
88
|
+
* list utils
|
89
|
+
*/
|
90
|
+
var list = (function () {
|
91
|
+
var head = function (array) { return array[0]; };
|
92
|
+
var last = function (array) { return array[array.length - 1]; };
|
93
|
+
var initial = function (array) { return array.slice(0, array.length - 1); };
|
94
|
+
var tail = function (array) { return array.slice(1); };
|
95
|
+
|
96
|
+
/**
|
97
|
+
* get sum from a list
|
98
|
+
* @param {array} array - array
|
99
|
+
* @param {function} fn - iterator
|
100
|
+
*/
|
101
|
+
var sum = function (array, fn) {
|
102
|
+
fn = fn || func.self;
|
103
|
+
return array.reduce(function (memo, v) {
|
104
|
+
return memo + fn(v);
|
105
|
+
}, 0);
|
106
|
+
};
|
107
|
+
|
108
|
+
/**
|
109
|
+
* returns a copy of the collection with array type.
|
110
|
+
* @param {collection} collection - collection eg) node.childNodes, ...
|
111
|
+
*/
|
112
|
+
var from = function (collection) {
|
113
|
+
var result = [], idx = -1, length = collection.length;
|
114
|
+
while (++idx < length) {
|
115
|
+
result[idx] = collection[idx];
|
116
|
+
}
|
117
|
+
return result;
|
118
|
+
};
|
119
|
+
|
120
|
+
/**
|
121
|
+
* cluster item by second function
|
122
|
+
* @param {array} array - array
|
123
|
+
* @param {function} fn - predicate function for cluster rule
|
124
|
+
*/
|
125
|
+
var clusterBy = function (array, fn) {
|
126
|
+
if (array.length === 0) { return []; }
|
127
|
+
var aTail = tail(array);
|
128
|
+
return aTail.reduce(function (memo, v) {
|
129
|
+
var aLast = last(memo);
|
130
|
+
if (fn(last(aLast), v)) {
|
131
|
+
aLast[aLast.length] = v;
|
132
|
+
} else {
|
133
|
+
memo[memo.length] = [v];
|
134
|
+
}
|
135
|
+
return memo;
|
136
|
+
}, [[head(array)]]);
|
137
|
+
};
|
138
|
+
|
139
|
+
/**
|
140
|
+
* returns a copy of the array with all falsy values removed
|
141
|
+
* @param {array} array - array
|
142
|
+
* @param {function} fn - predicate function for cluster rule
|
143
|
+
*/
|
144
|
+
var compact = function (array) {
|
145
|
+
var aResult = [];
|
146
|
+
for (var idx = 0, sz = array.length; idx < sz; idx ++) {
|
147
|
+
if (array[idx]) { aResult.push(array[idx]); }
|
148
|
+
}
|
149
|
+
return aResult;
|
150
|
+
};
|
151
|
+
|
152
|
+
return { head: head, last: last, initial: initial, tail: tail,
|
153
|
+
sum: sum, from: from, compact: compact, clusterBy: clusterBy };
|
154
|
+
})();
|
155
|
+
|
156
|
+
/**
|
157
|
+
* dom utils
|
158
|
+
*/
|
159
|
+
var dom = (function () {
|
160
|
+
/**
|
161
|
+
* returns predicate which judge whether nodeName is same
|
162
|
+
*/
|
163
|
+
var makePredByNodeName = function (sNodeName) {
|
164
|
+
// nodeName of element is always uppercase.
|
165
|
+
return function (node) {
|
166
|
+
return node && node.nodeName === sNodeName;
|
167
|
+
};
|
168
|
+
};
|
169
|
+
|
170
|
+
var isPara = function (node) {
|
171
|
+
// Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
|
172
|
+
return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName);
|
173
|
+
};
|
174
|
+
|
175
|
+
var isList = function (node) {
|
176
|
+
return node && /^UL|^OL/.test(node.nodeName);
|
177
|
+
};
|
178
|
+
|
179
|
+
/**
|
180
|
+
* returns whether node is `editor-editable` or not.
|
181
|
+
*/
|
182
|
+
var isEditable = function (node) {
|
183
|
+
return node && $(node).hasClass('editor-editable');
|
184
|
+
};
|
185
|
+
|
186
|
+
var isControlSizing = function (node) {
|
187
|
+
return node && $(node).hasClass('editor-control-sizing');
|
188
|
+
};
|
189
|
+
|
190
|
+
/**
|
191
|
+
* find nearest ancestor predicate hit
|
192
|
+
* @param {element} node
|
193
|
+
* @param {function} pred - predicate function
|
194
|
+
*/
|
195
|
+
var ancestor = function (node, pred) {
|
196
|
+
while (node) {
|
197
|
+
if (pred(node)) { return node; }
|
198
|
+
if (isEditable(node)) { break; }
|
199
|
+
|
200
|
+
node = node.parentNode;
|
201
|
+
}
|
202
|
+
return null;
|
203
|
+
};
|
204
|
+
|
205
|
+
/**
|
206
|
+
* returns new array of ancestor nodes (until predicate hit).
|
207
|
+
* @param {element} node
|
208
|
+
* @param {function} [optional] pred - predicate function
|
209
|
+
*/
|
210
|
+
var listAncestor = function (node, pred) {
|
211
|
+
pred = pred || func.fail;
|
212
|
+
|
213
|
+
var aAncestor = [];
|
214
|
+
ancestor(node, function (el) {
|
215
|
+
aAncestor.push(el);
|
216
|
+
return pred(el);
|
217
|
+
});
|
218
|
+
return aAncestor;
|
219
|
+
};
|
220
|
+
|
221
|
+
/**
|
222
|
+
* returns common ancestor node between two nodes.
|
223
|
+
* @param {element} nodeA
|
224
|
+
* @param {element} nodeB
|
225
|
+
*/
|
226
|
+
var commonAncestor = function (nodeA, nodeB) {
|
227
|
+
var aAncestor = listAncestor(nodeA);
|
228
|
+
for (var n = nodeB; n; n = n.parentNode) {
|
229
|
+
if ($.inArray(n, aAncestor) > -1) { return n; }
|
230
|
+
}
|
231
|
+
return null; // difference document area
|
232
|
+
};
|
233
|
+
|
234
|
+
/**
|
235
|
+
* listing all Nodes between two nodes.
|
236
|
+
* FIXME: nodeA and nodeB must be sorted, use comparePoints later.
|
237
|
+
* @param {element} nodeA
|
238
|
+
* @param {element} nodeB
|
239
|
+
*/
|
240
|
+
var listBetween = function (nodeA, nodeB) {
|
241
|
+
var aNode = [];
|
242
|
+
|
243
|
+
var bStart = false, bEnd = false;
|
244
|
+
var fnWalk = function (node) {
|
245
|
+
if (!node) { return; } // traverse fisnish
|
246
|
+
if (node === nodeA) { bStart = true; } // start point
|
247
|
+
if (bStart && !bEnd) { aNode.push(node); } // between
|
248
|
+
if (node === nodeB) { bEnd = true; return; } // end point
|
249
|
+
|
250
|
+
for (var idx = 0, sz = node.childNodes.length; idx < sz; idx++) {
|
251
|
+
fnWalk(node.childNodes[idx]);
|
252
|
+
}
|
253
|
+
};
|
254
|
+
|
255
|
+
fnWalk(commonAncestor(nodeA, nodeB)); // DFS with commonAcestor.
|
256
|
+
return aNode;
|
257
|
+
};
|
258
|
+
|
259
|
+
/**
|
260
|
+
* listing all prevSiblings (until predicate hit).
|
261
|
+
* @param {element} node
|
262
|
+
* @param {function} [optional] pred - predicate function
|
263
|
+
*/
|
264
|
+
var listPrev = function (node, pred) {
|
265
|
+
pred = pred || func.fail;
|
266
|
+
|
267
|
+
var aNext = [];
|
268
|
+
while (node) {
|
269
|
+
aNext.push(node);
|
270
|
+
if (pred(node)) { break; }
|
271
|
+
node = node.previousSibling;
|
272
|
+
}
|
273
|
+
return aNext;
|
274
|
+
};
|
275
|
+
|
276
|
+
/**
|
277
|
+
* listing nextSiblings (until predicate hit).
|
278
|
+
* @param {element} node
|
279
|
+
* @param {function} pred [optional] - predicate function
|
280
|
+
*/
|
281
|
+
var listNext = function (node, pred) {
|
282
|
+
pred = pred || func.fail;
|
283
|
+
|
284
|
+
var aNext = [];
|
285
|
+
while (node) {
|
286
|
+
aNext.push(node);
|
287
|
+
if (pred(node)) { break; }
|
288
|
+
node = node.nextSibling;
|
289
|
+
}
|
290
|
+
return aNext;
|
291
|
+
};
|
292
|
+
|
293
|
+
/**
|
294
|
+
* insert node after preceding
|
295
|
+
* @param {element} node
|
296
|
+
* @param {element} preceding - predicate function
|
297
|
+
*/
|
298
|
+
var insertAfter = function (node, preceding) {
|
299
|
+
var next = preceding.nextSibling, parent = preceding.parentNode;
|
300
|
+
if (next) {
|
301
|
+
parent.insertBefore(node, next);
|
302
|
+
} else {
|
303
|
+
parent.appendChild(node);
|
304
|
+
}
|
305
|
+
return node;
|
306
|
+
};
|
307
|
+
|
308
|
+
/**
|
309
|
+
* append children
|
310
|
+
* @param {element} node
|
311
|
+
* @param {collection} aChild
|
312
|
+
*/
|
313
|
+
var appends = function (node, aChild) {
|
314
|
+
$.each(aChild, function (idx, child) {
|
315
|
+
node.appendChild(child);
|
316
|
+
});
|
317
|
+
return node;
|
318
|
+
};
|
319
|
+
|
320
|
+
var isText = makePredByNodeName('#text');
|
321
|
+
|
322
|
+
/**
|
323
|
+
* returns #text's text size or element's childNodes size
|
324
|
+
* @param {element} node
|
325
|
+
*/
|
326
|
+
var length = function (node) {
|
327
|
+
if (isText(node)) { return node.nodeValue.length; }
|
328
|
+
return node.childNodes.length;
|
329
|
+
};
|
330
|
+
|
331
|
+
/**
|
332
|
+
* returns offset from parent.
|
333
|
+
* @param {element} node
|
334
|
+
*/
|
335
|
+
var position = function (node) {
|
336
|
+
var offset = 0;
|
337
|
+
while ((node = node.previousSibling)) { offset += 1; }
|
338
|
+
return offset;
|
339
|
+
};
|
340
|
+
|
341
|
+
/**
|
342
|
+
* return offsetPath(array of offset) from ancestor
|
343
|
+
* @param {element} ancestor - ancestor node
|
344
|
+
* @param {element} node
|
345
|
+
*/
|
346
|
+
var makeOffsetPath = function (ancestor, node) {
|
347
|
+
var aAncestor = list.initial(listAncestor(node, func.eq(ancestor)));
|
348
|
+
return $.map(aAncestor, position).reverse();
|
349
|
+
};
|
350
|
+
|
351
|
+
/**
|
352
|
+
* return element from offsetPath(array of offset)
|
353
|
+
* @param {element} ancestor - ancestor node
|
354
|
+
* @param {array} aOffset - offsetPath
|
355
|
+
*/
|
356
|
+
var fromOffsetPath = function (ancestor, aOffset) {
|
357
|
+
var current = ancestor;
|
358
|
+
for (var i = 0, sz = aOffset.length; i < sz; i++) {
|
359
|
+
current = current.childNodes[aOffset[i]];
|
360
|
+
}
|
361
|
+
return current;
|
362
|
+
};
|
363
|
+
|
364
|
+
/**
|
365
|
+
* split element or #text
|
366
|
+
* @param {element} node
|
367
|
+
* @param {number} offset
|
368
|
+
*/
|
369
|
+
var splitData = function (node, offset) {
|
370
|
+
if (offset === 0) { return node; }
|
371
|
+
if (offset >= length(node)) { return node.nextSibling; }
|
372
|
+
|
373
|
+
// splitText
|
374
|
+
if (isText(node)) { return node.splitText(offset); }
|
375
|
+
|
376
|
+
// splitElement
|
377
|
+
var child = node.childNodes[offset];
|
378
|
+
node = insertAfter(node.cloneNode(false), node);
|
379
|
+
return appends(node, listNext(child));
|
380
|
+
};
|
381
|
+
|
382
|
+
/**
|
383
|
+
* split dom tree by boundaryPoint(pivot and offset)
|
384
|
+
* @param {element} root
|
385
|
+
* @param {element} pivot - this will be boundaryPoint's node
|
386
|
+
* @param {number} offset - this will be boundaryPoint's offset
|
387
|
+
*/
|
388
|
+
var split = function (root, pivot, offset) {
|
389
|
+
var aAncestor = listAncestor(pivot, func.eq(root));
|
390
|
+
if (aAncestor.length === 1) { return splitData(pivot, offset); }
|
391
|
+
return aAncestor.reduce(function (node, parent) {
|
392
|
+
var clone = parent.cloneNode(false);
|
393
|
+
insertAfter(clone, parent);
|
394
|
+
if (node === pivot) {
|
395
|
+
node = splitData(node, offset);
|
396
|
+
}
|
397
|
+
appends(clone, listNext(node));
|
398
|
+
return clone;
|
399
|
+
});
|
400
|
+
};
|
401
|
+
|
402
|
+
/**
|
403
|
+
* remove node, (bRemoveChild: remove child or not)
|
404
|
+
* @param {element} node
|
405
|
+
* @param {boolean} bRemoveChild
|
406
|
+
*/
|
407
|
+
var remove = function (node, bRemoveChild) {
|
408
|
+
if (!node || !node.parentNode) { return; }
|
409
|
+
if (node.removeNode) { return node.removeNode(bRemoveChild); }
|
410
|
+
|
411
|
+
var elParent = node.parentNode;
|
412
|
+
if (!bRemoveChild) {
|
413
|
+
var aNode = [];
|
414
|
+
var i, sz;
|
415
|
+
for (i = 0, sz = node.childNodes.length; i < sz; i++) {
|
416
|
+
aNode.push(node.childNodes[i]);
|
417
|
+
}
|
418
|
+
|
419
|
+
for (i = 0, sz = aNode.length; i < sz; i++) {
|
420
|
+
elParent.insertBefore(aNode[i], node);
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
elParent.removeChild(node);
|
425
|
+
};
|
426
|
+
|
427
|
+
var html = function ($node) {
|
428
|
+
return dom.isTextarea($node[0]) ? $node.val() : $node.html();
|
429
|
+
};
|
430
|
+
|
431
|
+
return {
|
432
|
+
blank: agent.bMSIE ? ' ' : '<br/>',
|
433
|
+
emptyPara: '<p><br/></p>',
|
434
|
+
isText: isText,
|
435
|
+
isPara: isPara,
|
436
|
+
isList: isList,
|
437
|
+
isEditable: isEditable,
|
438
|
+
isControlSizing: isControlSizing,
|
439
|
+
isAnchor: makePredByNodeName('A'),
|
440
|
+
isDiv: makePredByNodeName('DIV'),
|
441
|
+
isLi: makePredByNodeName('LI'),
|
442
|
+
isSpan: makePredByNodeName('SPAN'),
|
443
|
+
isB: makePredByNodeName('B'),
|
444
|
+
isU: makePredByNodeName('U'),
|
445
|
+
isS: makePredByNodeName('S'),
|
446
|
+
isI: makePredByNodeName('I'),
|
447
|
+
isImg: makePredByNodeName('IMG'),
|
448
|
+
isTextarea: makePredByNodeName('TEXTAREA'),
|
449
|
+
ancestor: ancestor,
|
450
|
+
listAncestor: listAncestor,
|
451
|
+
listNext: listNext,
|
452
|
+
listPrev: listPrev,
|
453
|
+
commonAncestor: commonAncestor,
|
454
|
+
listBetween: listBetween,
|
455
|
+
insertAfter: insertAfter,
|
456
|
+
position: position,
|
457
|
+
makeOffsetPath: makeOffsetPath,
|
458
|
+
fromOffsetPath: fromOffsetPath,
|
459
|
+
split: split,
|
460
|
+
remove: remove,
|
461
|
+
html: html
|
462
|
+
};
|
463
|
+
})();
|
464
|
+
|
465
|
+
/**
|
466
|
+
* range module
|
467
|
+
*/
|
468
|
+
var range = (function () {
|
469
|
+
var bW3CRangeSupport = !!document.createRange;
|
470
|
+
|
471
|
+
// return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
|
472
|
+
var textRange2bp = function (textRange, bStart) {
|
473
|
+
var elCont = textRange.parentElement(), nOffset;
|
474
|
+
|
475
|
+
var tester = document.body.createTextRange(), elPrevCont;
|
476
|
+
var aChild = list.from(elCont.childNodes);
|
477
|
+
for (nOffset = 0; nOffset < aChild.length; nOffset++) {
|
478
|
+
if (dom.isText(aChild[nOffset])) { continue; }
|
479
|
+
tester.moveToElementText(aChild[nOffset]);
|
480
|
+
if (tester.compareEndPoints('StartToStart', textRange) >= 0) { break; }
|
481
|
+
elPrevCont = aChild[nOffset];
|
482
|
+
}
|
483
|
+
|
484
|
+
if (nOffset !== 0 && dom.isText(aChild[nOffset - 1])) {
|
485
|
+
var textRangeStart = document.body.createTextRange(), elCurText = null;
|
486
|
+
textRangeStart.moveToElementText(elPrevCont || elCont);
|
487
|
+
textRangeStart.collapse(!elPrevCont);
|
488
|
+
elCurText = elPrevCont ? elPrevCont.nextSibling : elCont.firstChild;
|
489
|
+
|
490
|
+
var pointTester = textRange.duplicate();
|
491
|
+
pointTester.setEndPoint('StartToStart', textRangeStart);
|
492
|
+
var nTextCount = pointTester.text.replace(/[\r\n]/g, '').length;
|
493
|
+
|
494
|
+
while (nTextCount > elCurText.nodeValue.length && elCurText.nextSibling) {
|
495
|
+
nTextCount -= elCurText.nodeValue.length;
|
496
|
+
elCurText = elCurText.nextSibling;
|
497
|
+
}
|
498
|
+
|
499
|
+
/* jshint ignore:start */
|
500
|
+
var sDummy = elCurText.nodeValue; //enforce IE to re-reference elCurText, hack
|
501
|
+
/* jshint ignore:end */
|
502
|
+
|
503
|
+
if (bStart && elCurText.nextSibling && dom.isText(elCurText.nextSibling) &&
|
504
|
+
nTextCount === elCurText.nodeValue.length) {
|
505
|
+
nTextCount -= elCurText.nodeValue.length;
|
506
|
+
elCurText = elCurText.nextSibling;
|
507
|
+
}
|
508
|
+
|
509
|
+
elCont = elCurText;
|
510
|
+
nOffset = nTextCount;
|
511
|
+
}
|
512
|
+
|
513
|
+
return {cont: elCont, offset: nOffset};
|
514
|
+
};
|
515
|
+
|
516
|
+
// return TextRange from boundary point (inspired by google closure-library)
|
517
|
+
var bp2textRange = function (bp) {
|
518
|
+
var textRangeInfo = function (elCont, nOffset) {
|
519
|
+
var elNode, bCollapseToStart;
|
520
|
+
|
521
|
+
if (dom.isText(elCont)) {
|
522
|
+
var aPrevText = dom.listPrev(elCont, func.not(dom.isText));
|
523
|
+
var elPrevCont = list.last(aPrevText).previousSibling;
|
524
|
+
elNode = elPrevCont || elCont.parentNode;
|
525
|
+
nOffset += list.sum(list.tail(aPrevText), dom.length);
|
526
|
+
bCollapseToStart = !elPrevCont;
|
527
|
+
} else {
|
528
|
+
elNode = elCont.childNodes[nOffset] || elCont;
|
529
|
+
if (dom.isText(elNode)) {
|
530
|
+
return textRangeInfo(elNode, nOffset);
|
531
|
+
}
|
532
|
+
|
533
|
+
nOffset = 0;
|
534
|
+
bCollapseToStart = false;
|
535
|
+
}
|
536
|
+
|
537
|
+
return {cont: elNode, collapseToStart: bCollapseToStart, offset: nOffset};
|
538
|
+
};
|
539
|
+
|
540
|
+
var textRange = document.body.createTextRange();
|
541
|
+
var info = textRangeInfo(bp.cont, bp.offset);
|
542
|
+
|
543
|
+
textRange.moveToElementText(info.cont);
|
544
|
+
textRange.collapse(info.collapseToStart);
|
545
|
+
textRange.moveStart('character', info.offset);
|
546
|
+
return textRange;
|
547
|
+
};
|
548
|
+
|
549
|
+
// {startContainer, startOffset, endContainer, endOffset}
|
550
|
+
var WrappedRange = function (sc, so, ec, eo) {
|
551
|
+
this.sc = sc;
|
552
|
+
this.so = so;
|
553
|
+
this.ec = ec;
|
554
|
+
this.eo = eo;
|
555
|
+
|
556
|
+
// nativeRange: get nativeRange from sc, so, ec, eo
|
557
|
+
var nativeRange = function () {
|
558
|
+
if (bW3CRangeSupport) {
|
559
|
+
var w3cRange = document.createRange();
|
560
|
+
w3cRange.setStart(sc, so);
|
561
|
+
w3cRange.setEnd(ec, eo);
|
562
|
+
return w3cRange;
|
563
|
+
} else {
|
564
|
+
var textRange = bp2textRange({cont: sc, offset: so});
|
565
|
+
textRange.setEndPoint('EndToEnd', bp2textRange({cont: ec, offset: eo}));
|
566
|
+
return textRange;
|
567
|
+
}
|
568
|
+
};
|
569
|
+
|
570
|
+
// select: update visible range
|
571
|
+
this.select = function () {
|
572
|
+
var nativeRng = nativeRange();
|
573
|
+
if (bW3CRangeSupport) {
|
574
|
+
var selection = document.getSelection();
|
575
|
+
if (selection.rangeCount > 0) { selection.removeAllRanges(); }
|
576
|
+
selection.addRange(nativeRng);
|
577
|
+
} else {
|
578
|
+
nativeRng.select();
|
579
|
+
}
|
580
|
+
};
|
581
|
+
|
582
|
+
// listPara: listing paragraphs on range
|
583
|
+
this.listPara = function () {
|
584
|
+
var aNode = dom.listBetween(sc, ec);
|
585
|
+
var aPara = list.compact($.map(aNode, function (node) {
|
586
|
+
return dom.ancestor(node, dom.isPara);
|
587
|
+
}));
|
588
|
+
return $.map(list.clusterBy(aPara, func.eq2), list.head);
|
589
|
+
};
|
590
|
+
|
591
|
+
// makeIsOn: return isOn(pred) function
|
592
|
+
var makeIsOn = function (pred) {
|
593
|
+
return function () {
|
594
|
+
var elAncestor = dom.ancestor(sc, pred);
|
595
|
+
return elAncestor && (elAncestor === dom.ancestor(ec, pred));
|
596
|
+
};
|
597
|
+
};
|
598
|
+
|
599
|
+
// isOnEditable: judge whether range is on editable or not
|
600
|
+
this.isOnEditable = makeIsOn(dom.isEditable);
|
601
|
+
// isOnList: judge whether range is on list node or not
|
602
|
+
this.isOnList = makeIsOn(dom.isList);
|
603
|
+
// isOnAnchor: judge whether range is on anchor node or not
|
604
|
+
this.isOnAnchor = makeIsOn(dom.isAnchor);
|
605
|
+
// isCollapsed: judge whether range was collapsed
|
606
|
+
this.isCollapsed = function () { return sc === ec && so === eo; };
|
607
|
+
|
608
|
+
// insertNode
|
609
|
+
this.insertNode = function (node) {
|
610
|
+
var nativeRng = nativeRange();
|
611
|
+
if (bW3CRangeSupport) {
|
612
|
+
nativeRng.insertNode(node);
|
613
|
+
} else {
|
614
|
+
nativeRng.pasteHTML(node.outerHTML); // NOTE: missing node reference.
|
615
|
+
}
|
616
|
+
};
|
617
|
+
|
618
|
+
this.toString = function () {
|
619
|
+
var nativeRng = nativeRange();
|
620
|
+
return bW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
|
621
|
+
};
|
622
|
+
|
623
|
+
//bookmark: offsetPath bookmark
|
624
|
+
this.bookmark = function (elEditable) {
|
625
|
+
return {
|
626
|
+
s: { path: dom.makeOffsetPath(elEditable, sc), offset: so },
|
627
|
+
e: { path: dom.makeOffsetPath(elEditable, ec), offset: eo }
|
628
|
+
};
|
629
|
+
};
|
630
|
+
};
|
631
|
+
|
632
|
+
return { // Range Object
|
633
|
+
// create Range Object From arguments or Browser Selection
|
634
|
+
create : function (sc, so, ec, eo) {
|
635
|
+
if (arguments.length === 0) { // from Browser Selection
|
636
|
+
if (bW3CRangeSupport) { // webkit, firefox
|
637
|
+
var selection = document.getSelection();
|
638
|
+
if (selection.rangeCount === 0) { return null; }
|
639
|
+
|
640
|
+
var nativeRng = selection.getRangeAt(0);
|
641
|
+
sc = nativeRng.startContainer;
|
642
|
+
so = nativeRng.startOffset;
|
643
|
+
ec = nativeRng.endContainer;
|
644
|
+
eo = nativeRng.endOffset;
|
645
|
+
} else { // IE8: TextRange
|
646
|
+
var textRange = document.selection.createRange();
|
647
|
+
var textRangeEnd = textRange.duplicate();
|
648
|
+
textRangeEnd.collapse(false);
|
649
|
+
var textRangeStart = textRange;
|
650
|
+
textRangeStart.collapse(true);
|
651
|
+
|
652
|
+
var bpStart = textRange2bp(textRangeStart, true),
|
653
|
+
bpEnd = textRange2bp(textRangeEnd, false);
|
654
|
+
|
655
|
+
sc = bpStart.cont;
|
656
|
+
so = bpStart.offset;
|
657
|
+
ec = bpEnd.cont;
|
658
|
+
eo = bpEnd.offset;
|
659
|
+
}
|
660
|
+
} else if (arguments.length === 2) { //collapsed
|
661
|
+
ec = sc;
|
662
|
+
eo = so;
|
663
|
+
}
|
664
|
+
return new WrappedRange(sc, so, ec, eo);
|
665
|
+
},
|
666
|
+
// createFromBookmark
|
667
|
+
createFromBookmark : function (elEditable, bookmark) {
|
668
|
+
var sc = dom.fromOffsetPath(elEditable, bookmark.s.path);
|
669
|
+
var so = bookmark.s.offset;
|
670
|
+
var ec = dom.fromOffsetPath(elEditable, bookmark.e.path);
|
671
|
+
var eo = bookmark.e.offset;
|
672
|
+
return new WrappedRange(sc, so, ec, eo);
|
673
|
+
}
|
674
|
+
};
|
675
|
+
})();
|
676
|
+
|
677
|
+
/**
|
678
|
+
* aysnc functions which returns deferred object
|
679
|
+
*/
|
680
|
+
var async = (function () {
|
681
|
+
/**
|
682
|
+
* readFile
|
683
|
+
* @param {file} file - file object
|
684
|
+
*/
|
685
|
+
var readFile = function (file) {
|
686
|
+
return $.Deferred(function (deferred) {
|
687
|
+
var reader = new FileReader();
|
688
|
+
reader.onload = function (e) { deferred.resolve(e.target.result); };
|
689
|
+
reader.onerror = function () { deferred.reject(this); };
|
690
|
+
reader.readAsDataURL(file);
|
691
|
+
}).promise();
|
692
|
+
};
|
693
|
+
|
694
|
+
/**
|
695
|
+
* loadImage from url string
|
696
|
+
* @param {string} sUrl
|
697
|
+
*/
|
698
|
+
var loadImage = function (sUrl) {
|
699
|
+
return $.Deferred(function (deferred) {
|
700
|
+
var image = new Image();
|
701
|
+
image.onload = loaded;
|
702
|
+
image.onerror = errored; // URL returns 404, etc
|
703
|
+
image.onabort = errored; // IE may call this if user clicks "Stop"
|
704
|
+
image.src = sUrl;
|
705
|
+
|
706
|
+
function loaded() {
|
707
|
+
unbindEvents();
|
708
|
+
deferred.resolve(image);
|
709
|
+
}
|
710
|
+
function errored() {
|
711
|
+
unbindEvents();
|
712
|
+
deferred.reject(image);
|
713
|
+
}
|
714
|
+
function unbindEvents() {
|
715
|
+
image.onload = null;
|
716
|
+
image.onerror = null;
|
717
|
+
image.onabort = null;
|
718
|
+
}
|
719
|
+
}).promise();
|
720
|
+
};
|
721
|
+
return { readFile: readFile, loadImage: loadImage };
|
722
|
+
})();
|
723
|
+
|
724
|
+
/**
|
725
|
+
* Style
|
726
|
+
*/
|
727
|
+
var Style = function () {
|
728
|
+
// para level style
|
729
|
+
this.stylePara = function (rng, oStyle) {
|
730
|
+
var aPara = rng.listPara();
|
731
|
+
$.each(aPara, function (idx, elPara) {
|
732
|
+
$.each(oStyle, function (sKey, sValue) {
|
733
|
+
elPara.style[sKey] = sValue;
|
734
|
+
});
|
735
|
+
});
|
736
|
+
};
|
737
|
+
|
738
|
+
// get current style, elTarget: target element on event.
|
739
|
+
this.current = function (rng, elTarget) {
|
740
|
+
var $cont = $(dom.isText(rng.sc) ? rng.sc.parentNode : rng.sc);
|
741
|
+
var oStyle = $cont.css(['font-size', 'text-align',
|
742
|
+
'list-style-type', 'line-height']) || {};
|
743
|
+
|
744
|
+
oStyle['font-size'] = parseInt(oStyle['font-size']);
|
745
|
+
|
746
|
+
// document.queryCommandState for toggle state
|
747
|
+
oStyle['font-bold'] = document.queryCommandState('bold') ? 'bold' : 'normal';
|
748
|
+
oStyle['font-italic'] = document.queryCommandState('italic') ? 'italic' : 'normal';
|
749
|
+
oStyle['font-underline'] = document.queryCommandState('underline') ? 'underline' : 'normal';
|
750
|
+
|
751
|
+
// list-style-type to list-style(unordered, ordered)
|
752
|
+
if (!rng.isOnList()) {
|
753
|
+
oStyle['list-style'] = 'none';
|
754
|
+
} else {
|
755
|
+
var aOrderedType = ['circle', 'disc', 'disc-leading-zero', 'square'];
|
756
|
+
var bUnordered = $.inArray(oStyle['list-style-type'], aOrderedType) > -1;
|
757
|
+
oStyle['list-style'] = bUnordered ? 'unordered' : 'ordered';
|
758
|
+
}
|
759
|
+
|
760
|
+
var elPara = dom.ancestor(rng.sc, dom.isPara);
|
761
|
+
if (elPara && elPara.style['line-height']) {
|
762
|
+
oStyle['line-height'] = elPara.style.lineHeight;
|
763
|
+
} else {
|
764
|
+
var lineHeight = parseInt(oStyle['line-height']) / parseInt(oStyle['font-size']);
|
765
|
+
oStyle['line-height'] = lineHeight.toFixed(1);
|
766
|
+
}
|
767
|
+
|
768
|
+
oStyle.image = dom.isImg(elTarget) && elTarget;
|
769
|
+
oStyle.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
|
770
|
+
oStyle.aAncestor = dom.listAncestor(rng.sc, dom.isEditable);
|
771
|
+
|
772
|
+
return oStyle;
|
773
|
+
};
|
774
|
+
};
|
775
|
+
|
776
|
+
/**
|
777
|
+
* History
|
778
|
+
*/
|
779
|
+
var History = function () {
|
780
|
+
var aUndo = [], aRedo = [];
|
781
|
+
|
782
|
+
var makeSnap = function ($editable) {
|
783
|
+
var elEditable = $editable[0], rng = range.create();
|
784
|
+
return {
|
785
|
+
contents: $editable.html(),
|
786
|
+
bookmark: rng.bookmark(elEditable),
|
787
|
+
scrollTop: $editable.scrollTop()
|
788
|
+
};
|
789
|
+
};
|
790
|
+
|
791
|
+
var applySnap = function ($editable, oSnap) {
|
792
|
+
$editable.html(oSnap.contents).scrollTop(oSnap.scrollTop);
|
793
|
+
range.createFromBookmark($editable[0], oSnap.bookmark).select();
|
794
|
+
};
|
795
|
+
|
796
|
+
this.undo = function ($editable) {
|
797
|
+
var oSnap = makeSnap($editable);
|
798
|
+
if (aUndo.length === 0) { return; }
|
799
|
+
applySnap($editable, aUndo.pop());
|
800
|
+
aRedo.push(oSnap);
|
801
|
+
};
|
802
|
+
|
803
|
+
this.redo = function ($editable) {
|
804
|
+
var oSnap = makeSnap($editable);
|
805
|
+
if (aRedo.length === 0) { return; }
|
806
|
+
applySnap($editable, aRedo.pop());
|
807
|
+
aUndo.push(oSnap);
|
808
|
+
};
|
809
|
+
|
810
|
+
this.recordUndo = function ($editable) {
|
811
|
+
aRedo = [];
|
812
|
+
aUndo.push(makeSnap($editable));
|
813
|
+
};
|
814
|
+
};
|
815
|
+
|
816
|
+
var Table = function () {
|
817
|
+
/**
|
818
|
+
* Create empty table element
|
819
|
+
* @param nRow {number}
|
820
|
+
* @param nCol {number}
|
821
|
+
*/
|
822
|
+
this.createTable = function (nCol, nRow) {
|
823
|
+
var aTD = [], sTD;
|
824
|
+
for (var idxCol = 0; idxCol < nCol; idxCol++) {
|
825
|
+
aTD.push('<td>' + dom.blank + '</td>');
|
826
|
+
}
|
827
|
+
sTD = aTD.join('');
|
828
|
+
|
829
|
+
var aTR = [], sTR;
|
830
|
+
for (var idxRow = 0; idxRow < nRow; idxRow++) {
|
831
|
+
aTR.push('<tr>' + sTD + '</tr>');
|
832
|
+
}
|
833
|
+
sTR = aTR.join('');
|
834
|
+
var sTable = '<table class="table table-bordered">' + sTR + '</table>';
|
835
|
+
|
836
|
+
return $(sTable)[0];
|
837
|
+
};
|
838
|
+
};
|
839
|
+
|
840
|
+
/**
|
841
|
+
* Editor
|
842
|
+
*/
|
843
|
+
var Editor = function () {
|
844
|
+
|
845
|
+
var style = new Style();
|
846
|
+
var table = new Table();
|
847
|
+
|
848
|
+
/**
|
849
|
+
* save current range
|
850
|
+
* @param $editable {jQuery}
|
851
|
+
*/
|
852
|
+
this.saveRange = function ($editable) {
|
853
|
+
$editable.data('range', range.create());
|
854
|
+
};
|
855
|
+
|
856
|
+
/**
|
857
|
+
* restore lately range
|
858
|
+
* @param $editable {jQuery}
|
859
|
+
*/
|
860
|
+
this.restoreRange = function ($editable) {
|
861
|
+
var rng = $editable.data('range');
|
862
|
+
if (rng) { rng.select(); }
|
863
|
+
};
|
864
|
+
|
865
|
+
/**
|
866
|
+
* currentStyle
|
867
|
+
* @param elTarget {element}
|
868
|
+
*/
|
869
|
+
this.currentStyle = function (elTarget) {
|
870
|
+
var rng = range.create();
|
871
|
+
return rng.isOnEditable() && style.current(rng, elTarget);
|
872
|
+
};
|
873
|
+
|
874
|
+
/**
|
875
|
+
* undo
|
876
|
+
* @param $editable {jQuery}
|
877
|
+
*/
|
878
|
+
this.undo = function ($editable) {
|
879
|
+
$editable.data('NoteHistory').undo($editable);
|
880
|
+
};
|
881
|
+
|
882
|
+
/**
|
883
|
+
* redo
|
884
|
+
* @param $editable {jQuery}
|
885
|
+
*/
|
886
|
+
this.redo = function ($editable) {
|
887
|
+
$editable.data('NoteHistory').redo($editable);
|
888
|
+
};
|
889
|
+
|
890
|
+
/**
|
891
|
+
* record Undo
|
892
|
+
* @param $editable {jQuery}
|
893
|
+
*/
|
894
|
+
var recordUndo = this.recordUndo = function ($editable) {
|
895
|
+
$editable.data('NoteHistory').recordUndo($editable);
|
896
|
+
};
|
897
|
+
|
898
|
+
/* jshint ignore:start */
|
899
|
+
// native commands(with execCommand), generate function for execCommand
|
900
|
+
var aCmd = ['bold', 'italic', 'underline', 'strikethrough',
|
901
|
+
'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
|
902
|
+
'insertOrderedList', 'insertUnorderedList',
|
903
|
+
'indent', 'outdent', 'formatBlock', 'removeFormat',
|
904
|
+
'backColor', 'foreColor', 'insertHorizontalRule'];
|
905
|
+
|
906
|
+
for (var idx = 0, len = aCmd.length; idx < len; idx ++) {
|
907
|
+
this[aCmd[idx]] = (function (sCmd) {
|
908
|
+
return function ($editable, sValue) {
|
909
|
+
recordUndo($editable);
|
910
|
+
document.execCommand(sCmd, false, sValue);
|
911
|
+
};
|
912
|
+
})(aCmd[idx]);
|
913
|
+
}
|
914
|
+
/* jshint ignore:end */
|
915
|
+
|
916
|
+
/**
|
917
|
+
* handle tag key
|
918
|
+
* @param $editable {jQuery}
|
919
|
+
*/
|
920
|
+
this.tab = function ($editable) {
|
921
|
+
recordUndo($editable);
|
922
|
+
var rng = range.create();
|
923
|
+
var sNbsp = new Array($editable.data('tabsize') + 1).join(' ');
|
924
|
+
rng.insertNode($('<span id="editorTab">' + sNbsp + '</span>')[0]);
|
925
|
+
var $tab = $('#editorTab').removeAttr('id');
|
926
|
+
rng = range.create($tab[0], 1);
|
927
|
+
rng.select();
|
928
|
+
dom.remove($tab[0]);
|
929
|
+
};
|
930
|
+
|
931
|
+
/**
|
932
|
+
* insert Image
|
933
|
+
* @param $editable {jQuery}
|
934
|
+
* @param sUrl {string}
|
935
|
+
*/
|
936
|
+
this.insertImage = function ($editable, sUrl) {
|
937
|
+
async.loadImage(sUrl).done(function (image) {
|
938
|
+
recordUndo($editable);
|
939
|
+
var $image = $('<img>').attr('src', sUrl);
|
940
|
+
$image.css('width', Math.min($editable.width(), image.width));
|
941
|
+
range.create().insertNode($image[0]);
|
942
|
+
}).fail(function () {
|
943
|
+
var callbacks = $editable.data('callbacks');
|
944
|
+
if (callbacks.onImageUploadError) {
|
945
|
+
callbacks.onImageUploadError();
|
946
|
+
}
|
947
|
+
});
|
948
|
+
};
|
949
|
+
|
950
|
+
/**
|
951
|
+
* insert video
|
952
|
+
* @param $editable {jQuery}
|
953
|
+
* @param sUrl {string}
|
954
|
+
*/
|
955
|
+
this.insertVideo = function ($editable, sUrl) {
|
956
|
+
// video url patterns(youtube, instagram, vimeo, dailymotion, youku)
|
957
|
+
var ytRegExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
|
958
|
+
var ytMatch = sUrl.match(ytRegExp);
|
959
|
+
|
960
|
+
var igRegExp = /\/\/instagram.com\/p\/(.[a-zA-Z0-9]*)/;
|
961
|
+
var igMatch = sUrl.match(igRegExp);
|
962
|
+
|
963
|
+
var vRegExp = /\/\/vine.co\/v\/(.[a-zA-Z0-9]*)/;
|
964
|
+
var vMatch = sUrl.match(vRegExp);
|
965
|
+
|
966
|
+
var vimRegExp = /\/\/(player.)?vimeo.com\/([a-z]*\/)*([0-9]{6,11})[?]?.*/;
|
967
|
+
var vimMatch = sUrl.match(vimRegExp);
|
968
|
+
|
969
|
+
var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
|
970
|
+
var dmMatch = sUrl.match(dmRegExp);
|
971
|
+
|
972
|
+
var ykRegExp = /\/\/(v.youku.com)\/v_show\/id_(.+?).html/;
|
973
|
+
var ykMatch = sUrl.match(ykRegExp);
|
974
|
+
|
975
|
+
var $video;
|
976
|
+
if (ytMatch && ytMatch[2].length === 11) {
|
977
|
+
var youtubeId = ytMatch[2];
|
978
|
+
$video = $('<iframe>')
|
979
|
+
.attr('src', 'http://www.youtube.com/embed/' + youtubeId)
|
980
|
+
.attr('width', '640').attr('height', '360');
|
981
|
+
} else if (igMatch && igMatch[0].length > 0) {
|
982
|
+
$video = $('<iframe>')
|
983
|
+
.attr('src', igMatch[0] + '/embed/')
|
984
|
+
.attr('width', '612').attr('height', '710')
|
985
|
+
.attr('scrolling', 'no')
|
986
|
+
.attr('allowtransparency', 'true');
|
987
|
+
} else if (vMatch && vMatch[0].length > 0) {
|
988
|
+
$video = $('<iframe>')
|
989
|
+
.attr('src', vMatch[0] + '/embed/simple')
|
990
|
+
.attr('width', '600').attr('height', '600')
|
991
|
+
.attr('class', 'vine-embed');
|
992
|
+
} else if (vimMatch && vimMatch[3].length > 0) {
|
993
|
+
$video = $('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
|
994
|
+
.attr('src', '//player.vimeo.com/video/' + vimMatch[3])
|
995
|
+
.attr('width', '640').attr('height', '360');
|
996
|
+
} else if (dmMatch && dmMatch[2].length > 0) {
|
997
|
+
$video = $('<iframe>')
|
998
|
+
.attr('src', 'http://www.dailymotion.com/embed/video/' + dmMatch[2])
|
999
|
+
.attr('width', '640').attr('height', '360');
|
1000
|
+
} else if (ykMatch && ykMatch[2].length > 0) {
|
1001
|
+
$video = $('<iframe>')
|
1002
|
+
.attr('src', 'http://player.youku.com/player.php/sid/' + ykMatch[2] + '/v.swf')
|
1003
|
+
.attr('width', '600').attr('height', '360');
|
1004
|
+
} else {
|
1005
|
+
// this is not a known video link. Now what, Cat? Now what?
|
1006
|
+
}
|
1007
|
+
|
1008
|
+
if ($video) {
|
1009
|
+
$video.attr('frameborder', 0);
|
1010
|
+
range.create().insertNode($video[0]);
|
1011
|
+
}
|
1012
|
+
};
|
1013
|
+
|
1014
|
+
/**
|
1015
|
+
* formatBlock
|
1016
|
+
* @param $editable {jQuery}
|
1017
|
+
* @param sTagName {string} - tag name
|
1018
|
+
*/
|
1019
|
+
this.formatBlock = function ($editable, sTagName) {
|
1020
|
+
recordUndo($editable);
|
1021
|
+
sTagName = agent.bMSIE ? '<' + sTagName + '>' : sTagName;
|
1022
|
+
document.execCommand('FormatBlock', false, sTagName);
|
1023
|
+
};
|
1024
|
+
|
1025
|
+
/**
|
1026
|
+
* fontsize
|
1027
|
+
* @param $editable {jQuery}
|
1028
|
+
* @param sValue {string} - fontsize (px)
|
1029
|
+
*/
|
1030
|
+
this.fontSize = function ($editable, sValue) {
|
1031
|
+
recordUndo($editable);
|
1032
|
+
document.execCommand('fontSize', false, 3);
|
1033
|
+
if (agent.bFF) {
|
1034
|
+
// firefox: <font size="3"> to <span style='font-size={sValue}px;'>, buggy
|
1035
|
+
$editable.find('font[size=3]').removeAttr('size').css('font-size', sValue + 'px');
|
1036
|
+
} else {
|
1037
|
+
// chrome: <span style="font-size: medium"> to <span style='font-size={sValue}px;'>
|
1038
|
+
$editable.find('span').filter(function () {
|
1039
|
+
return this.style.fontSize === 'medium';
|
1040
|
+
}).css('font-size', sValue + 'px');
|
1041
|
+
}
|
1042
|
+
};
|
1043
|
+
|
1044
|
+
/**
|
1045
|
+
* lineHeight
|
1046
|
+
* @param $editable {jQuery}
|
1047
|
+
* @param sValue {string} - lineHeight
|
1048
|
+
*/
|
1049
|
+
this.lineHeight = function ($editable, sValue) {
|
1050
|
+
recordUndo($editable);
|
1051
|
+
style.stylePara(range.create(), {lineHeight: sValue});
|
1052
|
+
};
|
1053
|
+
|
1054
|
+
/**
|
1055
|
+
* unlink
|
1056
|
+
* @param $editable {jQuery}
|
1057
|
+
*/
|
1058
|
+
this.unlink = function ($editable) {
|
1059
|
+
var rng = range.create();
|
1060
|
+
if (rng.isOnAnchor()) {
|
1061
|
+
recordUndo($editable);
|
1062
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
1063
|
+
rng = range.create(elAnchor, 0, elAnchor, 1);
|
1064
|
+
rng.select();
|
1065
|
+
document.execCommand('unlink');
|
1066
|
+
}
|
1067
|
+
};
|
1068
|
+
|
1069
|
+
this.setLinkDialog = function ($editable, fnShowDialog) {
|
1070
|
+
var rng = range.create();
|
1071
|
+
if (rng.isOnAnchor()) {
|
1072
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
1073
|
+
rng = range.create(elAnchor, 0, elAnchor, 1);
|
1074
|
+
}
|
1075
|
+
fnShowDialog({
|
1076
|
+
range: rng,
|
1077
|
+
text: rng.toString(),
|
1078
|
+
url: rng.isOnAnchor() ? dom.ancestor(rng.sc, dom.isAnchor).href : ''
|
1079
|
+
}, function (sLinkUrl) {
|
1080
|
+
rng.select();
|
1081
|
+
recordUndo($editable);
|
1082
|
+
|
1083
|
+
var sLinkUrlWithProtocol;
|
1084
|
+
if (sLinkUrl.indexOf('@') !== -1) { // email address
|
1085
|
+
sLinkUrlWithProtocol = sLinkUrl.indexOf(':') !== -1 ? sLinkUrl : 'mailto:' + sLinkUrl;
|
1086
|
+
} else { // normal address
|
1087
|
+
sLinkUrlWithProtocol = sLinkUrl.indexOf('://') !== -1 ? sLinkUrl : 'http://' + sLinkUrl;
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
//IE: createLink when range collapsed.
|
1091
|
+
if (agent.bMSIE && rng.isCollapsed()) {
|
1092
|
+
rng.insertNode($('<A id="linkAnchor">' + sLinkUrl + '</A>')[0]);
|
1093
|
+
var $anchor = $('#linkAnchor').removeAttr('id')
|
1094
|
+
.attr('href', sLinkUrlWithProtocol);
|
1095
|
+
rng = range.create($anchor[0], 0, $anchor[0], 1);
|
1096
|
+
rng.select();
|
1097
|
+
} else {
|
1098
|
+
document.execCommand('createlink', false, sLinkUrlWithProtocol);
|
1099
|
+
}
|
1100
|
+
});
|
1101
|
+
};
|
1102
|
+
|
1103
|
+
this.setVideoDialog = function ($editable, fnShowDialog) {
|
1104
|
+
var rng = range.create();
|
1105
|
+
var editor = this;
|
1106
|
+
|
1107
|
+
if (rng.isOnAnchor()) {
|
1108
|
+
var elAnchor = dom.ancestor(rng.sc, dom.isAnchor);
|
1109
|
+
rng = range.create(elAnchor, 0, elAnchor, 1);
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
fnShowDialog({
|
1113
|
+
range: rng,
|
1114
|
+
text: rng.toString()
|
1115
|
+
}, function (sLinkUrl) {
|
1116
|
+
rng.select();
|
1117
|
+
recordUndo($editable);
|
1118
|
+
editor.insertVideo($editable, sLinkUrl);
|
1119
|
+
});
|
1120
|
+
};
|
1121
|
+
|
1122
|
+
this.color = function ($editable, sObjColor) {
|
1123
|
+
var oColor = JSON.parse(sObjColor);
|
1124
|
+
var foreColor = oColor.foreColor, backColor = oColor.backColor;
|
1125
|
+
|
1126
|
+
recordUndo($editable);
|
1127
|
+
if (foreColor) { document.execCommand('foreColor', false, foreColor); }
|
1128
|
+
if (backColor) { document.execCommand('backColor', false, backColor); }
|
1129
|
+
};
|
1130
|
+
|
1131
|
+
this.insertTable = function ($editable, sDim) {
|
1132
|
+
recordUndo($editable);
|
1133
|
+
var aDim = sDim.split('x');
|
1134
|
+
range.create().insertNode(table.createTable(aDim[0], aDim[1]));
|
1135
|
+
};
|
1136
|
+
|
1137
|
+
this.floatMe = function ($editable, sValue, elTarget) {
|
1138
|
+
recordUndo($editable);
|
1139
|
+
elTarget.style.cssFloat = sValue;
|
1140
|
+
};
|
1141
|
+
|
1142
|
+
/**
|
1143
|
+
* resize target
|
1144
|
+
* @param $editable {jQuery}
|
1145
|
+
* @param sValue {string}
|
1146
|
+
* @param elTarget {element} - target element
|
1147
|
+
*/
|
1148
|
+
this.resize = function ($editable, sValue, elTarget) {
|
1149
|
+
recordUndo($editable);
|
1150
|
+
elTarget.style.width = $editable.width() * sValue + 'px';
|
1151
|
+
elTarget.style.height = '';
|
1152
|
+
};
|
1153
|
+
|
1154
|
+
this.resizeTo = function (pos, $target) {
|
1155
|
+
var newRatio = pos.y / pos.x;
|
1156
|
+
var ratio = $target.data('ratio');
|
1157
|
+
|
1158
|
+
$target.css({
|
1159
|
+
width: ratio > newRatio ? pos.x : pos.y / ratio,
|
1160
|
+
height: ratio > newRatio ? pos.x * ratio : pos.y
|
1161
|
+
});
|
1162
|
+
};
|
1163
|
+
};
|
1164
|
+
|
1165
|
+
/**
|
1166
|
+
* Toolbar
|
1167
|
+
*/
|
1168
|
+
var Toolbar = function () {
|
1169
|
+
this.update = function ($toolbar, oStyle) {
|
1170
|
+
//handle selectbox for fontsize, lineHeight
|
1171
|
+
var checkDropdownMenu = function ($btn, nValue) {
|
1172
|
+
$btn.find('.dropdown-menu li a').each(function () {
|
1173
|
+
var bChecked = parseFloat($(this).data('value')) === nValue;
|
1174
|
+
this.className = bChecked ? 'checked' : '';
|
1175
|
+
});
|
1176
|
+
};
|
1177
|
+
|
1178
|
+
var $fontsize = $toolbar.find('.editor-fontsize');
|
1179
|
+
$fontsize.find('.editor-current-fontsize').html(oStyle['font-size']);
|
1180
|
+
checkDropdownMenu($fontsize, parseFloat(oStyle['font-size']));
|
1181
|
+
|
1182
|
+
var $lineHeight = $toolbar.find('.editor-height');
|
1183
|
+
checkDropdownMenu($lineHeight, parseFloat(oStyle['line-height']));
|
1184
|
+
|
1185
|
+
//check button state
|
1186
|
+
var btnState = function (sSelector, pred) {
|
1187
|
+
var $btn = $toolbar.find(sSelector);
|
1188
|
+
$btn[pred() ? 'addClass' : 'removeClass']('active');
|
1189
|
+
};
|
1190
|
+
|
1191
|
+
btnState('button[data-event="bold"]', function () {
|
1192
|
+
return oStyle['font-bold'] === 'bold';
|
1193
|
+
});
|
1194
|
+
btnState('button[data-event="italic"]', function () {
|
1195
|
+
return oStyle['font-italic'] === 'italic';
|
1196
|
+
});
|
1197
|
+
btnState('button[data-event="underline"]', function () {
|
1198
|
+
return oStyle['font-underline'] === 'underline';
|
1199
|
+
});
|
1200
|
+
btnState('button[data-event="justifyLeft"]', function () {
|
1201
|
+
return oStyle['text-align'] === 'left' || oStyle['text-align'] === 'start';
|
1202
|
+
});
|
1203
|
+
btnState('button[data-event="justifyCenter"]', function () {
|
1204
|
+
return oStyle['text-align'] === 'center';
|
1205
|
+
});
|
1206
|
+
btnState('button[data-event="justifyRight"]', function () {
|
1207
|
+
return oStyle['text-align'] === 'right';
|
1208
|
+
});
|
1209
|
+
btnState('button[data-event="justifyFull"]', function () {
|
1210
|
+
return oStyle['text-align'] === 'justify';
|
1211
|
+
});
|
1212
|
+
btnState('button[data-event="insertUnorderedList"]', function () {
|
1213
|
+
return oStyle['list-style'] === 'unordered';
|
1214
|
+
});
|
1215
|
+
btnState('button[data-event="insertOrderedList"]', function () {
|
1216
|
+
return oStyle['list-style'] === 'ordered';
|
1217
|
+
});
|
1218
|
+
};
|
1219
|
+
|
1220
|
+
this.updateRecentColor = function (elBtn, sEvent, sValue) {
|
1221
|
+
var $color = $(elBtn).closest('.editor-color');
|
1222
|
+
var $recentColor = $color.find('.editor-recent-color');
|
1223
|
+
var oColor = JSON.parse($recentColor.attr('data-value'));
|
1224
|
+
oColor[sEvent] = sValue;
|
1225
|
+
$recentColor.attr('data-value', JSON.stringify(oColor));
|
1226
|
+
var sKey = sEvent === 'backColor' ? 'background-color' : 'color';
|
1227
|
+
$recentColor.find('i').css(sKey, sValue);
|
1228
|
+
};
|
1229
|
+
|
1230
|
+
this.updateFullscreen = function ($toolbar, bFullscreen) {
|
1231
|
+
var $btn = $toolbar.find('button[data-event="fullscreen"]');
|
1232
|
+
$btn[bFullscreen ? 'addClass' : 'removeClass']('active');
|
1233
|
+
};
|
1234
|
+
this.updateCodeview = function ($toolbar, bCodeview) {
|
1235
|
+
var $btn = $toolbar.find('button[data-event="codeview"]');
|
1236
|
+
$btn[bCodeview ? 'addClass' : 'removeClass']('active');
|
1237
|
+
};
|
1238
|
+
|
1239
|
+
this.enable = function ($toolbar) {
|
1240
|
+
$toolbar.find('button').not('button[data-event="codeview"]').removeClass('disabled');
|
1241
|
+
};
|
1242
|
+
|
1243
|
+
this.disable = function ($toolbar) {
|
1244
|
+
$toolbar.find('button').not('button[data-event="codeview"]').addClass('disabled');
|
1245
|
+
};
|
1246
|
+
};
|
1247
|
+
|
1248
|
+
/**
|
1249
|
+
* Popover (http://getbootstrap.com/javascript/#popovers)
|
1250
|
+
*/
|
1251
|
+
var Popover = function () {
|
1252
|
+
var showPopover = function ($popover, elPlaceholder) {
|
1253
|
+
var $placeholder = $(elPlaceholder);
|
1254
|
+
var pos = $placeholder.position(), height = $placeholder.height();
|
1255
|
+
$popover.css({
|
1256
|
+
display: 'block',
|
1257
|
+
left: pos.left,
|
1258
|
+
top: pos.top + height
|
1259
|
+
});
|
1260
|
+
};
|
1261
|
+
|
1262
|
+
this.update = function ($popover, oStyle) {
|
1263
|
+
var $linkPopover = $popover.find('.editor-link-popover'),
|
1264
|
+
$imagePopover = $popover.find('.editor-image-popover');
|
1265
|
+
if (oStyle.anchor) {
|
1266
|
+
var $anchor = $linkPopover.find('a');
|
1267
|
+
$anchor.attr('href', oStyle.anchor.href).html(oStyle.anchor.href);
|
1268
|
+
showPopover($linkPopover, oStyle.anchor);
|
1269
|
+
} else {
|
1270
|
+
$linkPopover.hide();
|
1271
|
+
}
|
1272
|
+
|
1273
|
+
if (oStyle.image) {
|
1274
|
+
showPopover($imagePopover, oStyle.image);
|
1275
|
+
} else {
|
1276
|
+
$imagePopover.hide();
|
1277
|
+
}
|
1278
|
+
};
|
1279
|
+
|
1280
|
+
this.hide = function ($popover) {
|
1281
|
+
$popover.children().hide();
|
1282
|
+
};
|
1283
|
+
};
|
1284
|
+
|
1285
|
+
/**
|
1286
|
+
* Handle
|
1287
|
+
*/
|
1288
|
+
var Handle = function () {
|
1289
|
+
this.update = function ($handle, oStyle) {
|
1290
|
+
var $selection = $handle.find('.editor-control-selection');
|
1291
|
+
if (oStyle.image) {
|
1292
|
+
var $image = $(oStyle.image);
|
1293
|
+
var pos = $image.position();
|
1294
|
+
var szImage = {w: $image.width(), h: $image.height()};
|
1295
|
+
$selection.css({
|
1296
|
+
display: 'block',
|
1297
|
+
left: pos.left,
|
1298
|
+
top: pos.top,
|
1299
|
+
width: szImage.w,
|
1300
|
+
height: szImage.h
|
1301
|
+
}).data('target', oStyle.image); // save current image element.
|
1302
|
+
var sSizing = szImage.w + 'x' + szImage.h;
|
1303
|
+
$selection.find('.editor-control-selection-info').text(sSizing);
|
1304
|
+
} else {
|
1305
|
+
$selection.hide();
|
1306
|
+
}
|
1307
|
+
};
|
1308
|
+
|
1309
|
+
this.hide = function ($handle) {
|
1310
|
+
$handle.children().hide();
|
1311
|
+
};
|
1312
|
+
};
|
1313
|
+
|
1314
|
+
/**
|
1315
|
+
* Dialog
|
1316
|
+
*/
|
1317
|
+
var Dialog = function () {
|
1318
|
+
/**
|
1319
|
+
* toggle button status
|
1320
|
+
* @param $btn {jquery}
|
1321
|
+
* @param bEnable {boolean}
|
1322
|
+
*/
|
1323
|
+
var toggleBtn = function ($btn, bEnable) {
|
1324
|
+
if (bEnable) {
|
1325
|
+
$btn.removeClass('disabled').attr('disabled', false);
|
1326
|
+
} else {
|
1327
|
+
$btn.addClass('disabled').attr('disabled', true);
|
1328
|
+
}
|
1329
|
+
};
|
1330
|
+
|
1331
|
+
/**
|
1332
|
+
* show image dialog
|
1333
|
+
* @param $dialog {jquery}
|
1334
|
+
* @param fnInsertImages {function}
|
1335
|
+
* @param fnInsertImage {function}
|
1336
|
+
*/
|
1337
|
+
this.showImageDialog = function ($dialog, fnInsertImages, fnInsertImage) {
|
1338
|
+
var $imageDialog = $dialog.find('.editor-image-dialog');
|
1339
|
+
var $imageInput = $dialog.find('.editor-image-input'),
|
1340
|
+
$imageUrl = $dialog.find('.editor-image-url'),
|
1341
|
+
$imageBtn = $dialog.find('.editor-image-btn');
|
1342
|
+
|
1343
|
+
$imageDialog.on('shown.bs.modal', function () {
|
1344
|
+
$imageInput.on('change', function () {
|
1345
|
+
fnInsertImages(this.files);
|
1346
|
+
$(this).val('');
|
1347
|
+
$imageDialog.modal('hide');
|
1348
|
+
});
|
1349
|
+
$imageUrl.val('').keyup(function () {
|
1350
|
+
toggleBtn($imageBtn, $imageUrl.val());
|
1351
|
+
}).trigger('focus');
|
1352
|
+
$imageBtn.click(function (event) {
|
1353
|
+
$imageDialog.modal('hide');
|
1354
|
+
fnInsertImage($imageUrl.val());
|
1355
|
+
event.preventDefault();
|
1356
|
+
});
|
1357
|
+
}).on('hidden.bs.modal', function () {
|
1358
|
+
$imageInput.off('change');
|
1359
|
+
$imageDialog.off('shown.bs.modal hidden.bs.modal');
|
1360
|
+
$imageUrl.off('keyup');
|
1361
|
+
$imageBtn.off('click');
|
1362
|
+
}).modal('show');
|
1363
|
+
};
|
1364
|
+
|
1365
|
+
/**
|
1366
|
+
* show video dialog
|
1367
|
+
* @param $dialog {jquery}
|
1368
|
+
* @param videoInfo {object}
|
1369
|
+
* @param callback {function}
|
1370
|
+
*/
|
1371
|
+
this.showVideoDialog = function ($dialog, videoInfo, callback) {
|
1372
|
+
var $videoDialog = $dialog.find('.editor-video-dialog');
|
1373
|
+
var $videoUrl = $videoDialog.find('.editor-video-url'),
|
1374
|
+
$videoBtn = $videoDialog.find('.editor-video-btn');
|
1375
|
+
|
1376
|
+
$videoDialog.on('shown.bs.modal', function () {
|
1377
|
+
$videoUrl.val(videoInfo.text).keyup(function () {
|
1378
|
+
toggleBtn($videoBtn, $videoUrl.val());
|
1379
|
+
}).trigger('keyup').trigger('focus');
|
1380
|
+
|
1381
|
+
$videoBtn.click(function (event) {
|
1382
|
+
$videoDialog.modal('hide');
|
1383
|
+
callback($videoUrl.val());
|
1384
|
+
event.preventDefault();
|
1385
|
+
});
|
1386
|
+
}).on('hidden.bs.modal', function () {
|
1387
|
+
$videoUrl.off('keyup');
|
1388
|
+
$videoBtn.off('click');
|
1389
|
+
$videoDialog.off('show.bs.modal hidden.bs.modal');
|
1390
|
+
}).modal('show');
|
1391
|
+
};
|
1392
|
+
|
1393
|
+
/**
|
1394
|
+
* show link dialog
|
1395
|
+
* @param $dialog {jquery}
|
1396
|
+
* @param linkInfo {object}
|
1397
|
+
* @param callback {function}
|
1398
|
+
*/
|
1399
|
+
this.showLinkDialog = function ($dialog, linkInfo, callback) {
|
1400
|
+
var $linkDialog = $dialog.find('.editor-link-dialog');
|
1401
|
+
var $linkText = $linkDialog.find('.editor-link-text'),
|
1402
|
+
$linkUrl = $linkDialog.find('.editor-link-url'),
|
1403
|
+
$linkBtn = $linkDialog.find('.editor-link-btn');
|
1404
|
+
|
1405
|
+
$linkDialog.on('shown.bs.modal', function () {
|
1406
|
+
$linkText.html(linkInfo.text);
|
1407
|
+
$linkUrl.val(linkInfo.url).keyup(function () {
|
1408
|
+
toggleBtn($linkBtn, $linkUrl.val());
|
1409
|
+
if (!linkInfo.text) { $linkText.html($linkUrl.val()); }
|
1410
|
+
}).trigger('focus');
|
1411
|
+
$linkBtn.click(function (event) {
|
1412
|
+
$linkDialog.modal('hide'); //hide and createLink (ie9+)
|
1413
|
+
callback($linkUrl.val());
|
1414
|
+
event.preventDefault();
|
1415
|
+
});
|
1416
|
+
}).on('hidden.bs.modal', function () {
|
1417
|
+
$linkUrl.off('keyup');
|
1418
|
+
$linkBtn.off('click');
|
1419
|
+
$linkDialog.off('shown.bs.modal hidden.bs.modal');
|
1420
|
+
}).modal('show');
|
1421
|
+
};
|
1422
|
+
|
1423
|
+
/**
|
1424
|
+
* show help dialog
|
1425
|
+
* @param $dialog {jquery}
|
1426
|
+
*/
|
1427
|
+
this.showHelpDialog = function ($dialog) {
|
1428
|
+
$dialog.find('.editor-help-dialog').modal('show');
|
1429
|
+
};
|
1430
|
+
};
|
1431
|
+
|
1432
|
+
/**
|
1433
|
+
* EventHandler
|
1434
|
+
*
|
1435
|
+
* handle mouse & key event on editor
|
1436
|
+
*/
|
1437
|
+
var EventHandler = function () {
|
1438
|
+
var editor = new Editor();
|
1439
|
+
var toolbar = new Toolbar(), popover = new Popover();
|
1440
|
+
var handle = new Handle(), dialog = new Dialog();
|
1441
|
+
|
1442
|
+
var key = { BACKSPACE: 8, TAB: 9, ENTER: 13, SPACE: 32,
|
1443
|
+
NUM0: 48, NUM1: 49, NUM6: 54, NUM7: 55, NUM8: 56,
|
1444
|
+
B: 66, E: 69, I: 73, J: 74, K: 75, L: 76, R: 82, S: 83, U: 85,
|
1445
|
+
Y: 89, Z: 90, SLASH: 191,
|
1446
|
+
LEFTBRACKET: 219, BACKSLACH: 220, RIGHTBRACKET: 221 };
|
1447
|
+
|
1448
|
+
// makeLayoutInfo from editor's descendant node.
|
1449
|
+
var makeLayoutInfo = function (descendant) {
|
1450
|
+
var $editor = $(descendant).closest('.editor-canvas');
|
1451
|
+
return {
|
1452
|
+
editor: function () { return $editor; },
|
1453
|
+
toolbar: function () { return $editor.find('.editor-toolbar'); },
|
1454
|
+
editable: function () { return $editor.find('.editor-editable'); },
|
1455
|
+
codable: function () { return $editor.find('.editor-codable'); },
|
1456
|
+
statusbar: function () { return $editor.find('.editor-statusbar'); },
|
1457
|
+
popover: function () { return $editor.find('.editor-popover'); },
|
1458
|
+
handle: function () { return $editor.find('.editor-handle'); },
|
1459
|
+
dialog: function () { return $editor.find('.editor-dialog'); }
|
1460
|
+
};
|
1461
|
+
};
|
1462
|
+
|
1463
|
+
var hKeydown = function (event) {
|
1464
|
+
var bCmd = agent.bMac ? event.metaKey : event.ctrlKey,
|
1465
|
+
bShift = event.shiftKey, keyCode = event.keyCode;
|
1466
|
+
|
1467
|
+
// optimize
|
1468
|
+
var bExecCmd = (bCmd || bShift || keyCode === key.TAB);
|
1469
|
+
var oLayoutInfo = (bExecCmd) ? makeLayoutInfo(event.target) : null;
|
1470
|
+
|
1471
|
+
if (keyCode === key.TAB && oLayoutInfo.editable().data('tabsize')) {
|
1472
|
+
editor.tab(oLayoutInfo.editable());
|
1473
|
+
} else if (bCmd && ((bShift && keyCode === key.Z) || keyCode === key.Y)) {
|
1474
|
+
editor.redo(oLayoutInfo.editable());
|
1475
|
+
} else if (bCmd && keyCode === key.Z) {
|
1476
|
+
editor.undo(oLayoutInfo.editable());
|
1477
|
+
} else if (bCmd && keyCode === key.B) {
|
1478
|
+
editor.bold(oLayoutInfo.editable());
|
1479
|
+
} else if (bCmd && keyCode === key.I) {
|
1480
|
+
editor.italic(oLayoutInfo.editable());
|
1481
|
+
} else if (bCmd && keyCode === key.U) {
|
1482
|
+
editor.underline(oLayoutInfo.editable());
|
1483
|
+
} else if (bCmd && bShift && keyCode === key.S) {
|
1484
|
+
editor.strikethrough(oLayoutInfo.editable());
|
1485
|
+
} else if (bCmd && keyCode === key.BACKSLACH) {
|
1486
|
+
editor.removeFormat(oLayoutInfo.editable());
|
1487
|
+
} else if (bCmd && keyCode === key.K) {
|
1488
|
+
oLayoutInfo.editable();
|
1489
|
+
editor.setLinkDialog(oLayoutInfo.editable(), function (linkInfo, cb) {
|
1490
|
+
dialog.showLinkDialog(oLayoutInfo.dialog(), linkInfo, cb);
|
1491
|
+
});
|
1492
|
+
} else if (bCmd && keyCode === key.SLASH) {
|
1493
|
+
dialog.showHelpDialog(oLayoutInfo.dialog());
|
1494
|
+
} else if (bCmd && bShift && keyCode === key.L) {
|
1495
|
+
editor.justifyLeft(oLayoutInfo.editable());
|
1496
|
+
} else if (bCmd && bShift && keyCode === key.E) {
|
1497
|
+
editor.justifyCenter(oLayoutInfo.editable());
|
1498
|
+
} else if (bCmd && bShift && keyCode === key.R) {
|
1499
|
+
editor.justifyRight(oLayoutInfo.editable());
|
1500
|
+
} else if (bCmd && bShift && keyCode === key.J) {
|
1501
|
+
editor.justifyFull(oLayoutInfo.editable());
|
1502
|
+
} else if (bCmd && bShift && keyCode === key.NUM7) {
|
1503
|
+
editor.insertUnorderedList(oLayoutInfo.editable());
|
1504
|
+
} else if (bCmd && bShift && keyCode === key.NUM8) {
|
1505
|
+
editor.insertOrderedList(oLayoutInfo.editable());
|
1506
|
+
} else if (bCmd && keyCode === key.LEFTBRACKET) {
|
1507
|
+
editor.outdent(oLayoutInfo.editable());
|
1508
|
+
} else if (bCmd && keyCode === key.RIGHTBRACKET) {
|
1509
|
+
editor.indent(oLayoutInfo.editable());
|
1510
|
+
} else if (bCmd && keyCode === key.NUM0) { // formatBlock Paragraph
|
1511
|
+
editor.formatBlock(oLayoutInfo.editable(), 'P');
|
1512
|
+
} else if (bCmd && (key.NUM1 <= keyCode && keyCode <= key.NUM6)) {
|
1513
|
+
var sHeading = 'H' + String.fromCharCode(keyCode); // H1~H6
|
1514
|
+
editor.formatBlock(oLayoutInfo.editable(), sHeading);
|
1515
|
+
} else if (bCmd && keyCode === key.ENTER) {
|
1516
|
+
editor.insertHorizontalRule(oLayoutInfo.editable());
|
1517
|
+
} else {
|
1518
|
+
if (keyCode === key.BACKSPACE || keyCode === key.ENTER ||
|
1519
|
+
keyCode === key.SPACE) {
|
1520
|
+
editor.recordUndo(makeLayoutInfo(event.target).editable());
|
1521
|
+
}
|
1522
|
+
return; // not matched
|
1523
|
+
}
|
1524
|
+
event.preventDefault(); //prevent default event for FF
|
1525
|
+
};
|
1526
|
+
|
1527
|
+
var insertImages = function ($editable, files) {
|
1528
|
+
var callbacks = $editable.data('callbacks');
|
1529
|
+
editor.restoreRange($editable);
|
1530
|
+
if (callbacks.onImageUpload) { // call custom handler
|
1531
|
+
callbacks.onImageUpload(files, editor, $editable);
|
1532
|
+
} else {
|
1533
|
+
$.each(files, function (idx, file) {
|
1534
|
+
async.readFile(file).done(function (sURL) {
|
1535
|
+
editor.insertImage($editable, sURL);
|
1536
|
+
}).fail(function () {
|
1537
|
+
if (callbacks.onImageUploadError) {
|
1538
|
+
callbacks.onImageUploadError();
|
1539
|
+
}
|
1540
|
+
});
|
1541
|
+
});
|
1542
|
+
}
|
1543
|
+
};
|
1544
|
+
|
1545
|
+
var hDropImage = function (event) {
|
1546
|
+
var dataTransfer = event.originalEvent.dataTransfer;
|
1547
|
+
if (dataTransfer && dataTransfer.files) {
|
1548
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
1549
|
+
oLayoutInfo.editable().focus();
|
1550
|
+
insertImages(oLayoutInfo.editable(), dataTransfer.files);
|
1551
|
+
}
|
1552
|
+
event.preventDefault();
|
1553
|
+
};
|
1554
|
+
|
1555
|
+
var hMousedown = function (event) {
|
1556
|
+
//preventDefault Selection for FF, IE8+
|
1557
|
+
if (dom.isImg(event.target)) { event.preventDefault(); }
|
1558
|
+
};
|
1559
|
+
|
1560
|
+
var hToolbarAndPopoverUpdate = function (event) {
|
1561
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
1562
|
+
var oStyle = editor.currentStyle(event.target);
|
1563
|
+
if (!oStyle) { return; }
|
1564
|
+
toolbar.update(oLayoutInfo.toolbar(), oStyle);
|
1565
|
+
popover.update(oLayoutInfo.popover(), oStyle);
|
1566
|
+
handle.update(oLayoutInfo.handle(), oStyle);
|
1567
|
+
};
|
1568
|
+
|
1569
|
+
var hScroll = function (event) {
|
1570
|
+
var oLayoutInfo = makeLayoutInfo(event.currentTarget || event.target);
|
1571
|
+
//hide popover and handle when scrolled
|
1572
|
+
popover.hide(oLayoutInfo.popover());
|
1573
|
+
handle.hide(oLayoutInfo.handle());
|
1574
|
+
};
|
1575
|
+
|
1576
|
+
var hHandleMousedown = function (event) {
|
1577
|
+
if (dom.isControlSizing(event.target)) {
|
1578
|
+
var oLayoutInfo = makeLayoutInfo(event.target),
|
1579
|
+
$handle = oLayoutInfo.handle(), $popover = oLayoutInfo.popover(),
|
1580
|
+
$editable = oLayoutInfo.editable(), $editor = oLayoutInfo.editor();
|
1581
|
+
|
1582
|
+
var elTarget = $handle.find('.editor-control-selection').data('target'),
|
1583
|
+
$target = $(elTarget);
|
1584
|
+
var posStart = $target.offset(),
|
1585
|
+
scrollTop = $(document).scrollTop(), posDistance;
|
1586
|
+
|
1587
|
+
$editor.on('mousemove', function (event) {
|
1588
|
+
posDistance = {x: event.clientX - posStart.left,
|
1589
|
+
y: event.clientY - (posStart.top - scrollTop)};
|
1590
|
+
editor.resizeTo(posDistance, $target);
|
1591
|
+
handle.update($handle, {image: elTarget});
|
1592
|
+
popover.update($popover, {image: elTarget});
|
1593
|
+
}).on('mouseup', function () {
|
1594
|
+
$editor.off('mousemove').off('mouseup');
|
1595
|
+
});
|
1596
|
+
|
1597
|
+
if (!$target.data('ratio')) { // original ratio.
|
1598
|
+
$target.data('ratio', $target.height() / $target.width());
|
1599
|
+
}
|
1600
|
+
|
1601
|
+
editor.recordUndo($editable);
|
1602
|
+
event.stopPropagation();
|
1603
|
+
event.preventDefault();
|
1604
|
+
}
|
1605
|
+
};
|
1606
|
+
|
1607
|
+
var hToolbarAndPopoverMousedown = function (event) {
|
1608
|
+
// prevent default event when insertTable (FF, Webkit)
|
1609
|
+
var $btn = $(event.target).closest('[data-event]');
|
1610
|
+
if ($btn.length > 0) { event.preventDefault(); }
|
1611
|
+
};
|
1612
|
+
|
1613
|
+
var hToolbarAndPopoverClick = function (event) {
|
1614
|
+
var $btn = $(event.target).closest('[data-event]');
|
1615
|
+
|
1616
|
+
if ($btn.length > 0) {
|
1617
|
+
var sEvent = $btn.attr('data-event'),
|
1618
|
+
sValue = $btn.attr('data-value');
|
1619
|
+
|
1620
|
+
var oLayoutInfo = makeLayoutInfo(event.target);
|
1621
|
+
var $editor = oLayoutInfo.editor(),
|
1622
|
+
$toolbar = oLayoutInfo.toolbar(),
|
1623
|
+
$dialog = oLayoutInfo.dialog(),
|
1624
|
+
$editable = oLayoutInfo.editable(),
|
1625
|
+
$codable = oLayoutInfo.codable();
|
1626
|
+
|
1627
|
+
// before command
|
1628
|
+
var elTarget;
|
1629
|
+
if ($.inArray(sEvent, ['resize', 'floatMe']) !== -1) {
|
1630
|
+
var $handle = oLayoutInfo.handle();
|
1631
|
+
var $selection = $handle.find('.editor-control-selection');
|
1632
|
+
elTarget = $selection.data('target');
|
1633
|
+
}
|
1634
|
+
|
1635
|
+
if (editor[sEvent]) { // on command
|
1636
|
+
$editable.trigger('focus');
|
1637
|
+
editor[sEvent]($editable, sValue, elTarget);
|
1638
|
+
}
|
1639
|
+
|
1640
|
+
// after command
|
1641
|
+
if ($.inArray(sEvent, ['backColor', 'foreColor']) !== -1) {
|
1642
|
+
toolbar.updateRecentColor($btn[0], sEvent, sValue);
|
1643
|
+
} else if (sEvent === 'showLinkDialog') { // popover to dialog
|
1644
|
+
$editable.focus();
|
1645
|
+
editor.setLinkDialog($editable, function (linkInfo, cb) {
|
1646
|
+
dialog.showLinkDialog($dialog, linkInfo, cb);
|
1647
|
+
});
|
1648
|
+
} else if (sEvent === 'showImageDialog') {
|
1649
|
+
$editable.focus();
|
1650
|
+
dialog.showImageDialog($dialog, function (files) {
|
1651
|
+
insertImages($editable, files);
|
1652
|
+
}, function (sUrl) {
|
1653
|
+
editor.restoreRange($editable);
|
1654
|
+
editor.insertImage($editable, sUrl);
|
1655
|
+
});
|
1656
|
+
} else if (sEvent === 'showVideoDialog') {
|
1657
|
+
$editable.focus();
|
1658
|
+
editor.setVideoDialog($editable, function (linkInfo, cb) {
|
1659
|
+
dialog.showVideoDialog($dialog, linkInfo, cb);
|
1660
|
+
});
|
1661
|
+
} else if (sEvent === 'showHelpDialog') {
|
1662
|
+
dialog.showHelpDialog($dialog);
|
1663
|
+
} else if (sEvent === 'fullscreen') {
|
1664
|
+
$editor.toggleClass('fullscreen');
|
1665
|
+
|
1666
|
+
var hResizeFullscreen = function () {
|
1667
|
+
var nHeight = $(window).height() - $toolbar.outerHeight();
|
1668
|
+
$editable.css('height', nHeight);
|
1669
|
+
};
|
1670
|
+
|
1671
|
+
var bFullscreen = $editor.hasClass('fullscreen');
|
1672
|
+
if (bFullscreen) {
|
1673
|
+
$editable.data('orgHeight', $editable.css('height'));
|
1674
|
+
$(window).resize(hResizeFullscreen).trigger('resize');
|
1675
|
+
} else {
|
1676
|
+
var hasOptionHeight = !!$editable.data('optionHeight');
|
1677
|
+
$editable.css('height', hasOptionHeight ? $editable.data('orgHeight') : 'auto');
|
1678
|
+
$(window).off('resize');
|
1679
|
+
}
|
1680
|
+
|
1681
|
+
toolbar.updateFullscreen($toolbar, bFullscreen);
|
1682
|
+
} else if (sEvent === 'codeview') {
|
1683
|
+
$editor.toggleClass('codeview');
|
1684
|
+
|
1685
|
+
var bCodeview = $editor.hasClass('codeview');
|
1686
|
+
if (bCodeview) {
|
1687
|
+
$codable.val($editable.html());
|
1688
|
+
$codable.height($editable.height());
|
1689
|
+
toolbar.disable($toolbar);
|
1690
|
+
$codable.focus();
|
1691
|
+
|
1692
|
+
// activate CodeMirror as codable
|
1693
|
+
if (agent.bCodeMirror) {
|
1694
|
+
var cmEditor = CodeMirror.fromTextArea($codable[0], $.extend({
|
1695
|
+
mode: 'text/html',
|
1696
|
+
lineNumbers: true
|
1697
|
+
}, $editor.data('options').codemirror));
|
1698
|
+
// CodeMirror hasn't Padding.
|
1699
|
+
cmEditor.setSize(null, $editable.outerHeight());
|
1700
|
+
// autoFormatRange If formatting included
|
1701
|
+
if (cmEditor.autoFormatRange) {
|
1702
|
+
cmEditor.autoFormatRange({line: 0, ch: 0}, {
|
1703
|
+
line: cmEditor.lineCount(),
|
1704
|
+
ch: cmEditor.getTextArea().value.length
|
1705
|
+
});
|
1706
|
+
}
|
1707
|
+
$codable.data('cmEditor', cmEditor);
|
1708
|
+
}
|
1709
|
+
} else {
|
1710
|
+
// deactivate CodeMirror as codable
|
1711
|
+
if (agent.bCodeMirror) {
|
1712
|
+
$codable.data('cmEditor').toTextArea();
|
1713
|
+
}
|
1714
|
+
|
1715
|
+
$editable.html($codable.val() || dom.emptyPara);
|
1716
|
+
$editable.height($editable.data('optionHeight') ? $codable.height() : 'auto');
|
1717
|
+
|
1718
|
+
toolbar.enable($toolbar);
|
1719
|
+
$editable.focus();
|
1720
|
+
}
|
1721
|
+
|
1722
|
+
toolbar.updateCodeview(oLayoutInfo.toolbar(), bCodeview);
|
1723
|
+
}
|
1724
|
+
|
1725
|
+
hToolbarAndPopoverUpdate(event);
|
1726
|
+
}
|
1727
|
+
};
|
1728
|
+
|
1729
|
+
var EDITABLE_PADDING = 24;
|
1730
|
+
var hStatusbarMousedown = function (event) {
|
1731
|
+
var $document = $(document);
|
1732
|
+
var oLayoutInfo = makeLayoutInfo(event.target);
|
1733
|
+
var $editable = oLayoutInfo.editable();
|
1734
|
+
|
1735
|
+
var nEditableTop = $editable.offset().top - $document.scrollTop();
|
1736
|
+
var hMousemove = function (event) {
|
1737
|
+
$editable.height(event.clientY - (nEditableTop + EDITABLE_PADDING));
|
1738
|
+
};
|
1739
|
+
var hMouseup = function () {
|
1740
|
+
$document.unbind('mousemove', hMousemove)
|
1741
|
+
.unbind('mouseup', hMouseup);
|
1742
|
+
};
|
1743
|
+
$document.mousemove(hMousemove).mouseup(hMouseup);
|
1744
|
+
event.stopPropagation();
|
1745
|
+
event.preventDefault();
|
1746
|
+
};
|
1747
|
+
|
1748
|
+
var PX_PER_EM = 18;
|
1749
|
+
var hDimensionPickerMove = function (event) {
|
1750
|
+
var $picker = $(event.target.parentNode); // target is mousecatcher
|
1751
|
+
var $dimensionDisplay = $picker.next();
|
1752
|
+
var $catcher = $picker.find('.editor-dimension-picker-mousecatcher');
|
1753
|
+
var $highlighted = $picker.find('.editor-dimension-picker-highlighted');
|
1754
|
+
var $unhighlighted = $picker.find('.editor-dimension-picker-unhighlighted');
|
1755
|
+
var posOffset;
|
1756
|
+
if (event.offsetX === undefined) {
|
1757
|
+
// HTML5 with jQuery - e.offsetX is undefined in Firefox
|
1758
|
+
var posCatcher = $(event.target).offset();
|
1759
|
+
posOffset = {x: event.pageX - posCatcher.left,
|
1760
|
+
y: event.pageY - posCatcher.top};
|
1761
|
+
} else {
|
1762
|
+
posOffset = {x: event.offsetX, y: event.offsetY};
|
1763
|
+
}
|
1764
|
+
|
1765
|
+
var dim = {c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
|
1766
|
+
r: Math.ceil(posOffset.y / PX_PER_EM) || 1};
|
1767
|
+
|
1768
|
+
$highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
|
1769
|
+
$catcher.attr('data-value', dim.c + 'x' + dim.r);
|
1770
|
+
|
1771
|
+
if (3 < dim.c && dim.c < 10) { // 5~10
|
1772
|
+
$unhighlighted.css({ width: dim.c + 1 + 'em'});
|
1773
|
+
}
|
1774
|
+
|
1775
|
+
if (3 < dim.r && dim.r < 10) { // 5~10
|
1776
|
+
$unhighlighted.css({ height: dim.r + 1 + 'em'});
|
1777
|
+
}
|
1778
|
+
|
1779
|
+
$dimensionDisplay.html(dim.c + ' x ' + dim.r);
|
1780
|
+
};
|
1781
|
+
|
1782
|
+
/**
|
1783
|
+
* attach Drag and Drop Events
|
1784
|
+
* @param oLayoutInfo {object} - layout Informations
|
1785
|
+
*/
|
1786
|
+
var attachDragAndDropEvent = function (oLayoutInfo) {
|
1787
|
+
var collection = $(), $dropzone = oLayoutInfo.dropzone,
|
1788
|
+
$dropzoneMessage = oLayoutInfo.dropzone.find('.editor-dropzone-message');
|
1789
|
+
|
1790
|
+
// show dropzone on dragenter when dragging a object to document.
|
1791
|
+
$(document).on('dragenter', function (e) {
|
1792
|
+
var bCodeview = oLayoutInfo.editor.hasClass('codeview');
|
1793
|
+
if (!bCodeview && collection.length === 0) {
|
1794
|
+
oLayoutInfo.editor.addClass('dragover');
|
1795
|
+
$dropzone.width(oLayoutInfo.editor.width());
|
1796
|
+
$dropzone.height(oLayoutInfo.editor.height());
|
1797
|
+
$dropzoneMessage.text('Drag Image Here');
|
1798
|
+
}
|
1799
|
+
collection = collection.add(e.target);
|
1800
|
+
}).on('dragleave', function (e) {
|
1801
|
+
collection = collection.not(e.target);
|
1802
|
+
if (collection.length === 0) {
|
1803
|
+
oLayoutInfo.editor.removeClass('dragover');
|
1804
|
+
}
|
1805
|
+
}).on('drop', function () {
|
1806
|
+
collection = $();
|
1807
|
+
oLayoutInfo.editor.removeClass('dragover');
|
1808
|
+
});
|
1809
|
+
|
1810
|
+
// change dropzone's message on hover.
|
1811
|
+
$dropzone.on('dragenter', function () {
|
1812
|
+
$dropzone.addClass('hover');
|
1813
|
+
$dropzoneMessage.text('Drop Image');
|
1814
|
+
}).on('dragleave', function () {
|
1815
|
+
$dropzone.removeClass('hover');
|
1816
|
+
$dropzoneMessage.text('Drag Image Here');
|
1817
|
+
});
|
1818
|
+
|
1819
|
+
// attach dropImage
|
1820
|
+
$dropzone.on('drop', function (e) {
|
1821
|
+
hDropImage(e);
|
1822
|
+
}).on('dragover', false); // prevent default dragover event
|
1823
|
+
};
|
1824
|
+
|
1825
|
+
/**
|
1826
|
+
* Attach eventhandler
|
1827
|
+
* @param {object} oLayoutInfo - layout Informations
|
1828
|
+
* @param {object} options - user options include custom event handlers
|
1829
|
+
* @param {function} options.enter - enter key handler
|
1830
|
+
*/
|
1831
|
+
this.attach = function (oLayoutInfo, options) {
|
1832
|
+
oLayoutInfo.editable.on('keydown', hKeydown);
|
1833
|
+
oLayoutInfo.editable.on('mousedown', hMousedown);
|
1834
|
+
oLayoutInfo.editable.on('keyup mouseup', hToolbarAndPopoverUpdate);
|
1835
|
+
oLayoutInfo.editable.on('scroll', hScroll);
|
1836
|
+
|
1837
|
+
attachDragAndDropEvent(oLayoutInfo);
|
1838
|
+
|
1839
|
+
oLayoutInfo.handle.on('mousedown', hHandleMousedown);
|
1840
|
+
oLayoutInfo.toolbar.on('click', hToolbarAndPopoverClick);
|
1841
|
+
oLayoutInfo.popover.on('click', hToolbarAndPopoverClick);
|
1842
|
+
oLayoutInfo.toolbar.on('mousedown', hToolbarAndPopoverMousedown);
|
1843
|
+
oLayoutInfo.popover.on('mousedown', hToolbarAndPopoverMousedown);
|
1844
|
+
oLayoutInfo.statusbar.on('mousedown', hStatusbarMousedown);
|
1845
|
+
|
1846
|
+
//toolbar table dimension
|
1847
|
+
var $toolbar = oLayoutInfo.toolbar;
|
1848
|
+
var $catcher = $toolbar.find('.editor-dimension-picker-mousecatcher');
|
1849
|
+
$catcher.on('mousemove', hDimensionPickerMove);
|
1850
|
+
|
1851
|
+
// save selection when focusout
|
1852
|
+
oLayoutInfo.editable.on('blur', function () {
|
1853
|
+
editor.saveRange(oLayoutInfo.editable);
|
1854
|
+
});
|
1855
|
+
|
1856
|
+
// basic event callbacks (lowercase)
|
1857
|
+
// enter, focus, blur, keyup, keydown
|
1858
|
+
if (options.onenter) {
|
1859
|
+
oLayoutInfo.editable.keypress(function (event) {
|
1860
|
+
if (event.keyCode === key.ENTER) { options.onenter(event); }
|
1861
|
+
});
|
1862
|
+
}
|
1863
|
+
if (options.onfocus) { oLayoutInfo.editable.focus(options.onfocus); }
|
1864
|
+
if (options.onblur) { oLayoutInfo.editable.blur(options.onblur); }
|
1865
|
+
if (options.onkeyup) { oLayoutInfo.editable.keyup(options.onkeyup); }
|
1866
|
+
if (options.onkeydown) { oLayoutInfo.editable.keydown(options.onkeydown); }
|
1867
|
+
|
1868
|
+
// callbacks for advanced features (camel)
|
1869
|
+
// All editor status will be saved on editable with jquery's data
|
1870
|
+
// for support multiple editor with singleton object.
|
1871
|
+
oLayoutInfo.editable.data('callbacks', {
|
1872
|
+
onChange: options.onChange,
|
1873
|
+
onAutoSave: options.onAutoSave,
|
1874
|
+
onPasteBefore: options.onPasteBefore,
|
1875
|
+
onPasteAfter: options.onPasteAfter,
|
1876
|
+
onImageUpload: options.onImageUpload,
|
1877
|
+
onImageUploadError: options.onImageUpload,
|
1878
|
+
onFileUpload: options.onFileUpload,
|
1879
|
+
onFileUploadError: options.onFileUpload
|
1880
|
+
});
|
1881
|
+
};
|
1882
|
+
|
1883
|
+
this.dettach = function (oLayoutInfo) {
|
1884
|
+
oLayoutInfo.editable.off();
|
1885
|
+
oLayoutInfo.toolbar.off();
|
1886
|
+
oLayoutInfo.handle.off();
|
1887
|
+
oLayoutInfo.popover.off();
|
1888
|
+
};
|
1889
|
+
};
|
1890
|
+
|
1891
|
+
/**
|
1892
|
+
* Renderer
|
1893
|
+
*
|
1894
|
+
* rendering toolbar and editable
|
1895
|
+
*/
|
1896
|
+
var Renderer = function () {
|
1897
|
+
var tplToolbarInfo, tplPopover, tplhandle, tplDialog, tplStatusbar;
|
1898
|
+
|
1899
|
+
/* jshint ignore:start */
|
1900
|
+
tplToolbarInfo = {
|
1901
|
+
picture: function (lang) {
|
1902
|
+
return '<button type="button" class="btn btn-small" title="' + lang.image.image + '" data-event="showImageDialog" tabindex="-1"><i class="fa fa-picture-o"></i></button>';
|
1903
|
+
},
|
1904
|
+
link: function (lang) {
|
1905
|
+
return '<button type="button" class="btn btn-small" title="' + lang.link.link + '" data-event="showLinkDialog" data-shortcut="Ctrl+K" data-mac-shortcut="⌘+K" tabindex="-1"><i class="fa fa-link"></i></button>';
|
1906
|
+
},
|
1907
|
+
video: function (lang) {
|
1908
|
+
return '<button type="button" class="btn btn-small" title="' + lang.video.video + '" data-event="showVideoDialog" tabindex="-1"><i class="fa fa-video-camera"></i></button>';
|
1909
|
+
},
|
1910
|
+
table: function (lang) {
|
1911
|
+
return '<button type="button" class="btn btn-small dropdown-toggle" title="' + lang.table.table + '" data-toggle="dropdown" tabindex="-1"><i class="fa fa-table"></i> <i class="fa fa-caret-down"></i></button>' +
|
1912
|
+
'<ul class="dropdown-menu">' +
|
1913
|
+
'<div class="editor-dimension-picker">' +
|
1914
|
+
'<div class="editor-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"></div>' +
|
1915
|
+
'<div class="editor-dimension-picker-highlighted"></div>' +
|
1916
|
+
'<div class="editor-dimension-picker-unhighlighted"></div>' +
|
1917
|
+
'</div>' +
|
1918
|
+
'<div class="editor-dimension-display"> 1 x 1 </div>' +
|
1919
|
+
'</ul>';
|
1920
|
+
},
|
1921
|
+
style: function (lang) {
|
1922
|
+
return '<button type="button" class="btn btn-small dropdown-toggle" title="' + lang.style.style + '" data-toggle="dropdown" tabindex="-1"><i class="fa fa-magic"></i> <i class="fa fa-caret-down"></i></button>' +
|
1923
|
+
'<ul class="dropdown-menu">' +
|
1924
|
+
'<li><a data-event="formatBlock" data-value="p">' + lang.style.normal + '</a></li>' +
|
1925
|
+
'<li><a data-event="formatBlock" data-value="blockquote"><blockquote>' + lang.style.blockquote + '</blockquote></a></li>' +
|
1926
|
+
'<li><a data-event="formatBlock" data-value="pre"><code>' + lang.style.pre + '</code></a></li>' +
|
1927
|
+
'<li><a data-event="formatBlock" data-value="h1"><h1>' + lang.style.h1 + '</h1></a></li>' +
|
1928
|
+
'<li><a data-event="formatBlock" data-value="h2"><h2>' + lang.style.h2 + '</h2></a></li>' +
|
1929
|
+
'<li><a data-event="formatBlock" data-value="h3"><h3>' + lang.style.h3 + '</h3></a></li>' +
|
1930
|
+
'<li><a data-event="formatBlock" data-value="h4"><h4>' + lang.style.h4 + '</h4></a></li>' +
|
1931
|
+
'<li><a data-event="formatBlock" data-value="h5"><h5>' + lang.style.h5 + '</h5></a></li>' +
|
1932
|
+
'<li><a data-event="formatBlock" data-value="h6"><h6>' + lang.style.h6 + '</h6></a></li>' +
|
1933
|
+
'</ul>';
|
1934
|
+
},
|
1935
|
+
fontsize: function (lang) {
|
1936
|
+
return '<button type="button" class="btn btn-small dropdown-toggle" data-toggle="dropdown" title="' + lang.font.size + '" tabindex="-1"><span class="editor-current-fontsize">16</span> <i class="fa fa-caret-down"></i></button>' +
|
1937
|
+
'<ul class="dropdown-menu">' +
|
1938
|
+
'<li><a data-event="fontSize" data-value="8"><i class="fa fa-check"></i> 8</a></li>' +
|
1939
|
+
'<li><a data-event="fontSize" data-value="9"><i class="fa fa-check"></i> 9</a></li>' +
|
1940
|
+
'<li><a data-event="fontSize" data-value="10"><i class="fa fa-check"></i> 10</a></li>' +
|
1941
|
+
'<li><a data-event="fontSize" data-value="11"><i class="fa fa-check"></i> 11</a></li>' +
|
1942
|
+
'<li><a data-event="fontSize" data-value="12"><i class="fa fa-check"></i> 12</a></li>' +
|
1943
|
+
'<li><a data-event="fontSize" data-value="16"><i class="fa fa-check"></i> 16</a></li>' +
|
1944
|
+
'<li><a data-event="fontSize" data-value="14"><i class="fa fa-check"></i> 14</a></li>' +
|
1945
|
+
'<li><a data-event="fontSize" data-value="18"><i class="fa fa-check"></i> 18</a></li>' +
|
1946
|
+
'<li><a data-event="fontSize" data-value="24"><i class="fa fa-check"></i> 24</a></li>' +
|
1947
|
+
'<li><a data-event="fontSize" data-value="36"><i class="fa fa-check"></i> 36</a></li>' +
|
1948
|
+
'</ul>';
|
1949
|
+
},
|
1950
|
+
color: function (lang) {
|
1951
|
+
return '<button type="button" class="btn btn-small editor-recent-color" title="' + lang.color.recent + '" data-event="color" data-value=\'{"backColor":"orange"}\' tabindex="-1"><i class="fa fa-font" style="background-color:orange;"></i></button>' +
|
1952
|
+
'<button type="button" class="btn btn-small dropdown-toggle" title="' + lang.color.more + '" data-toggle="dropdown" tabindex="-1">' +
|
1953
|
+
'<i class="fa fa-caret-down"></i>' +
|
1954
|
+
'</button>' +
|
1955
|
+
'<ul class="dropdown-menu">' +
|
1956
|
+
'<li>' +
|
1957
|
+
'<div class="btn-group">' +
|
1958
|
+
'<div class="editor-palette-title">' + lang.color.background + '</div>' +
|
1959
|
+
'<div class="editor-color-reset" data-event="backColor" data-value="inherit" title="' + lang.color.transparent + '">' + lang.color.setTransparent + '</div>' +
|
1960
|
+
'<div class="editor-color-palette" data-target-event="backColor"></div>' +
|
1961
|
+
'</div>' +
|
1962
|
+
'<div class="btn-group">' +
|
1963
|
+
'<div class="editor-palette-title">' + lang.color.foreground + '</div>' +
|
1964
|
+
'<div class="editor-color-reset" data-event="foreColor" data-value="inherit" title="' + lang.color.reset + '">' + lang.color.resetToDefault + '</div>' +
|
1965
|
+
'<div class="editor-color-palette" data-target-event="foreColor"></div>' +
|
1966
|
+
'</div>' +
|
1967
|
+
'</li>' +
|
1968
|
+
'</ul>';
|
1969
|
+
},
|
1970
|
+
bold: function (lang) {
|
1971
|
+
return '<button type="button" class="btn btn-small" title="' + lang.font.bold + '" data-shortcut="Ctrl+B" data-mac-shortcut="⌘+B" data-event="bold" tabindex="-1"><i class="fa fa-bold"></i></button>';
|
1972
|
+
},
|
1973
|
+
italic: function (lang) {
|
1974
|
+
return '<button type="button" class="btn btn-small" title="' + lang.font.italic + '" data-shortcut="Ctrl+I" data-mac-shortcut="⌘+I" data-event="italic" tabindex="-1"><i class="fa fa-italic"></i></button>';
|
1975
|
+
},
|
1976
|
+
underline: function (lang) {
|
1977
|
+
return '<button type="button" class="btn btn-small" title="' + lang.font.underline + '" data-shortcut="Ctrl+U" data-mac-shortcut="⌘+U" data-event="underline" tabindex="-1"><i class="fa fa-underline"></i></button>';
|
1978
|
+
},
|
1979
|
+
clear: function (lang) {
|
1980
|
+
return '<button type="button" class="btn btn-small" title="' + lang.font.clear + '" data-shortcut="Ctrl+\\" data-mac-shortcut="⌘+\\" data-event="removeFormat" tabindex="-1"><i class="fa fa-eraser"></i></button>';
|
1981
|
+
},
|
1982
|
+
ul: function (lang) {
|
1983
|
+
return '<button type="button" class="btn btn-small" title="' + lang.lists.unordered + '" data-shortcut="Ctrl+Shift+8" data-mac-shortcut="⌘+⇧+7" data-event="insertUnorderedList" tabindex="-1"><i class="fa fa-list-ul"></i></button>';
|
1984
|
+
},
|
1985
|
+
ol: function (lang) {
|
1986
|
+
return '<button type="button" class="btn btn-small" title="' + lang.lists.ordered + '" data-shortcut="Ctrl+Shift+7" data-mac-shortcut="⌘+⇧+8" data-event="insertOrderedList" tabindex="-1"><i class="fa fa-list-ol"></i></button>';
|
1987
|
+
},
|
1988
|
+
paragraph: function (lang) {
|
1989
|
+
return '<button type="button" class="btn btn-small dropdown-toggle" title="' + lang.paragraph.paragraph + '" data-toggle="dropdown" tabindex="-1"><i class="fa fa-align-left"></i> <i class="fa fa-caret-down"></i></button>' +
|
1990
|
+
'<ul class="dropdown-menu">' +
|
1991
|
+
'<li>' +
|
1992
|
+
'<div class="editor-align btn-group">' +
|
1993
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.left + '" data-shortcut="Ctrl+Shift+L" data-mac-shortcut="⌘+⇧+L" data-event="justifyLeft" tabindex="-1"><i class="fa fa-align-left"></i></button>' +
|
1994
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.center + '" data-shortcut="Ctrl+Shift+E" data-mac-shortcut="⌘+⇧+E" data-event="justifyCenter" tabindex="-1"><i class="fa fa-align-center"></i></button>' +
|
1995
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.right + '" data-shortcut="Ctrl+Shift+R" data-mac-shortcut="⌘+⇧+R" data-event="justifyRight" tabindex="-1"><i class="fa fa-align-right"></i></button>' +
|
1996
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.justify + '" data-shortcut="Ctrl+Shift+J" data-mac-shortcut="⌘+⇧+J" data-event="justifyFull" tabindex="-1"><i class="fa fa-align-justify"></i></button>' +
|
1997
|
+
'</div>' +
|
1998
|
+
'</li>' +
|
1999
|
+
'<li>' +
|
2000
|
+
'<div class="editor-list btn-group">' +
|
2001
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.outdent + '" data-shortcut="Ctrl+[" data-mac-shortcut="⌘+[" data-event="outdent" tabindex="-1"><i class="fa fa-outdent"></i></button>' +
|
2002
|
+
'<button type="button" class="btn btn-mini" title="' + lang.paragraph.indent + '" data-shortcut="Ctrl+]" data-mac-shortcut="⌘+]" data-event="indent" tabindex="-1"><i class="fa fa-indent"></i></button>' +
|
2003
|
+
'</div>' +
|
2004
|
+
'</li>' +
|
2005
|
+
'</ul>';
|
2006
|
+
},
|
2007
|
+
height: function (lang) {
|
2008
|
+
return '<button type="button" class="btn btn-small dropdown-toggle" data-toggle="dropdown" title="' + lang.font.height + '" tabindex="-1"><i class="fa fa-text-height"></i> <i class="fa fa-caret-down"></i></button>' +
|
2009
|
+
'<ul class="dropdown-menu">' +
|
2010
|
+
'<li><a data-event="lineHeight" data-value="1.0"><i class="fa fa-check"></i> 1.0</a></li>' +
|
2011
|
+
'<li><a data-event="lineHeight" data-value="1.2"><i class="fa fa-check"></i> 1.2</a></li>' +
|
2012
|
+
'<li><a data-event="lineHeight" data-value="1.4"><i class="fa fa-check"></i> 1.4</a></li>' +
|
2013
|
+
'<li><a data-event="lineHeight" data-value="1.5"><i class="fa fa-check"></i> 1.5</a></li>' +
|
2014
|
+
'<li><a data-event="lineHeight" data-value="1.6"><i class="fa fa-check"></i> 1.6</a></li>' +
|
2015
|
+
'<li><a data-event="lineHeight" data-value="1.8"><i class="fa fa-check"></i> 1.8</a></li>' +
|
2016
|
+
'<li><a data-event="lineHeight" data-value="2.0"><i class="fa fa-check"></i> 2.0</a></li>' +
|
2017
|
+
'<li><a data-event="lineHeight" data-value="3.0"><i class="fa fa-check"></i> 3.0</a></li>' +
|
2018
|
+
'</ul>';
|
2019
|
+
},
|
2020
|
+
help: function (lang) {
|
2021
|
+
return '<button type="button" class="btn btn-small" title="' + lang.options.help + '" data-shortcut="Ctrl+/" data-mac-shortcut="⌘+/" data-event="showHelpDialog" tabindex="-1"><i class="fa fa-question"></i></button>';
|
2022
|
+
},
|
2023
|
+
fullscreen: function (lang) {
|
2024
|
+
return '<button type="button" class="btn btn-small" title="' + lang.options.fullscreen + '" data-event="fullscreen" tabindex="-1"><i class="fa fa-arrows-alt"></i></button>';
|
2025
|
+
},
|
2026
|
+
codeview: function (lang) {
|
2027
|
+
return '<button type="button" class="btn btn-small" title="' + lang.options.codeview + '" data-event="codeview" tabindex="-1"><i class="fa fa-code"></i></button>';
|
2028
|
+
}
|
2029
|
+
};
|
2030
|
+
tplPopover = function (lang) {
|
2031
|
+
return '<div class="editor-popover">' +
|
2032
|
+
'<div class="editor-link-popover popover bottom in" style="display: none;">' +
|
2033
|
+
'<div class="arrow"></div>' +
|
2034
|
+
'<div class="popover-content editor-link-content">' +
|
2035
|
+
'<a href="http://www.google.com" target="_blank">www.google.com</a> ' +
|
2036
|
+
'<div class="editor-insert btn-group">' +
|
2037
|
+
'<button type="button" class="btn btn-mini" title="' + lang.link.edit + '" data-event="showLinkDialog" tabindex="-1"><i class="fa fa-edit"></i></button>' +
|
2038
|
+
'<button type="button" class="btn btn-mini" title="' + lang.link.unlink + '" data-event="unlink" tabindex="-1"><i class="fa fa-unlink"></i></button>' +
|
2039
|
+
'<button type="button" class="btn btn-mini" title="' + lang.video.videoLink + '" data-event="showVideoDialog" tabindex="-1"><i class="fa fa-video-camera"></i></button>' +
|
2040
|
+
'</div>' +
|
2041
|
+
'</div>' +
|
2042
|
+
'</div>' +
|
2043
|
+
'<div class="editor-image-popover popover bottom in" style="display: none;">' +
|
2044
|
+
'<div class="arrow"></div>' +
|
2045
|
+
'<div class="popover-content editor-image-content">' +
|
2046
|
+
'<div class="btn-group">' +
|
2047
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.resizeFull + '" data-event="resize" data-value="1" tabindex="-1"><span class="editor-fontsize-10">100%</span> </button>' +
|
2048
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.resizeHalf + '" data-event="resize" data-value="0.5" tabindex="-1"><span class="editor-fontsize-10">50%</span> </button>' +
|
2049
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.resizeQuarter + '" data-event="resize" data-value="0.25" tabindex="-1"><span class="editor-fontsize-10">25%</span> </button>' +
|
2050
|
+
'</div>' +
|
2051
|
+
'<div class="btn-group">' +
|
2052
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.floatLeft + '" data-event="floatMe" data-value="left" tabindex="-1"><i class="fa fa-align-left"></i></button>' +
|
2053
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.floatRight + '" data-event="floatMe" data-value="right" tabindex="-1"><i class="fa fa-align-right"></i></button>' +
|
2054
|
+
'<button type="button" class="btn btn-mini" title="' + lang.image.floatNone + '" data-event="floatMe" data-value="none" tabindex="-1"><i class="fa fa-align-justify"></i></button>' +
|
2055
|
+
'</div>' +
|
2056
|
+
'</div>' +
|
2057
|
+
'</div>' +
|
2058
|
+
'</div>';
|
2059
|
+
};
|
2060
|
+
|
2061
|
+
tplhandle = '<div class="editor-handle">' +
|
2062
|
+
'<div class="editor-control-selection">' +
|
2063
|
+
'<div class="editor-control-selection-bg"></div>' +
|
2064
|
+
'<div class="editor-control-holder editor-control-nw"></div>' +
|
2065
|
+
'<div class="editor-control-holder editor-control-ne"></div>' +
|
2066
|
+
'<div class="editor-control-holder editor-control-sw"></div>' +
|
2067
|
+
'<div class="editor-control-sizing editor-control-se"></div>' +
|
2068
|
+
'<div class="editor-control-selection-info"></div>' +
|
2069
|
+
'</div>' +
|
2070
|
+
'</div>';
|
2071
|
+
|
2072
|
+
var tplShortcutText = function (lang) {
|
2073
|
+
return '<table class="editor-shortcut">' +
|
2074
|
+
'<thead>' +
|
2075
|
+
'<tr><th></th><th>' + lang.shortcut.textFormatting + '</th></tr>' +
|
2076
|
+
'</thead>' +
|
2077
|
+
'<tbody>' +
|
2078
|
+
'<tr><td>⌘ + B</td><td>' + lang.font.bold + '</td></tr>' +
|
2079
|
+
'<tr><td>⌘ + I</td><td>' + lang.font.italic + '</td></tr>' +
|
2080
|
+
'<tr><td>⌘ + U</td><td>' + lang.font.underline + '</td></tr>' +
|
2081
|
+
'<tr><td>⌘ + ⇧ + S</td><td>' + lang.font.strike + '</td></tr>' +
|
2082
|
+
'<tr><td>⌘ + \\</td><td>' + lang.font.clear + '</td></tr>' +
|
2083
|
+
'</tr>' +
|
2084
|
+
'</tbody>' +
|
2085
|
+
'</table>';
|
2086
|
+
};
|
2087
|
+
|
2088
|
+
var tplShortcutAction = function (lang) {
|
2089
|
+
return '<table class="editor-shortcut">' +
|
2090
|
+
'<thead>' +
|
2091
|
+
'<tr><th></th><th>' + lang.shortcut.action + '</th></tr>' +
|
2092
|
+
'</thead>' +
|
2093
|
+
'<tbody>' +
|
2094
|
+
'<tr><td>⌘ + Z</td><td>' + lang.history.undo + '</td></tr>' +
|
2095
|
+
'<tr><td>⌘ + ⇧ + Z</td><td>' + lang.history.redo + '</td></tr>' +
|
2096
|
+
'<tr><td>⌘ + ]</td><td>' + lang.paragraph.indent + '</td></tr>' +
|
2097
|
+
'<tr><td>⌘ + [</td><td>' + lang.paragraph.outdent + '</td></tr>' +
|
2098
|
+
'<tr><td>⌘ + K</td><td>' + lang.link.insert + '</td></tr>' +
|
2099
|
+
'<tr><td>⌘ + ENTER</td><td>' + lang.hr.insert + '</td></tr>' +
|
2100
|
+
'</tbody>' +
|
2101
|
+
'</table>';
|
2102
|
+
};
|
2103
|
+
|
2104
|
+
var tplShortcutPara = function (lang) {
|
2105
|
+
return '<table class="editor-shortcut">' +
|
2106
|
+
'<thead>' +
|
2107
|
+
'<tr><th></th><th>' + lang.shortcut.paragraphFormatting + '</th></tr>' +
|
2108
|
+
'</thead>' +
|
2109
|
+
'<tbody>' +
|
2110
|
+
'<tr><td>⌘ + ⇧ + L</td><td>' + lang.paragraph.left + '</td></tr>' +
|
2111
|
+
'<tr><td>⌘ + ⇧ + E</td><td>' + lang.paragraph.center + '</td></tr>' +
|
2112
|
+
'<tr><td>⌘ + ⇧ + R</td><td>' + lang.paragraph.right + '</td></tr>' +
|
2113
|
+
'<tr><td>⌘ + ⇧ + J</td><td>' + lang.paragraph.justify + '</td></tr>' +
|
2114
|
+
'<tr><td>⌘ + ⇧ + NUM7</td><td>' + lang.lists.ordered + '</td></tr>' +
|
2115
|
+
'<tr><td>⌘ + ⇧ + NUM8</td><td>' + lang.lists.unordered + '</td></tr>' +
|
2116
|
+
'</tbody>' +
|
2117
|
+
'</table>';
|
2118
|
+
};
|
2119
|
+
|
2120
|
+
var tplShortcutStyle = function (lang) {
|
2121
|
+
return '<table class="editor-shortcut">' +
|
2122
|
+
'<thead>' +
|
2123
|
+
'<tr><th></th><th>' + lang.shortcut.documentStyle + '</th></tr>' +
|
2124
|
+
'</thead>' +
|
2125
|
+
'<tbody>' +
|
2126
|
+
'<tr><td>⌘ + NUM0</td><td>' + lang.style.normal + '</td></tr>' +
|
2127
|
+
'<tr><td>⌘ + NUM1</td><td>' + lang.style.h1 + '</td></tr>' +
|
2128
|
+
'<tr><td>⌘ + NUM2</td><td>' + lang.style.h2 + '</td></tr>' +
|
2129
|
+
'<tr><td>⌘ + NUM3</td><td>' + lang.style.h3 + '</td></tr>' +
|
2130
|
+
'<tr><td>⌘ + NUM4</td><td>' + lang.style.h4 + '</td></tr>' +
|
2131
|
+
'<tr><td>⌘ + NUM5</td><td>' + lang.style.h5 + '</td></tr>' +
|
2132
|
+
'<tr><td>⌘ + NUM6</td><td>' + lang.style.h6 + '</td></tr>' +
|
2133
|
+
'</tbody>' +
|
2134
|
+
'</table>';
|
2135
|
+
};
|
2136
|
+
|
2137
|
+
var tplShortcutTable = function (lang) {
|
2138
|
+
return '<table class="editor-shortcut-layout">' +
|
2139
|
+
'<tbody>' +
|
2140
|
+
'<tr><td>' + tplShortcutAction(lang) + '</td><td>' + tplShortcutText(lang) + '</td></tr>' +
|
2141
|
+
'<tr><td>' + tplShortcutStyle(lang) + '</td><td>' + tplShortcutPara(lang) + '</td></tr>' +
|
2142
|
+
'</tbody>' +
|
2143
|
+
'</table>';
|
2144
|
+
};
|
2145
|
+
|
2146
|
+
var replaceMacKeys = function (sHtml) {
|
2147
|
+
return sHtml.replace(/⌘/g, 'Ctrl').replace(/⇧/g, 'Shift');
|
2148
|
+
};
|
2149
|
+
|
2150
|
+
tplDialog = function (lang) {
|
2151
|
+
return '<div class="editor-dialog">' +
|
2152
|
+
'<div class="editor-image-dialog modal fade" aria-hidden="false">' +
|
2153
|
+
'<div class="modal-dialog">' +
|
2154
|
+
'<div class="modal-header">' +
|
2155
|
+
'<h3>' + lang.image.insert + '</h3>' +
|
2156
|
+
'</div>' +
|
2157
|
+
'<div class="modal-body">' +
|
2158
|
+
'<label>' + lang.image.selectFromFiles + '</label>' +
|
2159
|
+
'<input class="editor-image-input" type="file" name="files" accept="image/*" />' +
|
2160
|
+
'<label>' + lang.image.url + '</label>' +
|
2161
|
+
'<input class="editor-image-url" type="text" />' +
|
2162
|
+
'</div>' +
|
2163
|
+
'<div class="modal-footer">' +
|
2164
|
+
'<a data-dismiss="modal" href="#" class="modal-footer-btn modal-footer-btn-left">' + lang.shortcut.close + '</a>' +
|
2165
|
+
'<button href="#" class="modal-footer-btn modal-footer-btn-right editor-image-btn" disabled="disabled">' + lang.image.insert + '</button>' +
|
2166
|
+
'</div>' +
|
2167
|
+
'</div>' +
|
2168
|
+
'</div>' +
|
2169
|
+
// Link Modal
|
2170
|
+
'<div class="editor-link-dialog modal fade" aria-hidden="false">' +
|
2171
|
+
'<div class="modal-dialog">' +
|
2172
|
+
'<div class="modal-header">' +
|
2173
|
+
'<h3>' + lang.link.insert + '</h3>' +
|
2174
|
+
'</div>' +
|
2175
|
+
'<div class="modal-body">' +
|
2176
|
+
'<div class="form-group">' +
|
2177
|
+
'<label>' + lang.link.textToDisplay + '</label>' +
|
2178
|
+
'<span class="editor-link-text uneditable-input" disabled="disabled"/>' +
|
2179
|
+
'</div>' +
|
2180
|
+
'<div class="form-group">' +
|
2181
|
+
'<label>' + lang.link.url + '</label>' +
|
2182
|
+
'<input class="editor-link-url" type="text" />' +
|
2183
|
+
'</div>' +
|
2184
|
+
'</div>' +
|
2185
|
+
'<div class="modal-footer">' +
|
2186
|
+
'<a data-dismiss="modal" href="#" class="modal-footer-btn modal-footer-btn-left">' + lang.shortcut.close + '</a>' +
|
2187
|
+
'<button href="#" class="modal-footer-btn modal-footer-btn-right editor-link-btn" disabled="disabled">' + lang.link.insert + '</button>' +
|
2188
|
+
'</div>' +
|
2189
|
+
'</div>' +
|
2190
|
+
'</div>' +
|
2191
|
+
// Video Modal
|
2192
|
+
'<div class="editor-video-dialog modal fade" aria-hidden="false">' +
|
2193
|
+
'<div class="modal-dialog">' +
|
2194
|
+
'<div class="modal-header">' +
|
2195
|
+
'<h3>' + lang.video.insert + '</h3>' +
|
2196
|
+
'</div>' +
|
2197
|
+
'<div class="modal-body">' +
|
2198
|
+
'<div class="form-group">' +
|
2199
|
+
'<label>' + lang.video.url + '</label>' +
|
2200
|
+
'<input class="editor-video-url" type="text" />' +
|
2201
|
+
'<span class="form-help-block">' + lang.video.providers + '</span>' +
|
2202
|
+
'</div>' +
|
2203
|
+
'</div>' +
|
2204
|
+
'<div class="modal-footer">' +
|
2205
|
+
'<a data-dismiss="modal" href="#" class="modal-footer-btn modal-footer-btn-left">' + lang.shortcut.close + '</a>' +
|
2206
|
+
'<button href="#" class="modal-footer-btn modal-footer-btn-right editor-video-btn" disabled="disabled">' + lang.video.insert + '</button>' +
|
2207
|
+
'</div>' +
|
2208
|
+
'</div>' +
|
2209
|
+
'</div>' +
|
2210
|
+
// Help Modal
|
2211
|
+
'<div class="editor-help-dialog modal fade" aria-hidden="false">' +
|
2212
|
+
'<div class="modal-dialog">' +
|
2213
|
+
'<div class="modal-header">' +
|
2214
|
+
'<h3>' + lang.shortcut.shortcuts + '</h3>' +
|
2215
|
+
'</div>' +
|
2216
|
+
'<div class="modal-body">' +
|
2217
|
+
(agent.bMac ? tplShortcutTable(lang) : replaceMacKeys(tplShortcutTable(lang))) +
|
2218
|
+
'</div>' +
|
2219
|
+
'<div class="modal-footer">' +
|
2220
|
+
'<a data-dismiss="modal" class="modal-footer-btn" href="#">' + lang.shortcut.close + '</a>' +
|
2221
|
+
'</div>' +
|
2222
|
+
'</div>' +
|
2223
|
+
'</div>' +
|
2224
|
+
'</div>';
|
2225
|
+
};
|
2226
|
+
|
2227
|
+
tplStatusbar = '<div class="editor-resizebar"><div class="editor-icon-bar"></div><div class="editor-icon-bar"></div><div class="editor-icon-bar"></div></div>';
|
2228
|
+
/* jshint ignore:end */
|
2229
|
+
|
2230
|
+
// createTooltip
|
2231
|
+
var createTooltip = function ($container, sPlacement) {
|
2232
|
+
$container.find('button').each(function (i, elBtn) {
|
2233
|
+
var $btn = $(elBtn);
|
2234
|
+
var tplShortcut = $btn.attr(agent.bMac ? 'data-mac-shortcut': 'data-shortcut');
|
2235
|
+
if (tplShortcut) { $btn.attr('title', function (i, v) { return v + ' (' + tplShortcut + ')'; }); }
|
2236
|
+
// bootstrap tooltip on btn-group bug: https://github.com/twitter/bootstrap/issues/5687
|
2237
|
+
}).tooltip({container: 'body', trigger: 'hover', placement: sPlacement || 'top'})
|
2238
|
+
.on('click', function () {$(this).tooltip('hide'); });
|
2239
|
+
};
|
2240
|
+
|
2241
|
+
// pallete colors
|
2242
|
+
var aaColor = [
|
2243
|
+
['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
|
2244
|
+
['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
|
2245
|
+
['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
|
2246
|
+
['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
|
2247
|
+
['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
|
2248
|
+
['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
|
2249
|
+
['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
|
2250
|
+
['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
|
2251
|
+
];
|
2252
|
+
|
2253
|
+
// createPalette
|
2254
|
+
var createPalette = function ($container) {
|
2255
|
+
$container.find('.editor-color-palette').each(function () {
|
2256
|
+
var $palette = $(this), sEvent = $palette.attr('data-target-event');
|
2257
|
+
var aPaletteContents = [];
|
2258
|
+
for (var row = 0, szRow = aaColor.length; row < szRow; row++) {
|
2259
|
+
var aColor = aaColor[row];
|
2260
|
+
var aButton = [];
|
2261
|
+
for (var col = 0, szCol = aColor.length; col < szCol; col++) {
|
2262
|
+
var sColor = aColor[col];
|
2263
|
+
aButton.push(['<button type="button" class="editor-color-btn" style="background-color:', sColor,
|
2264
|
+
';" data-event="', sEvent,
|
2265
|
+
'" data-value="', sColor,
|
2266
|
+
'" title="', sColor,
|
2267
|
+
'" data-toggle="button" tabindex="-1"></button>'].join(''));
|
2268
|
+
}
|
2269
|
+
aPaletteContents.push('<div>' + aButton.join('') + '</div>');
|
2270
|
+
}
|
2271
|
+
$palette.html(aPaletteContents.join(''));
|
2272
|
+
});
|
2273
|
+
};
|
2274
|
+
|
2275
|
+
// createLayout
|
2276
|
+
this.createLayout = function ($holder, options) {
|
2277
|
+
var nHeight = options.height,
|
2278
|
+
nTabsize = options.tabsize,
|
2279
|
+
sDirection = options.direction,
|
2280
|
+
aToolbarSetting = options.toolbar,
|
2281
|
+
langInfo = $.editor.lang[options.lang];
|
2282
|
+
|
2283
|
+
//already created
|
2284
|
+
if ($holder.next().hasClass('editor-canvas')) { return; }
|
2285
|
+
|
2286
|
+
//01. create Editor
|
2287
|
+
var $editor = $('<div class="editor-canvas"></div>');
|
2288
|
+
$editor.data('options', options);
|
2289
|
+
|
2290
|
+
//02. statusbar
|
2291
|
+
if (nHeight > 0) {
|
2292
|
+
$('<div class="editor-statusbar">' + tplStatusbar + '</div>').prependTo($editor);
|
2293
|
+
}
|
2294
|
+
|
2295
|
+
//03. create Editable
|
2296
|
+
var $editable = $('<div class="editor-editable" contentEditable="true"></div>').prependTo($editor);
|
2297
|
+
if (nHeight) {
|
2298
|
+
$editable.height(nHeight);
|
2299
|
+
$editable.data('optionHeight', nHeight);
|
2300
|
+
}
|
2301
|
+
if (nTabsize) {
|
2302
|
+
$editable.data('tabsize', nTabsize);
|
2303
|
+
}
|
2304
|
+
if (sDirection) {
|
2305
|
+
$editable.attr('dir', sDirection);
|
2306
|
+
}
|
2307
|
+
|
2308
|
+
$editable.html(dom.html($holder) || dom.emptyPara);
|
2309
|
+
$editable.data('NoteHistory', new History());
|
2310
|
+
|
2311
|
+
//031. create codable
|
2312
|
+
$('<textarea class="editor-codable"></textarea>').prependTo($editor);
|
2313
|
+
|
2314
|
+
//032. set styleWithCSS for backColor / foreColor clearing with 'inherit'.
|
2315
|
+
setTimeout(function () { // protect FF Error: NS_ERROR_FAILURE: Failure
|
2316
|
+
document.execCommand('styleWithCSS', 0, true);
|
2317
|
+
});
|
2318
|
+
|
2319
|
+
//04. create Toolbar
|
2320
|
+
var sToolbar = '';
|
2321
|
+
for (var idx = 0, sz = aToolbarSetting.length; idx < sz; idx ++) {
|
2322
|
+
var group = aToolbarSetting[idx];
|
2323
|
+
sToolbar += '<div class="editor-' + group[0] + ' btn-group">';
|
2324
|
+
for (var i = 0, szGroup = group[1].length; i < szGroup; i++) {
|
2325
|
+
sToolbar += tplToolbarInfo[group[1][i]](langInfo);
|
2326
|
+
}
|
2327
|
+
sToolbar += '</div>';
|
2328
|
+
}
|
2329
|
+
|
2330
|
+
sToolbar = '<div class="editor-toolbar btn-toolbar">' + sToolbar + '</div>';
|
2331
|
+
|
2332
|
+
var $toolbar = $(sToolbar).prependTo($editor);
|
2333
|
+
createPalette($toolbar);
|
2334
|
+
createTooltip($toolbar, 'bottom');
|
2335
|
+
|
2336
|
+
//05. create Popover
|
2337
|
+
var $popover = $(tplPopover(langInfo)).prependTo($editor);
|
2338
|
+
createTooltip($popover);
|
2339
|
+
|
2340
|
+
//06. handle(control selection, ...)
|
2341
|
+
$(tplhandle).prependTo($editor);
|
2342
|
+
|
2343
|
+
//07. create Dialog
|
2344
|
+
var $dialog = $(tplDialog(langInfo)).prependTo($editor);
|
2345
|
+
$dialog.find('button.close, a.modal-close').click(function () {
|
2346
|
+
$(this).closest('.modal').modal('hide');
|
2347
|
+
});
|
2348
|
+
|
2349
|
+
//08. create Dropzone
|
2350
|
+
$('<div class="editor-dropzone"><div class="editor-dropzone-message"></div></div>').prependTo($editor);
|
2351
|
+
|
2352
|
+
//09. Editor/Holder switch
|
2353
|
+
$editor.insertAfter($holder);
|
2354
|
+
$holder.hide();
|
2355
|
+
};
|
2356
|
+
|
2357
|
+
// layoutInfoFromHolder
|
2358
|
+
var layoutInfoFromHolder = this.layoutInfoFromHolder = function ($holder) {
|
2359
|
+
var $editor = $holder.next();
|
2360
|
+
if (!$editor.hasClass('editor-canvas')) { return; }
|
2361
|
+
|
2362
|
+
return {
|
2363
|
+
editor: $editor,
|
2364
|
+
dropzone: $editor.find('.editor-dropzone'),
|
2365
|
+
toolbar: $editor.find('.editor-toolbar'),
|
2366
|
+
editable: $editor.find('.editor-editable'),
|
2367
|
+
codable: $editor.find('.editor-codable'),
|
2368
|
+
statusbar: $editor.find('.editor-statusbar'),
|
2369
|
+
popover: $editor.find('.editor-popover'),
|
2370
|
+
handle: $editor.find('.editor-handle'),
|
2371
|
+
dialog: $editor.find('.editor-dialog')
|
2372
|
+
};
|
2373
|
+
};
|
2374
|
+
|
2375
|
+
// removeLayout
|
2376
|
+
this.removeLayout = function ($holder) {
|
2377
|
+
var info = layoutInfoFromHolder($holder);
|
2378
|
+
if (!info) { return; }
|
2379
|
+
$holder.html(info.editable.html());
|
2380
|
+
|
2381
|
+
info.editor.remove();
|
2382
|
+
$holder.show();
|
2383
|
+
};
|
2384
|
+
};
|
2385
|
+
|
2386
|
+
var renderer = new Renderer();
|
2387
|
+
var eventHandler = new EventHandler();
|
2388
|
+
|
2389
|
+
$.editor = $.editor || {};
|
2390
|
+
|
2391
|
+
$.extend($.editor, {
|
2392
|
+
version: '0.5.1',
|
2393
|
+
lang: {
|
2394
|
+
'en-US': {
|
2395
|
+
font: {
|
2396
|
+
bold: 'Bold',
|
2397
|
+
italic: 'Italic',
|
2398
|
+
underline: 'Underline',
|
2399
|
+
strike: 'Strike',
|
2400
|
+
clear: 'Remove Font Style',
|
2401
|
+
height: 'Line Height',
|
2402
|
+
size: 'Font Size'
|
2403
|
+
},
|
2404
|
+
image: {
|
2405
|
+
image: 'Picture',
|
2406
|
+
insert: 'Insert Image',
|
2407
|
+
resizeFull: 'Resize Full',
|
2408
|
+
resizeHalf: 'Resize Half',
|
2409
|
+
resizeQuarter: 'Resize Quarter',
|
2410
|
+
floatLeft: 'Float Left',
|
2411
|
+
floatRight: 'Float Right',
|
2412
|
+
floatNone: 'Float None',
|
2413
|
+
dragImageHere: 'Drag an image here',
|
2414
|
+
selectFromFiles: 'Select from files',
|
2415
|
+
url: 'Image URL'
|
2416
|
+
},
|
2417
|
+
link: {
|
2418
|
+
link: 'Link',
|
2419
|
+
insert: 'Insert Link',
|
2420
|
+
unlink: 'Unlink',
|
2421
|
+
edit: 'Edit',
|
2422
|
+
textToDisplay: 'Text to display',
|
2423
|
+
url: 'To what URL should this link go?'
|
2424
|
+
},
|
2425
|
+
video: {
|
2426
|
+
video: 'Video',
|
2427
|
+
videoLink: 'Video Link',
|
2428
|
+
insert: 'Insert Video',
|
2429
|
+
url: 'Video URL?',
|
2430
|
+
providers: '(YouTube, Vimeo, Vine, Instagram, or DailyMotion)'
|
2431
|
+
},
|
2432
|
+
table: {
|
2433
|
+
table: 'Table'
|
2434
|
+
},
|
2435
|
+
hr: {
|
2436
|
+
insert: 'Insert Horizontal Rule'
|
2437
|
+
},
|
2438
|
+
style: {
|
2439
|
+
style: 'Style',
|
2440
|
+
normal: 'Normal',
|
2441
|
+
blockquote: 'Quote',
|
2442
|
+
pre: 'Code',
|
2443
|
+
h1: 'Header 1',
|
2444
|
+
h2: 'Header 2',
|
2445
|
+
h3: 'Header 3',
|
2446
|
+
h4: 'Header 4',
|
2447
|
+
h5: 'Header 5',
|
2448
|
+
h6: 'Header 6'
|
2449
|
+
},
|
2450
|
+
lists: {
|
2451
|
+
unordered: 'Unordered list',
|
2452
|
+
ordered: 'Ordered list'
|
2453
|
+
},
|
2454
|
+
options: {
|
2455
|
+
help: 'Help',
|
2456
|
+
fullscreen: 'Full Screen',
|
2457
|
+
codeview: 'Code View'
|
2458
|
+
},
|
2459
|
+
paragraph: {
|
2460
|
+
paragraph: 'Paragraph',
|
2461
|
+
outdent: 'Outdent',
|
2462
|
+
indent: 'Indent',
|
2463
|
+
left: 'Align left',
|
2464
|
+
center: 'Align center',
|
2465
|
+
right: 'Align right',
|
2466
|
+
justify: 'Justify full'
|
2467
|
+
},
|
2468
|
+
color: {
|
2469
|
+
recent: 'Recent Color',
|
2470
|
+
more: 'More Color',
|
2471
|
+
background: 'BackColor',
|
2472
|
+
foreground: 'FontColor',
|
2473
|
+
transparent: 'Transparent',
|
2474
|
+
setTransparent: 'Set transparent',
|
2475
|
+
reset: 'Reset',
|
2476
|
+
resetToDefault: 'Reset to default'
|
2477
|
+
},
|
2478
|
+
shortcut: {
|
2479
|
+
shortcuts: 'Keyboard shortcuts',
|
2480
|
+
close: 'Close',
|
2481
|
+
textFormatting: 'Text formatting',
|
2482
|
+
action: 'Action',
|
2483
|
+
paragraphFormatting: 'Paragraph formatting',
|
2484
|
+
documentStyle: 'Document Style'
|
2485
|
+
},
|
2486
|
+
history: {
|
2487
|
+
undo: 'Undo',
|
2488
|
+
redo: 'Redo'
|
2489
|
+
}
|
2490
|
+
}
|
2491
|
+
}
|
2492
|
+
});
|
2493
|
+
|
2494
|
+
/**
|
2495
|
+
* extend jquery fn
|
2496
|
+
*/
|
2497
|
+
$.fn.extend({
|
2498
|
+
// create Editor Layout and attach Key and Mouse Event
|
2499
|
+
editor: function (options) {
|
2500
|
+
options = $.extend({
|
2501
|
+
toolbar: [
|
2502
|
+
['style', ['style']],
|
2503
|
+
['font', ['bold', 'italic', 'underline', 'clear']],
|
2504
|
+
['fontsize', ['fontsize']],
|
2505
|
+
['color', ['color']],
|
2506
|
+
['para', ['ul', 'ol', 'paragraph']],
|
2507
|
+
['height', ['height']],
|
2508
|
+
['table', ['table']],
|
2509
|
+
['insert', ['link', 'picture', 'video']],
|
2510
|
+
['view', ['fullscreen', 'codeview']],
|
2511
|
+
['help', ['help']]
|
2512
|
+
],
|
2513
|
+
lang: 'en-US'
|
2514
|
+
}, options);
|
2515
|
+
|
2516
|
+
this.each(function (idx, elHolder) {
|
2517
|
+
var $holder = $(elHolder);
|
2518
|
+
|
2519
|
+
// createLayout with options
|
2520
|
+
renderer.createLayout($holder, options);
|
2521
|
+
|
2522
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
2523
|
+
eventHandler.attach(info, options);
|
2524
|
+
|
2525
|
+
// Textarea auto filling the code before form submit.
|
2526
|
+
if (dom.isTextarea($holder[0])) {
|
2527
|
+
$holder.closest('form').submit(function () {
|
2528
|
+
$holder.html($holder.code());
|
2529
|
+
});
|
2530
|
+
}
|
2531
|
+
});
|
2532
|
+
|
2533
|
+
if (this.first() && options.focus) { // focus on first editable element
|
2534
|
+
var info = renderer.layoutInfoFromHolder(this.first());
|
2535
|
+
info.editable.focus();
|
2536
|
+
}
|
2537
|
+
if (this.length > 0 && options.oninit) { // callback on init
|
2538
|
+
options.oninit();
|
2539
|
+
}
|
2540
|
+
},
|
2541
|
+
// get the HTML contents of editor or set the HTML contents of editor.
|
2542
|
+
code: function (sHTML) {
|
2543
|
+
// get the HTML contents
|
2544
|
+
if (sHTML === undefined) {
|
2545
|
+
var $holder = this.first();
|
2546
|
+
if ($holder.length === 0) { return; }
|
2547
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
2548
|
+
if (!!(info && info.editable)) {
|
2549
|
+
var bCodeview = info.editor.hasClass('codeview');
|
2550
|
+
if (bCodeview && agent.bCodeMirror) {
|
2551
|
+
info.codable.data('cmEditor').save();
|
2552
|
+
}
|
2553
|
+
return bCodeview ? info.codable.val() : info.editable.html();
|
2554
|
+
}
|
2555
|
+
return $holder.html();
|
2556
|
+
}
|
2557
|
+
|
2558
|
+
// set the HTML contents
|
2559
|
+
this.each(function (i, elHolder) {
|
2560
|
+
var info = renderer.layoutInfoFromHolder($(elHolder));
|
2561
|
+
if (info && info.editable) { info.editable.html(sHTML); }
|
2562
|
+
});
|
2563
|
+
},
|
2564
|
+
// destroy Editor Layout and dettach Key and Mouse Event
|
2565
|
+
destroy: function () {
|
2566
|
+
this.each(function (idx, elHolder) {
|
2567
|
+
var $holder = $(elHolder);
|
2568
|
+
|
2569
|
+
var info = renderer.layoutInfoFromHolder($holder);
|
2570
|
+
if (!info || !info.editable) { return; }
|
2571
|
+
eventHandler.dettach(info);
|
2572
|
+
renderer.removeLayout($holder);
|
2573
|
+
});
|
2574
|
+
},
|
2575
|
+
// inner object for test
|
2576
|
+
editorInner: function () {
|
2577
|
+
return { dom: dom, list: list, func: func, range: range };
|
2578
|
+
}
|
2579
|
+
});
|
2580
|
+
}));
|