jquerypp-rails 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/.document +5 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.rdoc +24 -0
  6. data/Rakefile +2 -0
  7. data/jquerypp-rails.gemspec +20 -0
  8. data/lib/jquerypp/generators/jquerypp/install/install_generator.rb +49 -0
  9. data/lib/jquerypp/rails/engine.rb +8 -0
  10. data/lib/jquerypp/rails/version.rb +6 -0
  11. data/lib/jquerypp/rails.rb +8 -0
  12. data/lib/jquerypp-rails.rb +1 -0
  13. data/vendor/assets/javascripts/jquerypp.js +5419 -0
  14. data/vendor/assets/javascripts/lib/jquery.animate.js +326 -0
  15. data/vendor/assets/javascripts/lib/jquery.compare.js +75 -0
  16. data/vendor/assets/javascripts/lib/jquery.cookie.js +118 -0
  17. data/vendor/assets/javascripts/lib/jquery.dimensions.js +191 -0
  18. data/vendor/assets/javascripts/lib/jquery.event.default.js +115 -0
  19. data/vendor/assets/javascripts/lib/jquery.event.destroyed.js +23 -0
  20. data/vendor/assets/javascripts/lib/jquery.event.drag.js +727 -0
  21. data/vendor/assets/javascripts/lib/jquery.event.drop.js +457 -0
  22. data/vendor/assets/javascripts/lib/jquery.event.fastfix.js +95 -0
  23. data/vendor/assets/javascripts/lib/jquery.event.hover.js +266 -0
  24. data/vendor/assets/javascripts/lib/jquery.event.key.js +156 -0
  25. data/vendor/assets/javascripts/lib/jquery.event.livehack.js +174 -0
  26. data/vendor/assets/javascripts/lib/jquery.event.pause.js +92 -0
  27. data/vendor/assets/javascripts/lib/jquery.event.resize.js +47 -0
  28. data/vendor/assets/javascripts/lib/jquery.event.swipe.js +133 -0
  29. data/vendor/assets/javascripts/lib/jquery.fills.js +249 -0
  30. data/vendor/assets/javascripts/lib/jquery.form_params.js +167 -0
  31. data/vendor/assets/javascripts/lib/jquery.lang.json.js +196 -0
  32. data/vendor/assets/javascripts/lib/jquery.lang.vector.js +214 -0
  33. data/vendor/assets/javascripts/lib/jquery.range.js +861 -0
  34. data/vendor/assets/javascripts/lib/jquery.selection.js +232 -0
  35. data/vendor/assets/javascripts/lib/jquery.styles.js +103 -0
  36. data/vendor/assets/javascripts/lib/jquery.within.js +94 -0
  37. metadata +81 -0
@@ -0,0 +1,861 @@
1
+ // Dependencies:
2
+ //
3
+ // - jquery.range.js
4
+ // - jquery.compare.js
5
+
6
+ (function($){
7
+
8
+ $.fn.range =
9
+ /**
10
+ * @function jQuery.fn.range
11
+ * @parent jQuery.Range
12
+ *
13
+ * `$.fn.range` returns a new [jQuery.Range] instance for the first selected element.
14
+ *
15
+ * $('#content').range() //-> range
16
+ *
17
+ * @return {$.Range} A $.Range instance for the selected element
18
+ */
19
+ function(){
20
+ return $.Range(this[0])
21
+ }
22
+
23
+ var convertType = function(type){
24
+ return type.replace(/([a-z])([a-z]+)/gi, function(all,first, next){
25
+ return first+next.toLowerCase()
26
+ }).replace(/_/g,"");
27
+ },
28
+ // reverses things like START_TO_END into END_TO_START
29
+ reverse = function(type){
30
+ return type.replace(/^([a-z]+)_TO_([a-z]+)/i, function(all, first, last){
31
+ return last+"_TO_"+first;
32
+ });
33
+ },
34
+ getWindow = function( element ) {
35
+ return element ? element.ownerDocument.defaultView || element.ownerDocument.parentWindow : window
36
+ },
37
+ bisect = function(el, start, end){
38
+ //split the start and end ... figure out who is touching ...
39
+ if(end-start == 1){
40
+ return
41
+ }
42
+ },
43
+ support = {};
44
+ /**
45
+ * @Class jQuery.Range
46
+ * @parent jQuery.Range
47
+ *
48
+ * Depending on the object passed, the selected text will be different.
49
+ *
50
+ * @param {TextRange|HTMLElement|Point} [range] An object specifiying a
51
+ * range. Depending on the object, the selected text will be different. $.Range supports the
52
+ * following types
53
+ *
54
+ * - __undefined or null__ - returns a range with nothing selected
55
+ * - __HTMLElement__ - returns a range with the node's text selected
56
+ * - __Point__ - returns a range at the point on the screen. The point can be specified like:
57
+ *
58
+ * //client coordinates
59
+ * {clientX: 200, clientY: 300}
60
+ *
61
+ * //page coordinates
62
+ * {pageX: 200, pageY: 300}
63
+ * {top: 200, left: 300}
64
+ *
65
+ * - __TextRange__ a raw text range object.
66
+ */
67
+ $.Range = function(range){
68
+ // If it's called w/o new, call it with new!
69
+ if(this.constructor !== $.Range){
70
+ return new $.Range(range);
71
+ }
72
+ // If we are passed a jQuery-wrapped element, get the raw element
73
+ if(range && range.jquery){
74
+ range = range[0];
75
+ }
76
+ // If we have an element, or nothing
77
+ if(!range || range.nodeType){
78
+ // create a range
79
+ this.win = getWindow(range)
80
+ if(this.win.document.createRange){
81
+ this.range = this.win.document.createRange()
82
+ }else{
83
+ this.range = this.win.document.body.createTextRange()
84
+ }
85
+ // if we have an element, make the range select it
86
+ if(range){
87
+ this.select(range)
88
+ }
89
+ }
90
+ // if we are given a point
91
+ else if (range.clientX != null || range.pageX != null || range.left != null) {
92
+ this.moveToPoint(range);
93
+ }
94
+ // if we are given a touch event
95
+ else if (range.originalEvent && range.originalEvent.touches && range.originalEvent.touches.length) {
96
+ this.moveToPoint(range.originalEvent.touches[0])
97
+
98
+ }
99
+ // if we are a normal event
100
+ else if (range.originalEvent && range.originalEvent.changedTouches && range.originalEvent.changedTouches.length) {
101
+ this.moveToPoint(range.originalEvent.changedTouches[0])
102
+ }
103
+ // given a TextRange or something else?
104
+ else {
105
+ this.range = range;
106
+ }
107
+ };
108
+ /**
109
+ * @static
110
+ */
111
+ $.Range.
112
+ /**
113
+ * `$.Range.current([element])` returns the currently selected range
114
+ * (using [window.getSelection](https://developer.mozilla.org/en/nsISelection)).
115
+ *
116
+ * var range = $.Range.current()
117
+ * range.start().offset // -> selection start offset
118
+ * range.end().offset // -> selection end offset
119
+ *
120
+ * @param {HTMLElement} [el] an optional element used to get selection for a given window.
121
+ * @return {jQuery.Range} The range instance.
122
+ */
123
+ current = function(el){
124
+ var win = getWindow(el),
125
+ selection;
126
+ if(win.getSelection){
127
+ // If we can get the selection
128
+ selection = win.getSelection()
129
+ return new $.Range( selection.rangeCount ? selection.getRangeAt(0) : win.document.createRange())
130
+ } else {
131
+ // Otherwise use document.selection
132
+ return new $.Range( win.document.selection.createRange() );
133
+ }
134
+ };
135
+
136
+
137
+
138
+
139
+ $.extend($.Range.prototype,
140
+ /** @prototype **/
141
+ {
142
+ /**
143
+ * `range.moveToPoint(point)` moves the range end and start position to a specific point.
144
+ * A point can be specified like:
145
+ *
146
+ * //client coordinates
147
+ * {clientX: 200, clientY: 300}
148
+ *
149
+ * //page coordinates
150
+ * {pageX: 200, pageY: 300}
151
+ * {top: 200, left: 300}
152
+ *
153
+ * @param point The point to move the range to
154
+ * @return {$.Range}
155
+ */
156
+ moveToPoint : function(point){
157
+ var clientX = point.clientX, clientY = point.clientY
158
+ if(!clientX){
159
+ var off = scrollOffset();
160
+ clientX = (point.pageX || point.left || 0 ) - off.left;
161
+ clientY = (point.pageY || point.top || 0 ) - off.top;
162
+ }
163
+ if(support.moveToPoint){
164
+ this.range = $.Range().range
165
+ this.range.moveToPoint(clientX, clientY);
166
+ return this;
167
+ }
168
+
169
+
170
+ // it's some text node in this range ...
171
+ var parent = document.elementFromPoint(clientX, clientY);
172
+
173
+ //typically it will be 'on' text
174
+ for(var n=0; n < parent.childNodes.length; n++){
175
+ var node = parent.childNodes[n];
176
+ if(node.nodeType === 3 || node.nodeType === 4){
177
+ var range = $.Range(node),
178
+ length = range.toString().length;
179
+
180
+
181
+ // now lets start moving the end until the boundingRect is within our range
182
+ for(var i = 1; i < length+1; i++){
183
+ var rect = range.end(i).rect();
184
+ if(rect.left <= clientX && rect.left+rect.width >= clientX &&
185
+ rect.top <= clientY && rect.top+rect.height >= clientY ){
186
+ range.start(i-1);
187
+ this.range = range.range;
188
+ return this;
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ // if not 'on' text, recursively go through and find out when we shift to next
195
+ // 'line'
196
+ var previous;
197
+ iterate(parent.childNodes, function(textNode){
198
+ var range = $.Range(textNode);
199
+ if(range.rect().top > point.clientY){
200
+ return false;
201
+ }else{
202
+ previous = range;
203
+ }
204
+ });
205
+
206
+ if(previous){
207
+ previous.start(previous.toString().length);
208
+ this.range = previous.range;
209
+ }else{
210
+ this.range = $.Range(parent).range
211
+ }
212
+ },
213
+
214
+ window : function(){
215
+ return this.win || window;
216
+ },
217
+ /**
218
+ * `range.overlaps([elRange])` returns `true` if any portion of these two ranges overlap.
219
+ *
220
+ * var foo = document.getElementById('foo');
221
+ *
222
+ * $.Range(foo.childNodes[0]).overlaps(foo.childNodes[1]) //-> false
223
+ *
224
+ * @param {jQuery.Range} elRange The range to compare
225
+ * @return {Boolean} true if part of the ranges overlap, false if otherwise.
226
+ */
227
+ overlaps : function(elRange){
228
+ if(elRange.nodeType){
229
+ elRange = $.Range(elRange).select(elRange);
230
+ }
231
+ //if the start is within the element ...
232
+ var startToStart = this.compare("START_TO_START", elRange),
233
+ endToEnd = this.compare("END_TO_END", elRange)
234
+
235
+ // if we wrap elRange
236
+ if(startToStart <=0 && endToEnd >=0){
237
+ return true;
238
+ }
239
+ // if our start is inside of it
240
+ if( startToStart >= 0 &&
241
+ this.compare("START_TO_END", elRange) <= 0 ) {
242
+ return true;
243
+ }
244
+ // if our end is inside of elRange
245
+ if(this.compare("END_TO_START", elRange) >= 0 &&
246
+ endToEnd <= 0 ) {
247
+ return true;
248
+ }
249
+ return false;
250
+ },
251
+ /**
252
+ * `range.collapse([toStart])` collapses a range to one of its boundary points.
253
+ * See [range.collapse](https://developer.mozilla.org/en/DOM/range.collapse).
254
+ *
255
+ * $('#foo').range().collapse()
256
+ *
257
+ * @param {Boolean} [toStart] true if to the start of the range, false if to the
258
+ * end. Defaults to false.
259
+ * @return {jQuery.Range} returns the range for chaining.
260
+ */
261
+ collapse : function(toStart){
262
+ this.range.collapse(toStart === undefined ? true : toStart);
263
+ return this;
264
+ },
265
+ /**
266
+ * `range.toString()` returns the text of the range.
267
+ *
268
+ * currentText = $.Range.current().toString()
269
+ *
270
+ * @return {String} The text content of this range
271
+ */
272
+ toString : function(){
273
+ return typeof this.range.text == "string" ? this.range.text : this.range.toString();
274
+ },
275
+ /**
276
+ * `range.start([start])` gets or sets the start of the range.
277
+ *
278
+ * If a value is not provided, start returns the range's starting container and offset like:
279
+ *
280
+ * $('#foo').range().start()
281
+ * //-> {container: fooElement, offset: 0 }
282
+ *
283
+ * If a set value is provided, it can set the range. The start of the range is set differently
284
+ * depending on the type of set value:
285
+ *
286
+ * - __Object__ - an object with the new starting container and offset like
287
+ *
288
+ * $.Range().start({container: $('#foo')[0], offset: 20})
289
+ *
290
+ * - __Number__ - the new offset value. The container is kept the same.
291
+ *
292
+ * - __String__ - adjusts the offset by converting the string offset to a number and adding it to the current
293
+ * offset. For example, the following moves the offset forward four characters:
294
+ *
295
+ * $('#foo').range().start("+4")
296
+ *
297
+ * Note that `start` can return a text node. To get the containing element use this:
298
+ *
299
+ * var startNode = range.start().container;
300
+ * if( startNode.nodeType === Node.TEXT_NODE ||
301
+ * startNode.nodeType === Node.CDATA_SECTION_NODE ) {
302
+ * startNode = startNode.parentNode;
303
+ * }
304
+ * $(startNode).addClass('highlight');
305
+ *
306
+ * @param {Object|String|Number} [set] a set value if setting the start of the range or nothing if reading it.
307
+ * @return {jQuery.Range|Object} if setting the start, the range is returned for chaining, otherwise, the
308
+ * start offset and container are returned.
309
+ */
310
+ start : function(set){
311
+ // return start
312
+ if(set === undefined){
313
+ if(this.range.startContainer){
314
+ return {
315
+ container : this.range.startContainer,
316
+ offset : this.range.startOffset
317
+ }
318
+ }else{
319
+ // Get the start parent element
320
+ var start = this.clone().collapse().parent();
321
+ // used to get the start element offset
322
+ var startRange = $.Range(start).select(start).collapse();
323
+ startRange.move("END_TO_START", this);
324
+ return {
325
+ container : start,
326
+ offset : startRange.toString().length
327
+ }
328
+ }
329
+ } else {
330
+ if (this.range.setStart) {
331
+ // supports setStart
332
+ if(typeof set == 'number'){
333
+ this.range.setStart(this.range.startContainer, set)
334
+ } else if(typeof set == 'string') {
335
+ var res = callMove(this.range.startContainer, this.range.startOffset, parseInt(set,10))
336
+ this.range.setStart(res.node, res.offset );
337
+ } else {
338
+ this.range.setStart(set.container, set.offset)
339
+ }
340
+ } else {
341
+ if(typeof set == "string"){
342
+ this.range.moveStart('character', parseInt(set,10))
343
+ } else {
344
+ // get the current end container
345
+ var container = this.start().container,
346
+ offset
347
+ if(typeof set == "number") {
348
+ offset = set
349
+ } else {
350
+ container = set.container
351
+ offset = set.offset
352
+ }
353
+ var newPoint = $.Range(container).collapse();
354
+ //move it over offset characters
355
+ newPoint.range.move(offset);
356
+ this.move("START_TO_START",newPoint);
357
+ }
358
+ }
359
+ return this;
360
+ }
361
+
362
+
363
+ },
364
+ /**
365
+ * `range.end([end])` gets or sets the end of the range.
366
+ * It takes similar options as [jQuery.Range::start start]:
367
+ *
368
+ * - __Object__ - an object with the new end container and offset like
369
+ *
370
+ * $.Range().end({container: $('#foo')[0], offset: 20})
371
+ *
372
+ * - __Number__ - the new offset value. The container is kept the same.
373
+ *
374
+ * - __String__ - adjusts the offset by converting the string offset to a number and adding it to the current
375
+ * offset. For example, the following moves the offset forward four characters:
376
+ *
377
+ * $('#foo').range().end("+4")
378
+ *
379
+ * Note that `end` can return a text node. To get the containing element use this:
380
+ *
381
+ * var startNode = range.end().container;
382
+ * if( startNode.nodeType === Node.TEXT_NODE ||
383
+ * startNode.nodeType === Node.CDATA_SECTION_NODE ) {
384
+ * startNode = startNode.parentNode;
385
+ * }
386
+ * $(startNode).addClass('highlight');
387
+ *
388
+ * @param {Object|String|Number} [set] a set value if setting the end of the range or nothing if reading it.
389
+ */
390
+ end : function(set){
391
+ // read end
392
+ if (set === undefined) {
393
+ if (this.range.startContainer) {
394
+ return {
395
+ container: this.range.endContainer,
396
+ offset: this.range.endOffset
397
+ }
398
+ }
399
+ else {
400
+ var
401
+ // Get the end parent element
402
+ end = this.clone().collapse(false).parent(),
403
+ // used to get the end elements offset
404
+ endRange = $.Range(end).select(end).collapse();
405
+ endRange.move("END_TO_END", this);
406
+ return {
407
+ container: end,
408
+ offset: endRange.toString().length
409
+ }
410
+ }
411
+ } else {
412
+ if (this.range.setEnd) {
413
+ if(typeof set == 'number'){
414
+ this.range.setEnd(this.range.endContainer, set)
415
+ } else if(typeof set == 'string') {
416
+ var res = callMove(this.range.endContainer, this.range.endOffset, parseInt(set,10))
417
+ this.range.setEnd(res.node, res.offset );
418
+ } else {
419
+ this.range.setEnd(set.container, set.offset)
420
+ }
421
+ } else {
422
+ if(typeof set == "string"){
423
+ this.range.moveEnd('character', parseInt(set,10));
424
+ } else {
425
+ // get the current end container
426
+ var container = this.end().container,
427
+ offset
428
+ if(typeof set == "number") {
429
+ offset = set
430
+ } else {
431
+ container = set.container
432
+ offset = set.offset
433
+ }
434
+ var newPoint = $.Range(container).collapse();
435
+ //move it over offset characters
436
+ newPoint.range.move(offset);
437
+ this.move("END_TO_START",newPoint);
438
+ }
439
+ }
440
+ return this;
441
+ }
442
+ },
443
+ /**
444
+ * `range.parent()` returns the most common ancestor element of
445
+ * the endpoints in the range. This will return a text element if the range is
446
+ * within a text element. In this case, to get the containing element use this:
447
+ *
448
+ * var parent = range.parent();
449
+ * if( parent.nodeType === Node.TEXT_NODE ||
450
+ * parent.nodeType === Node.CDATA_SECTION_NODE ) {
451
+ * parent = startNode.parentNode;
452
+ * }
453
+ * $(parent).addClass('highlight');
454
+ *
455
+ * @return {HTMLNode} the TextNode or HTMLElement
456
+ * that fully contains the range
457
+ */
458
+ parent : function(){
459
+ if(this.range.commonAncestorContainer){
460
+ return this.range.commonAncestorContainer;
461
+ } else {
462
+
463
+ var parentElement = this.range.parentElement(),
464
+ range = this.range;
465
+
466
+ // IE's parentElement will always give an element, we want text ranges
467
+ iterate(parentElement.childNodes, function(txtNode){
468
+ if($.Range(txtNode).range.inRange( range ) ){
469
+ // swap out the parentElement
470
+ parentElement = txtNode;
471
+ return false;
472
+ }
473
+ });
474
+
475
+ return parentElement;
476
+ }
477
+ },
478
+ /**
479
+ * `range.rect([from])` returns the bounding rectangle of this range.
480
+ *
481
+ * @param {String} [from] - where the coordinates should be
482
+ * positioned from. By default, coordinates are given from the client viewport.
483
+ * But if 'page' is given, they are provided relative to the page.
484
+ *
485
+ * @return {TextRectangle} - The client rects.
486
+ */
487
+ rect : function(from){
488
+ var rect = this.range.getBoundingClientRect();
489
+ // for some reason in webkit this gets a better value
490
+ if(!rect.height && !rect.width){
491
+ rect = this.range.getClientRects()[0]
492
+ }
493
+ if(from === 'page'){
494
+ // Add the scroll offset
495
+ var off = scrollOffset();
496
+ rect = $.extend({}, rect);
497
+ rect.top += off.top;
498
+ rect.left += off.left;
499
+ }
500
+ return rect;
501
+ },
502
+ /**
503
+ * `range.rects(from)` returns the client rects.
504
+ *
505
+ * @param {String} [from] how the rects coordinates should be given (viewport or page). Provide 'page' for
506
+ * rect coordinates from the page.
507
+ * @return {Array} The client rects
508
+ */
509
+ rects : function(from){
510
+ // order rects by size
511
+ var rects = $.map($.makeArray( this.range.getClientRects() ).sort(function(rect1, rect2){
512
+ return rect2.width*rect2.height - rect1.width*rect1.height;
513
+ }), function(rect){
514
+ return $.extend({}, rect)
515
+ }),
516
+ i=0,j,
517
+ len = rects.length;
518
+
519
+ // safari returns overlapping client rects
520
+ //
521
+ // - big rects can contain 2 smaller rects
522
+ // - some rects can contain 0 - width rects
523
+ // - we don't want these 0 width rects
524
+ while(i < rects.length){
525
+ var cur = rects[i],
526
+ found = false;
527
+
528
+ j = i+1;
529
+ while( j < rects.length ){
530
+ if( withinRect( cur, rects[j] ) ) {
531
+ if(!rects[j].width){
532
+ rects.splice(j,1)
533
+ } else {
534
+ found = rects[j];
535
+ break;
536
+ }
537
+ } else {
538
+ j++;
539
+ }
540
+ }
541
+
542
+
543
+ if(found){
544
+ rects.splice(i,1)
545
+ }else{
546
+ i++;
547
+ }
548
+
549
+ }
550
+ // safari will be return overlapping ranges ...
551
+ if(from == 'page'){
552
+ var off = scrollOffset();
553
+ return $.each(rects, function(ith, item){
554
+ item.top += off.top;
555
+ item.left += off.left;
556
+ })
557
+ }
558
+
559
+
560
+ return rects;
561
+ }
562
+
563
+ });
564
+ (function(){
565
+ //method branching ....
566
+ var fn = $.Range.prototype,
567
+ range = $.Range().range;
568
+
569
+ /**
570
+ * @function compare
571
+ *
572
+ * `range.compare(type, compareRange)` compares one range to another range.
573
+ *
574
+ * ## Example
575
+ *
576
+ * // compare the highlight element's start position
577
+ * // to the start of the current range
578
+ * $('#highlight')
579
+ * .range()
580
+ * .compare('START_TO_START', $.Range.current())
581
+ *
582
+ *
583
+ *
584
+ * @param {String} type Specifies the boundary of the
585
+ * range and the <code>compareRange</code> to compare.
586
+ *
587
+ * - `"START_TO_START"` - the start of the range and the start of compareRange
588
+ * - `"START_TO_END"` - the start of the range and the end of compareRange
589
+ * - `"END_TO_END"` - the end of the range and the end of compareRange
590
+ * - `"END_TO_START"` - the end of the range and the start of compareRange
591
+ *
592
+ * @param {$.Range} compareRange The other range
593
+ * to compare against.
594
+ * @return {Number} a number indicating if the range
595
+ * boundary is before,
596
+ * after, or equal to <code>compareRange</code>'s
597
+ * boundary where:
598
+ *
599
+ * - -1 - the range boundary comes before the compareRange boundary
600
+ * - 0 - the boundaries are equal
601
+ * - 1 - the range boundary comes after the compareRange boundary
602
+ */
603
+ fn.compare = range.compareBoundaryPoints ?
604
+ function(type, range){
605
+ return this.range.compareBoundaryPoints(this.window().Range[reverse( type )], range.range)
606
+ }:
607
+ function(type, range){
608
+ return this.range.compareEndPoints(convertType(type), range.range)
609
+ }
610
+
611
+ /**
612
+ * @function move
613
+ *
614
+ * `range.move([referenceRange])` moves the endpoints of a range relative to another range.
615
+ *
616
+ * // Move the current selection's end to the
617
+ * // end of the #highlight element
618
+ * $.Range.current().move('END_TO_END',
619
+ * $('#highlight').range() )
620
+ *
621
+ *
622
+ * @param {String} type a string indicating the ranges boundary point
623
+ * to move to which referenceRange boundary point where:
624
+ *
625
+ * - `"START_TO_START"` - the start of the range moves to the start of referenceRange
626
+ * - `"START\_TO\_END"` - the start of the range move to the end of referenceRange
627
+ * - `"END_TO_END"` - the end of the range moves to the end of referenceRange
628
+ * - `"END_TO_START"` - the end of the range moves to the start of referenceRange
629
+ *
630
+ * @param {jQuery.Range} referenceRange
631
+ * @return {jQuery.Range} the original range for chaining
632
+ */
633
+ fn.move = range.setStart ?
634
+ function(type, range){
635
+
636
+ var rangesRange = range.range;
637
+ switch(type){
638
+ case "START_TO_END" :
639
+ this.range.setStart(rangesRange.endContainer, rangesRange.endOffset)
640
+ break;
641
+ case "START_TO_START" :
642
+ this.range.setStart(rangesRange.startContainer, rangesRange.startOffset)
643
+ break;
644
+ case "END_TO_END" :
645
+ this.range.setEnd(rangesRange.endContainer, rangesRange.endOffset)
646
+ break;
647
+ case "END_TO_START" :
648
+ this.range.setEnd(rangesRange.startContainer, rangesRange.startOffset)
649
+ break;
650
+ }
651
+
652
+ return this;
653
+ }:
654
+ function(type, range){
655
+ this.range.setEndPoint(convertType(type), range.range)
656
+ return this;
657
+ };
658
+ var cloneFunc = range.cloneRange ? "cloneRange" : "duplicate",
659
+ selectFunc = range.selectNodeContents ? "selectNodeContents" : "moveToElementText";
660
+
661
+ fn.
662
+ /**
663
+ * `range.clone()` clones the range and returns a new $.Range
664
+ * object:
665
+ *
666
+ * var range = new $.Range(document.getElementById('text'));
667
+ * var newRange = range.clone();
668
+ * range.start('+2');
669
+ * range.select();
670
+ *
671
+ * @return {jQuery.Range} returns the range as a $.Range.
672
+ */
673
+ clone = function(){
674
+ return $.Range( this.range[cloneFunc]() );
675
+ };
676
+
677
+ fn.
678
+ /**
679
+ * @function
680
+ *
681
+ * `range.select([el])` selects an element with this range. If nothing
682
+ * is provided, makes the current range appear as if the user has selected it.
683
+ *
684
+ * This works with text nodes. For example with:
685
+ *
686
+ * <div id="text">This is a text</div>
687
+ *
688
+ * $.Range can select `is a` like this:
689
+ *
690
+ * var range = new $.Range(document.getElementById('text'));
691
+ * range.start('+5');
692
+ * range.end('-5');
693
+ * range.select();
694
+ *
695
+ * @param {HTMLElement} [el] The element in which this range should be selected
696
+ * @return {jQuery.Range} the range for chaining.
697
+ */
698
+ select = range.selectNodeContents ? function(el){
699
+ if(!el){
700
+ var selection = this.window().getSelection();
701
+ selection.removeAllRanges();
702
+ selection.addRange(this.range);
703
+ }else {
704
+ this.range.selectNodeContents(el);
705
+ }
706
+ return this;
707
+ } : function(el){
708
+ if(!el){
709
+ this.range.select()
710
+ } else if(el.nodeType === 3){
711
+ //select this node in the element ...
712
+ var parent = el.parentNode,
713
+ start = 0,
714
+ end;
715
+ iterate(parent.childNodes, function(txtNode){
716
+ if(txtNode === el){
717
+ end = start + txtNode.nodeValue.length;
718
+ return false;
719
+ } else {
720
+ start = start + txtNode.nodeValue.length
721
+ }
722
+ })
723
+ this.range.moveToElementText(parent);
724
+
725
+ this.range.moveEnd('character', end - this.range.text.length)
726
+ this.range.moveStart('character', start);
727
+ } else {
728
+ this.range.moveToElementText(el);
729
+ }
730
+ return this;
731
+ };
732
+
733
+ })();
734
+
735
+
736
+ // helpers -----------------
737
+
738
+ // iterates through a list of elements, calls cb on every text node
739
+ // if cb returns false, exits the iteration
740
+ var iterate = function(elems, cb){
741
+ var elem, start;
742
+ for (var i = 0; elems[i]; i++) {
743
+ elem = elems[i];
744
+ // Get the text from text nodes and CDATA nodes
745
+ if (elem.nodeType === 3 || elem.nodeType === 4) {
746
+ if (cb(elem) === false) {
747
+ return false;
748
+ }
749
+ // Traverse everything else, except comment nodes
750
+ }
751
+ else
752
+ if (elem.nodeType !== 8) {
753
+ if (iterate(elem.childNodes, cb) === false) {
754
+ return false;
755
+ }
756
+ }
757
+ }
758
+
759
+ },
760
+ isText = function(node){
761
+ return node.nodeType === 3 || node.nodeType === 4
762
+ },
763
+ iteratorMaker = function(toChildren, toNext){
764
+ return function( node, mustMoveRight ) {
765
+ // first try down
766
+ if(node[toChildren] && !mustMoveRight){
767
+ return isText(node[toChildren]) ?
768
+ node[toChildren] :
769
+ arguments.callee(node[toChildren])
770
+ } else if(node[toNext]) {
771
+ return isText(node[toNext]) ?
772
+ node[toNext] :
773
+ arguments.callee(node[toNext])
774
+ } else if(node.parentNode){
775
+ return arguments.callee(node.parentNode, true)
776
+ }
777
+ }
778
+ },
779
+ getNextTextNode = iteratorMaker("firstChild","nextSibling"),
780
+ getPrevTextNode = iteratorMaker("lastChild","previousSibling"),
781
+ callMove = function(container, offset, howMany){
782
+ if(isText(container)){
783
+ return move(container, offset+howMany)
784
+ } else {
785
+ return container.childNodes[offset] ?
786
+ move(container.childNodes[offset] , howMany) :
787
+ move(container.lastChild, howMany , true)
788
+ return
789
+ }
790
+ },
791
+ move = function(from, howMany){
792
+ var mover = howMany < 0 ?
793
+ getPrevTextNode : getNextTextNode;
794
+
795
+ howMany = Math.abs(howMany);
796
+
797
+ if(!isText(from)){
798
+ from = mover(from)
799
+ }
800
+ while(from && howMany >= from.nodeValue.length){
801
+ hasMany = howMany- from.nodeValue.length;
802
+ from = mover(from)
803
+ }
804
+ return {
805
+ node: from,
806
+ offset: mover === getNextTextNode ?
807
+ howMany :
808
+ from.nodeValue.length - howMany
809
+ }
810
+ },
811
+ supportWhitespace,
812
+ isWhitespace = function(el){
813
+ if(supportWhitespace == null){
814
+ supportWhitespace = 'isElementContentWhitespace' in el;
815
+ }
816
+ return (supportWhitespace? el.isElementContentWhitespace :
817
+ (el.nodeType === 3 && '' == el.data.trim()));
818
+
819
+ },
820
+ // if a point is within a rectangle
821
+ within = function(rect, point){
822
+
823
+ return rect.left <= point.clientX && rect.left + rect.width >= point.clientX &&
824
+ rect.top <= point.clientY &&
825
+ rect.top + rect.height >= point.clientY
826
+ },
827
+ // if a rectangle is within another rectangle
828
+ withinRect = function(outer, inner){
829
+ return within(outer, {
830
+ clientX: inner.left,
831
+ clientY: inner.top
832
+ }) && //top left
833
+ within(outer, {
834
+ clientX: inner.left + inner.width,
835
+ clientY: inner.top
836
+ }) && //top right
837
+ within(outer, {
838
+ clientX: inner.left,
839
+ clientY: inner.top + inner.height
840
+ }) && //bottom left
841
+ within(outer, {
842
+ clientX: inner.left + inner.width,
843
+ clientY: inner.top + inner.height
844
+ }) //bottom right
845
+ },
846
+ // gets the scroll offset from a window
847
+ scrollOffset = function( win){
848
+ var win = win ||window;
849
+ doc = win.document.documentElement, body = win.document.body;
850
+
851
+ return {
852
+ left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
853
+ top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
854
+ };
855
+ };
856
+
857
+
858
+ support.moveToPoint = !!$.Range().range.moveToPoint
859
+
860
+
861
+ })(jQuery)