syn-rails 3.2.3

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b86cd0674b8c13869e2b0ae4eb8d4559565f268
4
+ data.tar.gz: 4d8ebccb96bf513bb75d2beaec15dfbe4dcc21d8
5
+ SHA512:
6
+ metadata.gz: 1689d37173ac12e5f84666f60b1baf97c09ee7bd00e4107cf5f6c620de798d84822e56187defa4505c9212d67c7af74537d50a3cdbdbb1ab424227a4fc0a646e
7
+ data.tar.gz: 868b0fc65869ddd5bb868288bb44c7c91f443a2722b50d575ae105593bb257f8e4881a89769c3fd9d9ad460886e468be55471c0753cb68d3985696c5677d52b4
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in syn-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Ivan Turkovic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ syn-rails
2
+ =========
3
+
4
+ syn.js asset-pipeline provider/wrapper
5
+
6
+
7
+ syn.js is the Standalone Synthetic Event Library.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ require "syn-rails/version"
2
+
3
+ module Syn
4
+ module Rails
5
+ if defined?(::Rails) and Gem::Requirement.new('>= 3.1').satisfied_by?(Gem::Version.new ::Rails.version)
6
+ class Rails::Engine < ::Rails::Engine
7
+ # this class enables the asset pipeline
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Syn
2
+ module Rails
3
+ VERSION = "3.2.3"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "syn-rails/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "syn-rails"
7
+ s.version = Syn::Rails::VERSION
8
+ s.authors = ["Ivan Turkovic"]
9
+ s.email = ["ivan.turkovic@gmail.com"]
10
+ s.homepage = "https://github.com/Thorsson/syn-rails"
11
+ s.summary = %q{syn.js asset pipeline provider/wrapper}
12
+ s.license = "MIT"
13
+
14
+ s.rubyforge_project = "syn-rails"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ end
@@ -0,0 +1,2491 @@
1
+ // Use this file to load all required javascript files for the assets pipeline
2
+ (function(){
3
+ var extend = function( d, s ) {
4
+ var p;
5
+ for (p in s) {
6
+ d[p] = s[p];
7
+ }
8
+ return d;
9
+ },
10
+ // only uses browser detection for key events
11
+ browser = {
12
+ msie: !! (window.attachEvent && !window.opera),
13
+ opera: !! window.opera,
14
+ webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
15
+ safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') === -1,
16
+ gecko: navigator.userAgent.indexOf('Gecko') > -1,
17
+ mobilesafari: !! navigator.userAgent.match(/Apple.*Mobile.*Safari/),
18
+ rhino: navigator.userAgent.match(/Rhino/) && true
19
+ },
20
+ createEventObject = function( type, options, element ) {
21
+ var event = element.ownerDocument.createEventObject();
22
+ return extend(event, options);
23
+ },
24
+ data = {},
25
+ id = 1,
26
+ expando = "_synthetic" + new Date().getTime(),
27
+ bind, unbind, key = /keypress|keyup|keydown/,
28
+ page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/,
29
+ //this is maintained so we can click on html and blur the active element
30
+ activeElement,
31
+
32
+ /**
33
+ * @class Syn
34
+ * @download funcunit/dist/syn.js
35
+ * @test funcunit/synthetic/qunit.html
36
+ * Syn is used to simulate user actions. It creates synthetic events and
37
+ * performs their default behaviors.
38
+ *
39
+ * <h2>Basic Use</h2>
40
+ * The following clicks an input element with <code>id='description'</code>
41
+ * and then types <code>'Hello World'</code>.
42
+ *
43
+ @codestart
44
+ Syn.click({},'description')
45
+ .type("Hello World")
46
+ @codeend
47
+ * <h2>User Actions and Events</h2>
48
+ * <p>Syn is typically used to simulate user actions as opposed to triggering events. Typing characters
49
+ * is an example of a user action. The keypress that represents an <code>'a'</code>
50
+ * character being typed is an example of an event.
51
+ * </p>
52
+ * <p>
53
+ * While triggering events is supported, it's much more useful to simulate actual user behavior. The
54
+ * following actions are supported by Syn:
55
+ * </p>
56
+ * <ul>
57
+ * <li><code>[Syn.prototype.click click]</code> - a mousedown, focus, mouseup, and click.</li>
58
+ * <li><code>[Syn.prototype.dblclick dblclick]</code> - two <code>click!</code> events followed by a <code>dblclick</code>.</li>
59
+ * <li><code>[Syn.prototype.key key]</code> - types a single character (keydown, keypress, keyup).</li>
60
+ * <li><code>[Syn.prototype.type type]</code> - types multiple characters into an element.</li>
61
+ * <li><code>[Syn.prototype.move move]</code> - moves the mouse from one position to another (triggering mouseover / mouseouts).</li>
62
+ * <li><code>[Syn.prototype.drag drag]</code> - a mousedown, followed by mousemoves, and a mouseup.</li>
63
+ * </ul>
64
+ * All actions run asynchronously.
65
+ * Click on the links above for more
66
+ * information on how to use the specific action.
67
+ * <h2>Asynchronous Callbacks</h2>
68
+ * Actions don't complete immediately. This is almost
69
+ * entirely because <code>focus()</code>
70
+ * doesn't run immediately in IE.
71
+ * If you provide a callback function to Syn, it will
72
+ * be called after the action is completed.
73
+ * <br/>The following checks that "Hello World" was entered correctly:
74
+ @codestart
75
+ Syn.click({},'description')
76
+ .type("Hello World", function(){
77
+
78
+ ok("Hello World" == document.getElementById('description').value)
79
+ })
80
+ @codeend
81
+ <h2>Asynchronous Chaining</h2>
82
+ <p>You might have noticed the [Syn.prototype.then then] method. It provides chaining
83
+ so you can do a sequence of events with a single (final) callback.
84
+ </p><p>
85
+ If an element isn't provided to then, it uses the previous Syn's element.
86
+ </p>
87
+ The following does a lot of stuff before checking the result:
88
+ @codestart
89
+ Syn.type('ice water','title')
90
+ .type('ice and water','description')
91
+ .click({},'create')
92
+ .drag({to: 'favorites'},'newRecipe',
93
+ function(){
94
+ ok($('#newRecipe').parents('#favorites').length);
95
+ })
96
+ @codeend
97
+
98
+ <h2>jQuery Helper</h2>
99
+ If jQuery is present, Syn adds a triggerSyn helper you can use like:
100
+ @codestart
101
+ $("#description").triggerSyn("type","Hello World");
102
+ @codeend
103
+ * <h2>Key Event Recording</h2>
104
+ * <p>Every browser has very different rules for dispatching key events.
105
+ * As there is no way to feature detect how a browser handles key events,
106
+ * synthetic uses a description of how the browser behaves generated
107
+ * by a recording application. </p>
108
+ * <p>
109
+ * If you want to support a browser not currently supported, you can
110
+ * record that browser's key event description and add it to
111
+ * <code>Syn.key.browsers</code> by it's navigator agent.
112
+ * </p>
113
+ @codestart
114
+ Syn.key.browsers["Envjs\ Resig/20070309 PilotFish/1.2.0.10\1.6"] = {
115
+ 'prevent':
116
+ {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
117
+ 'character':
118
+ { ... }
119
+ }
120
+ @codeend
121
+ * <h2>Limitations</h2>
122
+ * Syn fully supports IE 6+, FF 3+, Chrome, Safari, Opera 10+.
123
+ * With FF 1+, drag / move events are only partially supported. They will
124
+ * not trigger mouseover / mouseout events.<br/>
125
+ * Safari crashes when a mousedown is triggered on a select. Syn will not
126
+ * create this event.
127
+ * <h2>Contributing to Syn</h2>
128
+ * Have we missed something? We happily accept patches. The following are
129
+ * important objects and properties of Syn:
130
+ * <ul>
131
+ * <li><code>Syn.create</code> - contains methods to setup, convert options, and create an event of a specific type.</li>
132
+ * <li><code>Syn.defaults</code> - default behavior by event type (except for keys).</li>
133
+ * <li><code>Syn.key.defaults</code> - default behavior by key.</li>
134
+ * <li><code>Syn.keycodes</code> - supported keys you can type.</li>
135
+ * </ul>
136
+ * <h2>Roll Your Own Functional Test Framework</h2>
137
+ * <p>Syn is really the foundation of JavaScriptMVC's functional testing framework - [FuncUnit].
138
+ * But, we've purposely made Syn work without any dependencies in the hopes that other frameworks or
139
+ * testing solutions can use it as well.
140
+ * </p>
141
+ * @constructor
142
+ * Creates a synthetic event on the element.
143
+ * @param {Object} type
144
+ * @param {Object} options
145
+ * @param {Object} element
146
+ * @param {Object} callback
147
+ * @return Syn
148
+ */
149
+ Syn = function( type, options, element, callback ) {
150
+ return (new Syn.init(type, options, element, callback));
151
+ };
152
+
153
+ bind = function( el, ev, f ) {
154
+ return el.addEventListener ? el.addEventListener(ev, f, false) : el.attachEvent("on" + ev, f);
155
+ };
156
+ unbind = function( el, ev, f ) {
157
+ return el.addEventListener ? el.removeEventListener(ev, f, false) : el.detachEvent("on" + ev, f);
158
+ };
159
+
160
+ /**
161
+ * @Static
162
+ */
163
+ extend(Syn, {
164
+ /**
165
+ * Creates a new synthetic event instance
166
+ * @hide
167
+ * @param {Object} type
168
+ * @param {Object} options
169
+ * @param {Object} element
170
+ * @param {Object} callback
171
+ */
172
+ init: function( type, options, element, callback ) {
173
+ var args = Syn.args(options, element, callback),
174
+ self = this;
175
+ this.queue = [];
176
+ this.element = args.element;
177
+
178
+ //run event
179
+ if ( typeof this[type] === "function" ) {
180
+ this[type](args.options, args.element, function( defaults, el ) {
181
+ args.callback && args.callback.apply(self, arguments);
182
+ self.done.apply(self, arguments);
183
+ });
184
+ } else {
185
+ this.result = Syn.trigger(type, args.options, args.element);
186
+ args.callback && args.callback.call(this, args.element, this.result);
187
+ }
188
+ },
189
+ jquery: function( el, fast ) {
190
+ if ( window.FuncUnit && window.FuncUnit.jquery ) {
191
+ return window.FuncUnit.jquery;
192
+ }
193
+ if ( el ) {
194
+ return Syn.helpers.getWindow(el).jQuery || window.jQuery;
195
+ }
196
+ else {
197
+ return window.jQuery;
198
+ }
199
+ },
200
+ /**
201
+ * Returns an object with the args for a Syn.
202
+ * @hide
203
+ * @return {Object}
204
+ */
205
+ args: function() {
206
+ var res = {},
207
+ i = 0;
208
+ for ( ; i < arguments.length; i++ ) {
209
+ if ( typeof arguments[i] === 'function' ) {
210
+ res.callback = arguments[i];
211
+ } else if ( arguments[i] && arguments[i].jquery ) {
212
+ res.element = arguments[i][0];
213
+ } else if ( arguments[i] && arguments[i].nodeName ) {
214
+ res.element = arguments[i];
215
+ } else if ( res.options && typeof arguments[i] === 'string' ) { //we can get by id
216
+ res.element = document.getElementById(arguments[i]);
217
+ }
218
+ else if ( arguments[i] ) {
219
+ res.options = arguments[i];
220
+ }
221
+ }
222
+ return res;
223
+ },
224
+ click: function( options, element, callback ) {
225
+ Syn('click!', options, element, callback);
226
+ },
227
+ /**
228
+ * @attribute defaults
229
+ * Default actions for events. Each default function is called with this as its
230
+ * element. It should return true if a timeout
231
+ * should happen after it. If it returns an element, a timeout will happen
232
+ * and the next event will happen on that element.
233
+ */
234
+ defaults: {
235
+ focus: function() {
236
+ if (!Syn.support.focusChanges ) {
237
+ var element = this,
238
+ nodeName = element.nodeName.toLowerCase();
239
+ Syn.data(element, "syntheticvalue", element.value);
240
+
241
+ //TODO, this should be textarea too
242
+ //and this might be for only text style inputs ... hmmmmm ....
243
+ if ( nodeName === "input" || nodeName === "textarea" ) {
244
+ bind(element, "blur", function() {
245
+ if ( Syn.data(element, "syntheticvalue") != element.value ) {
246
+
247
+ Syn.trigger("change", {}, element);
248
+ }
249
+ unbind(element, "blur", arguments.callee);
250
+ });
251
+
252
+ }
253
+ }
254
+ },
255
+ submit: function() {
256
+ Syn.onParents(this, function( el ) {
257
+ if ( el.nodeName.toLowerCase() === 'form' ) {
258
+ el.submit();
259
+ return false;
260
+ }
261
+ });
262
+ }
263
+ },
264
+ changeOnBlur: function( element, prop, value ) {
265
+
266
+ bind(element, "blur", function() {
267
+ if ( value !== element[prop] ) {
268
+ Syn.trigger("change", {}, element);
269
+ }
270
+ unbind(element, "blur", arguments.callee);
271
+ });
272
+
273
+ },
274
+ /**
275
+ * Returns the closest element of a particular type.
276
+ * @hide
277
+ * @param {Object} el
278
+ * @param {Object} type
279
+ */
280
+ closest: function( el, type ) {
281
+ while ( el && el.nodeName.toLowerCase() !== type.toLowerCase() ) {
282
+ el = el.parentNode;
283
+ }
284
+ return el;
285
+ },
286
+ /**
287
+ * adds jQuery like data (adds an expando) and data exists FOREVER :)
288
+ * @hide
289
+ * @param {Object} el
290
+ * @param {Object} key
291
+ * @param {Object} value
292
+ */
293
+ data: function( el, key, value ) {
294
+ var d;
295
+ if (!el[expando] ) {
296
+ el[expando] = id++;
297
+ }
298
+ if (!data[el[expando]] ) {
299
+ data[el[expando]] = {};
300
+ }
301
+ d = data[el[expando]];
302
+ if ( value ) {
303
+ data[el[expando]][key] = value;
304
+ } else {
305
+ return data[el[expando]][key];
306
+ }
307
+ },
308
+ /**
309
+ * Calls a function on the element and all parents of the element until the function returns
310
+ * false.
311
+ * @hide
312
+ * @param {Object} el
313
+ * @param {Object} func
314
+ */
315
+ onParents: function( el, func ) {
316
+ var res;
317
+ while ( el && res !== false ) {
318
+ res = func(el);
319
+ el = el.parentNode;
320
+ }
321
+ return el;
322
+ },
323
+ //regex to match focusable elements
324
+ focusable: /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i,
325
+ /**
326
+ * Returns if an element is focusable
327
+ * @hide
328
+ * @param {Object} elem
329
+ */
330
+ isFocusable: function( elem ) {
331
+ var attributeNode;
332
+ return (this.focusable.test(elem.nodeName) ||
333
+ ((attributeNode = elem.getAttributeNode("tabIndex"))
334
+ && attributeNode.specified)) && Syn.isVisible(elem);
335
+ },
336
+ /**
337
+ * Returns if an element is visible or not
338
+ * @hide
339
+ * @param {Object} elem
340
+ */
341
+ isVisible: function( elem ) {
342
+ return (elem.offsetWidth && elem.offsetHeight) || (elem.clientWidth && elem.clientHeight);
343
+ },
344
+ /**
345
+ * Gets the tabIndex as a number or null
346
+ * @hide
347
+ * @param {Object} elem
348
+ */
349
+ tabIndex: function( elem ) {
350
+ var attributeNode = elem.getAttributeNode("tabIndex");
351
+ return attributeNode && attributeNode.specified && (parseInt(elem.getAttribute('tabIndex')) || 0);
352
+ },
353
+ bind: bind,
354
+ unbind: unbind,
355
+ browser: browser,
356
+ //some generic helpers
357
+ helpers: {
358
+ createEventObject: createEventObject,
359
+ createBasicStandardEvent: function( type, defaults, doc ) {
360
+ var event;
361
+ try {
362
+ event = doc.createEvent("Events");
363
+ } catch (e2) {
364
+ event = doc.createEvent("UIEvents");
365
+ } finally {
366
+ event.initEvent(type, true, true);
367
+ extend(event, defaults);
368
+ }
369
+ return event;
370
+ },
371
+ inArray: function( item, array ) {
372
+ var i =0;
373
+ for ( ; i < array.length; i++ ) {
374
+ if ( array[i] === item ) {
375
+ return i;
376
+ }
377
+ }
378
+ return -1;
379
+ },
380
+ getWindow: function( element ) {
381
+ return element.ownerDocument.defaultView || element.ownerDocument.parentWindow;
382
+ },
383
+ extend: extend,
384
+ scrollOffset: function( win , set) {
385
+ var doc = win.document.documentElement,
386
+ body = win.document.body;
387
+ if(set){
388
+ window.scrollTo(set.left, set.top);
389
+
390
+ } else {
391
+ return {
392
+ left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
393
+ top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
394
+ };
395
+ }
396
+
397
+ },
398
+ scrollDimensions: function(win){
399
+ var doc = win.document.documentElement,
400
+ body = win.document.body,
401
+ docWidth = doc.clientWidth,
402
+ docHeight = doc.clientHeight,
403
+ compat = win.document.compatMode === "CSS1Compat";
404
+
405
+ return {
406
+ height: compat && docHeight ||
407
+ body.clientHeight || docHeight,
408
+ width: compat && docWidth ||
409
+ body.clientWidth || docWidth
410
+ };
411
+ },
412
+ addOffset: function( options, el ) {
413
+ var jq = Syn.jquery(el),
414
+ off;
415
+ if ( typeof options === 'object' && options.clientX === undefined && options.clientY === undefined && options.pageX === undefined && options.pageY === undefined && jq ) {
416
+ el = jq(el);
417
+ off = el.offset();
418
+ options.pageX = off.left + el.width() / 2;
419
+ options.pageY = off.top + el.height() / 2;
420
+ }
421
+ }
422
+ },
423
+ // place for key data
424
+ key: {
425
+ ctrlKey: null,
426
+ altKey: null,
427
+ shiftKey: null,
428
+ metaKey: null
429
+ },
430
+ //triggers an event on an element, returns true if default events should be run
431
+ /**
432
+ * Dispatches an event and returns true if default events should be run.
433
+ * @hide
434
+ * @param {Object} event
435
+ * @param {Object} element
436
+ * @param {Object} type
437
+ * @param {Object} autoPrevent
438
+ */
439
+ dispatch: function( event, element, type, autoPrevent ) {
440
+
441
+ // dispatchEvent doesn't always work in IE (mostly in a popup)
442
+ if ( element.dispatchEvent && event ) {
443
+ var preventDefault = event.preventDefault,
444
+ prevents = autoPrevent ? -1 : 0;
445
+
446
+ //automatically prevents the default behavior for this event
447
+ //this is to protect agianst nasty browser freezing bug in safari
448
+ if ( autoPrevent ) {
449
+ bind(element, type, function( ev ) {
450
+ ev.preventDefault();
451
+ unbind(this, type, arguments.callee);
452
+ });
453
+ }
454
+
455
+
456
+ event.preventDefault = function() {
457
+ prevents++;
458
+ if (++prevents > 0 ) {
459
+ preventDefault.apply(this, []);
460
+ }
461
+ };
462
+ element.dispatchEvent(event);
463
+ return prevents <= 0;
464
+ } else {
465
+ try {
466
+ window.event = event;
467
+ } catch (e) {}
468
+ //source element makes sure element is still in the document
469
+ return element.sourceIndex <= 0 || (element.fireEvent && element.fireEvent('on' + type, event));
470
+ }
471
+ },
472
+ /**
473
+ * @attribute
474
+ * @hide
475
+ * An object of eventType -> function that create that event.
476
+ */
477
+ create: {
478
+ //-------- PAGE EVENTS ---------------------
479
+ page: {
480
+ event: function( type, options, element ) {
481
+ var doc = Syn.helpers.getWindow(element).document || document,
482
+ event;
483
+ if ( doc.createEvent ) {
484
+ event = doc.createEvent("Events");
485
+
486
+ event.initEvent(type, true, true);
487
+ return event;
488
+ }
489
+ else {
490
+ try {
491
+ event = createEventObject(type, options, element);
492
+ }
493
+ catch (e) {}
494
+ return event;
495
+ }
496
+ }
497
+ },
498
+ // unique events
499
+ focus: {
500
+ event: function( type, options, element ) {
501
+ Syn.onParents(element, function( el ) {
502
+ if ( Syn.isFocusable(el) ) {
503
+ if ( el.nodeName.toLowerCase() !== 'html' ) {
504
+ el.focus();
505
+ activeElement = el;
506
+ }
507
+ else if ( activeElement ) {
508
+ // TODO: The HTML element isn't focasable in IE, but it is
509
+ // in FF. We should detect this and do a true focus instead
510
+ // of just a blur
511
+ var doc = Syn.helpers.getWindow(element).document;
512
+ if ( doc !== window.document ) {
513
+ return false;
514
+ } else if ( doc.activeElement ) {
515
+ doc.activeElement.blur();
516
+ activeElement = null;
517
+ } else {
518
+ activeElement.blur();
519
+ activeElement = null;
520
+ }
521
+
522
+
523
+ }
524
+ return false;
525
+ }
526
+ });
527
+ return true;
528
+ }
529
+ }
530
+ },
531
+ /**
532
+ * @attribute support
533
+ * Feature detected properties of a browser's event system.
534
+ * Support has the following properties:
535
+ * <ul>
536
+ * <li><code>clickChanges</code> - clicking on an option element creates a change event.</li>
537
+ * <li><code>clickSubmits</code> - clicking on a form button submits the form.</li>
538
+ * <li><code>mouseupSubmits</code> - a mouseup on a form button submits the form.</li>
539
+ * <li><code>radioClickChanges</code> - clicking a radio button changes the radio.</li>
540
+ * <li><code>focusChanges</code> - focus/blur creates a change event.</li>
541
+ * <li><code>linkHrefJS</code> - An achor's href JavaScript is run.</li>
542
+ * <li><code>mouseDownUpClicks</code> - A mousedown followed by mouseup creates a click event.</li>
543
+ * <li><code>tabKeyTabs</code> - A tab key changes tabs.</li>
544
+ * <li><code>keypressOnAnchorClicks</code> - Keying enter on an anchor triggers a click.</li>
545
+ * </ul>
546
+ */
547
+ support: {
548
+ clickChanges: false,
549
+ clickSubmits: false,
550
+ keypressSubmits: false,
551
+ mouseupSubmits: false,
552
+ radioClickChanges: false,
553
+ focusChanges: false,
554
+ linkHrefJS: false,
555
+ keyCharacters: false,
556
+ backspaceWorks: false,
557
+ mouseDownUpClicks: false,
558
+ tabKeyTabs: false,
559
+ keypressOnAnchorClicks: false,
560
+ optionClickBubbles: false,
561
+ ready: 0
562
+ },
563
+ /**
564
+ * Creates a synthetic event and dispatches it on the element.
565
+ * This will run any default actions for the element.
566
+ * Typically you want to use Syn, but if you want the return value, use this.
567
+ * @param {String} type
568
+ * @param {Object} options
569
+ * @param {HTMLElement} element
570
+ * @return {Boolean} true if default events were run, false if otherwise.
571
+ */
572
+ trigger: function( type, options, element ) {
573
+ options || (options = {});
574
+
575
+ var create = Syn.create,
576
+ setup = create[type] && create[type].setup,
577
+ kind = key.test(type) ? 'key' : (page.test(type) ? "page" : "mouse"),
578
+ createType = create[type] || {},
579
+ createKind = create[kind],
580
+ event, ret, autoPrevent, dispatchEl = element;
581
+
582
+ //any setup code?
583
+ Syn.support.ready === 2 && setup && setup(type, options, element);
584
+
585
+ autoPrevent = options._autoPrevent;
586
+ //get kind
587
+ delete options._autoPrevent;
588
+
589
+ if ( createType.event ) {
590
+ ret = createType.event(type, options, element);
591
+ } else {
592
+ //convert options
593
+ options = createKind.options ? createKind.options(type, options, element) : options;
594
+
595
+ if (!Syn.support.changeBubbles && /option/i.test(element.nodeName) ) {
596
+ dispatchEl = element.parentNode; //jQuery expects clicks on select
597
+ }
598
+
599
+ //create the event
600
+ event = createKind.event(type, options, dispatchEl);
601
+
602
+ //send the event
603
+ ret = Syn.dispatch(event, dispatchEl, type, autoPrevent);
604
+ }
605
+
606
+ //run default behavior
607
+ ret && Syn.support.ready === 2 && Syn.defaults[type] && Syn.defaults[type].call(element, options, autoPrevent);
608
+ return ret;
609
+ },
610
+ eventSupported: function( eventName ) {
611
+ var el = document.createElement("div");
612
+ eventName = "on" + eventName;
613
+
614
+ var isSupported = (eventName in el);
615
+ if (!isSupported ) {
616
+ el.setAttribute(eventName, "return;");
617
+ isSupported = typeof el[eventName] === "function";
618
+ }
619
+ el = null;
620
+
621
+ return isSupported;
622
+ }
623
+
624
+ });
625
+ /**
626
+ * @Prototype
627
+ */
628
+ extend(Syn.init.prototype, {
629
+ /**
630
+ * @function then
631
+ * <p>
632
+ * Then is used to chain a sequence of actions to be run one after the other.
633
+ * This is useful when many asynchronous actions need to be performed before some
634
+ * final check needs to be made.
635
+ * </p>
636
+ * <p>The following clicks and types into the <code>id='age'</code> element and then checks that only numeric characters can be entered.</p>
637
+ * <h3>Example</h3>
638
+ * @codestart
639
+ * Syn('click',{},'age')
640
+ * .then('type','I am 12',function(){
641
+ * equals($('#age').val(),"12")
642
+ * })
643
+ * @codeend
644
+ * If the element argument is undefined, then the last element is used.
645
+ *
646
+ * @param {String} type The type of event or action to create: "_click", "_dblclick", "_drag", "_type".
647
+ * @param {Object} options Optiosn to pass to the event.
648
+ * @param {String|HTMLElement} [element] A element's id or an element. If undefined, defaults to the previous element.
649
+ * @param {Function} [callback] A function to callback after the action has run, but before any future chained actions are run.
650
+ */
651
+ then: function( type, options, element, callback ) {
652
+ if ( Syn.autoDelay ) {
653
+ this.delay();
654
+ }
655
+ var args = Syn.args(options, element, callback),
656
+ self = this;
657
+
658
+
659
+ //if stack is empty run right away
660
+ //otherwise ... unshift it
661
+ this.queue.unshift(function( el, prevented ) {
662
+
663
+ if ( typeof this[type] === "function" ) {
664
+ this.element = args.element || el;
665
+ this[type](args.options, this.element, function( defaults, el ) {
666
+ args.callback && args.callback.apply(self, arguments);
667
+ self.done.apply(self, arguments);
668
+ });
669
+ } else {
670
+ this.result = Syn.trigger(type, args.options, args.element);
671
+ args.callback && args.callback.call(this, args.element, this.result);
672
+ return this;
673
+ }
674
+ })
675
+ return this;
676
+ },
677
+ /**
678
+ * Delays the next command a set timeout.
679
+ * @param {Number} [timeout]
680
+ * @param {Function} [callback]
681
+ */
682
+ delay: function( timeout, callback ) {
683
+ if ( typeof timeout === 'function' ) {
684
+ callback = timeout;
685
+ timeout = null;
686
+ }
687
+ timeout = timeout || 600;
688
+ var self = this;
689
+ this.queue.unshift(function() {
690
+ setTimeout(function() {
691
+ callback && callback.apply(self, [])
692
+ self.done.apply(self, arguments);
693
+ }, timeout);
694
+ });
695
+ return this;
696
+ },
697
+ done: function( defaults, el ) {
698
+ el && (this.element = el);
699
+ if ( this.queue.length ) {
700
+ this.queue.pop().call(this, this.element, defaults);
701
+ }
702
+
703
+ },
704
+ /**
705
+ * @function click
706
+ * Clicks an element by triggering a mousedown,
707
+ * mouseup,
708
+ * and a click event.
709
+ * <h3>Example</h3>
710
+ * @codestart
711
+ * Syn.click({},'create',function(){
712
+ * //check something
713
+ * })
714
+ * @codeend
715
+ * You can also provide the coordinates of the click.
716
+ * If jQuery is present, it will set clientX and clientY
717
+ * for you. Here's how to set it yourself:
718
+ * @codestart
719
+ * Syn.click(
720
+ * {clientX: 20, clientY: 100},
721
+ * 'create',
722
+ * function(){
723
+ * //check something
724
+ * })
725
+ * @codeend
726
+ * You can also provide pageX and pageY and Syn will convert it for you.
727
+ * @param {Object} options
728
+ * @param {HTMLElement} element
729
+ * @param {Function} callback
730
+ */
731
+ "_click": function( options, element, callback, force ) {
732
+ Syn.helpers.addOffset(options, element);
733
+ Syn.trigger("mousedown", options, element);
734
+
735
+ //timeout is b/c IE is stupid and won't call focus handlers
736
+ setTimeout(function() {
737
+ Syn.trigger("mouseup", options, element);
738
+ if (!Syn.support.mouseDownUpClicks || force ) {
739
+ Syn.trigger("click", options, element);
740
+ callback(true);
741
+ } else {
742
+ //we still have to run the default (presumably)
743
+ Syn.create.click.setup('click', options, element);
744
+ Syn.defaults.click.call(element);
745
+ //must give time for callback
746
+ setTimeout(function() {
747
+ callback(true);
748
+ }, 1);
749
+ }
750
+
751
+ }, 1);
752
+ },
753
+ /**
754
+ * Right clicks in browsers that support it (everyone but opera).
755
+ * @param {Object} options
756
+ * @param {Object} element
757
+ * @param {Object} callback
758
+ */
759
+ "_rightClick": function( options, element, callback ) {
760
+ Syn.helpers.addOffset(options, element);
761
+ var mouseopts = extend(extend({}, Syn.mouse.browser.right.mouseup), options);
762
+
763
+ Syn.trigger("mousedown", mouseopts, element);
764
+
765
+ //timeout is b/c IE is stupid and won't call focus handlers
766
+ setTimeout(function() {
767
+ Syn.trigger("mouseup", mouseopts, element);
768
+ if ( Syn.mouse.browser.right.contextmenu ) {
769
+ Syn.trigger("contextmenu", extend(extend({}, Syn.mouse.browser.right.contextmenu), options), element);
770
+ }
771
+ callback(true);
772
+ }, 1);
773
+ },
774
+ /**
775
+ * @function dblclick
776
+ * Dblclicks an element. This runs two [Syn.prototype.click click] events followed by
777
+ * a dblclick on the element.
778
+ * <h3>Example</h3>
779
+ * @codestart
780
+ * Syn.dblclick({},'open')
781
+ * @codeend
782
+ * @param {Object} options
783
+ * @param {HTMLElement} element
784
+ * @param {Function} callback
785
+ */
786
+ "_dblclick": function( options, element, callback ) {
787
+ Syn.helpers.addOffset(options, element);
788
+ var self = this;
789
+ this._click(options, element, function() {
790
+ setTimeout(function() {
791
+ self._click(options, element, function() {
792
+ Syn.trigger("dblclick", options, element);
793
+ callback(true);
794
+ }, true);
795
+ }, 2);
796
+
797
+ });
798
+ }
799
+ });
800
+
801
+ var actions = ["click", "dblclick", "move", "drag", "key", "type", 'rightClick'],
802
+ makeAction = function( name ) {
803
+ Syn[name] = function( options, element, callback ) {
804
+ return Syn("_" + name, options, element, callback);
805
+ };
806
+ Syn.init.prototype[name] = function( options, element, callback ) {
807
+ return this.then("_" + name, options, element, callback);
808
+ };
809
+ },
810
+ i = 0;
811
+ for ( ; i < actions.length; i++ ) {
812
+ makeAction(actions[i]);
813
+ }
814
+ /**
815
+ * Used for creating and dispatching synthetic events.
816
+ * @codestart
817
+ * new MVC.Syn('click').send(MVC.$E('id'))
818
+ * @codeend
819
+ * @constructor Sets up a synthetic event.
820
+ * @param {String} type type of event, ex: 'click'
821
+ * @param {optional:Object} options
822
+ */
823
+ if ( (window.FuncUnit && window.FuncUnit.jQuery) || window.jQuery ) {
824
+ ((window.FuncUnit && window.FuncUnit.jQuery) || window.jQuery).fn.triggerSyn = function( type, options, callback ) {
825
+ Syn(type, options, this[0], callback);
826
+ return this;
827
+ };
828
+ }
829
+
830
+ window.Syn = Syn;
831
+ })(true);
832
+ (function(){
833
+ //handles mosue events
834
+
835
+ var h = Syn.helpers,
836
+ getWin = h.getWindow;
837
+
838
+ Syn.mouse = {};
839
+ h.extend(Syn.defaults, {
840
+ mousedown: function( options ) {
841
+ Syn.trigger("focus", {}, this)
842
+ },
843
+ click: function() {
844
+ // prevents the access denied issue in IE if the click causes the element to be destroyed
845
+ var element = this;
846
+ try {
847
+ element.nodeType;
848
+ } catch (e) {
849
+ return;
850
+ }
851
+ //get old values
852
+ var href, radioChanged = Syn.data(element, "radioChanged"),
853
+ scope = getWin(element),
854
+ nodeName = element.nodeName.toLowerCase();
855
+
856
+ //this code was for restoring the href attribute to prevent popup opening
857
+ //if ((href = Syn.data(element, "href"))) {
858
+ // element.setAttribute('href', href)
859
+ //}
860
+
861
+ //run href javascript
862
+ if (!Syn.support.linkHrefJS && /^\s*javascript:/.test(element.href) ) {
863
+ //eval js
864
+ var code = element.href.replace(/^\s*javascript:/, "")
865
+
866
+ //try{
867
+ if ( code != "//" && code.indexOf("void(0)") == -1 ) {
868
+ if ( window.selenium ) {
869
+ eval("with(selenium.browserbot.getCurrentWindow()){" + code + "}")
870
+ } else {
871
+ eval("with(scope){" + code + "}")
872
+ }
873
+ }
874
+ }
875
+
876
+ //submit a form
877
+ if (!(Syn.support.clickSubmits) && (nodeName == "input" && element.type == "submit") || nodeName == 'button' ) {
878
+
879
+ var form = Syn.closest(element, "form");
880
+ if ( form ) {
881
+ Syn.trigger("submit", {}, form)
882
+ }
883
+
884
+ }
885
+ //follow a link, probably needs to check if in an a.
886
+ if ( nodeName == "a" && element.href && !/^\s*javascript:/.test(element.href) ) {
887
+
888
+ scope.location.href = element.href;
889
+
890
+ }
891
+
892
+ //change a checkbox
893
+ if ( nodeName == "input" && element.type == "checkbox" ) {
894
+
895
+ //if(!Syn.support.clickChecks && !Syn.support.changeChecks){
896
+ // element.checked = !element.checked;
897
+ //}
898
+ if (!Syn.support.clickChanges ) {
899
+ Syn.trigger("change", {}, element);
900
+ }
901
+ }
902
+
903
+ //change a radio button
904
+ if ( nodeName == "input" && element.type == "radio" ) { // need to uncheck others if not checked
905
+ if ( radioChanged && !Syn.support.radioClickChanges ) {
906
+ Syn.trigger("change", {}, element);
907
+ }
908
+ }
909
+ // change options
910
+ if ( nodeName == "option" && Syn.data(element, "createChange") ) {
911
+ Syn.trigger("change", {}, element.parentNode); //does not bubble
912
+ Syn.data(element, "createChange", false)
913
+ }
914
+ }
915
+ })
916
+
917
+ //add create and setup behavior for mosue events
918
+ h.extend(Syn.create, {
919
+ mouse: {
920
+ options: function( type, options, element ) {
921
+ var doc = document.documentElement,
922
+ body = document.body,
923
+ center = [options.pageX || 0, options.pageY || 0],
924
+ //browser might not be loaded yet (doing support code)
925
+ left = Syn.mouse.browser && Syn.mouse.browser.left[type],
926
+ right = Syn.mouse.browser && Syn.mouse.browser.right[type];
927
+ return h.extend({
928
+ bubbles: true,
929
+ cancelable: true,
930
+ view: window,
931
+ detail: 1,
932
+ screenX: 1,
933
+ screenY: 1,
934
+ clientX: options.clientX || center[0] - (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0),
935
+ clientY: options.clientY || center[1] - (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0),
936
+ ctrlKey: !! Syn.key.ctrlKey,
937
+ altKey: !! Syn.key.altKey,
938
+ shiftKey: !! Syn.key.shiftKey,
939
+ metaKey: !! Syn.key.metaKey,
940
+ button: left && left.button != null ? left.button : right && right.button || (type == 'contextmenu' ? 2 : 0),
941
+ relatedTarget: document.documentElement
942
+ }, options);
943
+ },
944
+ event: function( type, defaults, element ) { //Everyone Else
945
+ var doc = getWin(element).document || document
946
+ if ( doc.createEvent ) {
947
+ var event;
948
+
949
+ try {
950
+ event = doc.createEvent('MouseEvents');
951
+ event.initMouseEvent(type, defaults.bubbles, defaults.cancelable, defaults.view, defaults.detail, defaults.screenX, defaults.screenY, defaults.clientX, defaults.clientY, defaults.ctrlKey, defaults.altKey, defaults.shiftKey, defaults.metaKey, defaults.button, defaults.relatedTarget);
952
+ } catch (e) {
953
+ event = h.createBasicStandardEvent(type, defaults, doc)
954
+ }
955
+ event.synthetic = true;
956
+ return event;
957
+ } else {
958
+ var event;
959
+ try {
960
+ event = h.createEventObject(type, defaults, element)
961
+ }
962
+ catch (e) {}
963
+
964
+ return event;
965
+ }
966
+
967
+ }
968
+ },
969
+ click: {
970
+ setup: function( type, options, element ) {
971
+ var nodeName = element.nodeName.toLowerCase(),
972
+ type;
973
+
974
+ //we need to manually 'check' in browser that can't check
975
+ //so checked has the right value
976
+ if (!Syn.support.clickChecks && !Syn.support.changeChecks && nodeName === "input" ) {
977
+ type = element.type.toLowerCase(); //pretty sure lowercase isn't needed
978
+ if ( type === 'checkbox' ) {
979
+ element.checked = !element.checked;
980
+ }
981
+ if ( type === "radio" ) {
982
+ //do the checks manually
983
+ if (!element.checked ) { //do nothing, no change
984
+ try {
985
+ Syn.data(element, "radioChanged", true);
986
+ } catch (e) {}
987
+ element.checked = true;
988
+ }
989
+ }
990
+ }
991
+
992
+ if ( nodeName == "a" && element.href && !/^\s*javascript:/.test(element.href) ) {
993
+
994
+ //save href
995
+ Syn.data(element, "href", element.href)
996
+
997
+ //remove b/c safari/opera will open a new tab instead of changing the page
998
+ // this has been removed because newer versions don't have this problem
999
+ //element.setAttribute('href', 'javascript://')
1000
+ //however this breaks scripts using the href
1001
+ //we need to listen to this and prevent the default behavior
1002
+ //and run the default behavior ourselves. Boo!
1003
+ }
1004
+ //if select or option, save old value and mark to change
1005
+ if (/option/i.test(element.nodeName) ) {
1006
+ var child = element.parentNode.firstChild,
1007
+ i = -1;
1008
+ while ( child ) {
1009
+ if ( child.nodeType == 1 ) {
1010
+ i++;
1011
+ if ( child == element ) break;
1012
+ }
1013
+ child = child.nextSibling;
1014
+ }
1015
+ if ( i !== element.parentNode.selectedIndex ) {
1016
+ //shouldn't this wait on triggering
1017
+ //change?
1018
+ element.parentNode.selectedIndex = i;
1019
+ Syn.data(element, "createChange", true)
1020
+ }
1021
+ }
1022
+
1023
+ }
1024
+ },
1025
+ mousedown: {
1026
+ setup: function( type, options, element ) {
1027
+ var nn = element.nodeName.toLowerCase();
1028
+ //we have to auto prevent default to prevent freezing error in safari
1029
+ if ( Syn.browser.safari && (nn == "select" || nn == "option") ) {
1030
+ options._autoPrevent = true;
1031
+ }
1032
+ }
1033
+ }
1034
+ });
1035
+ //do support code
1036
+ (function() {
1037
+ if (!document.body ) {
1038
+ setTimeout(arguments.callee, 1)
1039
+ return;
1040
+ }
1041
+ var oldSynth = window.__synthTest;
1042
+ window.__synthTest = function() {
1043
+ Syn.support.linkHrefJS = true;
1044
+ }
1045
+ var div = document.createElement("div"),
1046
+ checkbox, submit, form, input, select;
1047
+
1048
+ div.innerHTML = "<form id='outer'>" + "<input name='checkbox' type='checkbox'/>" + "<input name='radio' type='radio' />" + "<input type='submit' name='submitter'/>" + "<input type='input' name='inputter'/>" + "<input name='one'>" + "<input name='two'/>" + "<a href='javascript:__synthTest()' id='synlink'></a>" + "<select><option></option></select>" + "</form>";
1049
+ document.documentElement.appendChild(div);
1050
+ form = div.firstChild
1051
+ checkbox = form.childNodes[0];
1052
+ submit = form.childNodes[2];
1053
+ select = form.getElementsByTagName('select')[0]
1054
+
1055
+ checkbox.checked = false;
1056
+ checkbox.onchange = function() {
1057
+ Syn.support.clickChanges = true;
1058
+ }
1059
+
1060
+ Syn.trigger("click", {}, checkbox)
1061
+ Syn.support.clickChecks = checkbox.checked;
1062
+
1063
+ checkbox.checked = false;
1064
+
1065
+ Syn.trigger("change", {}, checkbox);
1066
+
1067
+ Syn.support.changeChecks = checkbox.checked;
1068
+
1069
+ form.onsubmit = function( ev ) {
1070
+ if ( ev.preventDefault ) ev.preventDefault();
1071
+ Syn.support.clickSubmits = true;
1072
+ return false;
1073
+ }
1074
+ Syn.trigger("click", {}, submit)
1075
+
1076
+
1077
+
1078
+ form.childNodes[1].onchange = function() {
1079
+ Syn.support.radioClickChanges = true;
1080
+ }
1081
+ Syn.trigger("click", {}, form.childNodes[1])
1082
+
1083
+
1084
+ Syn.bind(div, 'click', function() {
1085
+ Syn.support.optionClickBubbles = true;
1086
+ Syn.unbind(div, 'click', arguments.callee)
1087
+ })
1088
+ Syn.trigger("click", {}, select.firstChild)
1089
+
1090
+
1091
+ Syn.support.changeBubbles = Syn.eventSupported('change');
1092
+
1093
+ //test if mousedown followed by mouseup causes click (opera), make sure there are no clicks after this
1094
+ var clicksCount = 0
1095
+ div.onclick = function() {
1096
+ Syn.support.mouseDownUpClicks = true;
1097
+ //we should use this to check for opera potentially, but would
1098
+ //be difficult to remove element correctly
1099
+ //Syn.support.mouseDownUpRepeatClicks = (2 == (++clicksCount))
1100
+ }
1101
+ Syn.trigger("mousedown", {}, div)
1102
+ Syn.trigger("mouseup", {}, div)
1103
+
1104
+ //setTimeout(function(){
1105
+ // Syn.trigger("mousedown",{},div)
1106
+ // Syn.trigger("mouseup",{},div)
1107
+ //},1)
1108
+
1109
+ document.documentElement.removeChild(div);
1110
+
1111
+ //check stuff
1112
+ window.__synthTest = oldSynth;
1113
+ Syn.support.ready++;
1114
+ })();
1115
+ })(true);
1116
+ (function(){
1117
+ Syn.key.browsers = {
1118
+ webkit : {
1119
+ 'prevent':
1120
+ {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
1121
+ 'character':
1122
+ {"keydown":[0,"key"],"keypress":["char","char"],"keyup":[0,"key"]},
1123
+ 'specialChars':
1124
+ {"keydown":[0,"char"],"keyup":[0,"char"]},
1125
+ 'navigation':
1126
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1127
+ 'special':
1128
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1129
+ 'tab':
1130
+ {"keydown":[0,"char"],"keyup":[0,"char"]},
1131
+ 'pause-break':
1132
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1133
+ 'caps':
1134
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1135
+ 'escape':
1136
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1137
+ 'num-lock':
1138
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1139
+ 'scroll-lock':
1140
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1141
+ 'print':
1142
+ {"keyup":[0,"key"]},
1143
+ 'function':
1144
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1145
+ '\r':
1146
+ {"keydown":[0,"key"],"keypress":["char","key"],"keyup":[0,"key"]}
1147
+ },
1148
+ gecko : {
1149
+ 'prevent':
1150
+ {"keyup":[],"keydown":["char"],"keypress":["char"]},
1151
+ 'character':
1152
+ {"keydown":[0,"key"],"keypress":["char",0],"keyup":[0,"key"]},
1153
+ 'specialChars':
1154
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1155
+ 'navigation':
1156
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1157
+ 'special':
1158
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1159
+ '\t':
1160
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1161
+ 'pause-break':
1162
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1163
+ 'caps':
1164
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1165
+ 'escape':
1166
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1167
+ 'num-lock':
1168
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1169
+ 'scroll-lock':
1170
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1171
+ 'print':
1172
+ {"keyup":[0,"key"]},
1173
+ 'function':
1174
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
1175
+ '\r':
1176
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]}
1177
+ },
1178
+ msie : {
1179
+ 'prevent':{"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
1180
+ 'character':{"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
1181
+ 'specialChars':{"keydown":[null,"char"],"keyup":[null,"char"]},
1182
+ 'navigation':{"keydown":[null,"key"],"keyup":[null,"key"]},
1183
+ 'special':{"keydown":[null,"key"],"keyup":[null,"key"]},
1184
+ 'tab':{"keydown":[null,"char"],"keyup":[null,"char"]},
1185
+ 'pause-break':{"keydown":[null,"key"],"keyup":[null,"key"]},
1186
+ 'caps':{"keydown":[null,"key"],"keyup":[null,"key"]},
1187
+ 'escape':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1188
+ 'num-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
1189
+ 'scroll-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
1190
+ 'print':{"keyup":[null,"key"]},
1191
+ 'function':{"keydown":[null,"key"],"keyup":[null,"key"]},
1192
+ '\r':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
1193
+ },
1194
+ opera : {
1195
+ 'prevent':
1196
+ {"keyup":[],"keydown":[],"keypress":["char"]},
1197
+ 'character':
1198
+ {"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
1199
+ 'specialChars':
1200
+ {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
1201
+ 'navigation':
1202
+ {"keydown":[null,"key"],"keypress":[null,"key"]},
1203
+ 'special':
1204
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1205
+ 'tab':
1206
+ {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
1207
+ 'pause-break':
1208
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1209
+ 'caps':
1210
+ {"keydown":[null,"key"],"keyup":[null,"key"]},
1211
+ 'escape':
1212
+ {"keydown":[null,"key"],"keypress":[null,"key"]},
1213
+ 'num-lock':
1214
+ {"keyup":[null,"key"],"keydown":[null,"key"],"keypress":[null,"key"]},
1215
+ 'scroll-lock':
1216
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1217
+ 'print':
1218
+ {},
1219
+ 'function':
1220
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1221
+ '\r':
1222
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
1223
+ }
1224
+ };
1225
+
1226
+ Syn.mouse.browsers = {
1227
+ webkit : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
1228
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1229
+ opera: {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3}},
1230
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1231
+ msie: { "right":{"mousedown":{"button":2},"mouseup":{"button":2},"contextmenu":{"button":0}},
1232
+ "left":{"mousedown":{"button":1},"mouseup":{"button":1},"click":{"button":0}}},
1233
+ chrome : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
1234
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1235
+ gecko: {"left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}},
1236
+ "right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}}}
1237
+ }
1238
+
1239
+ //set browser
1240
+ Syn.key.browser =
1241
+ (function(){
1242
+ if(Syn.key.browsers[window.navigator.userAgent]){
1243
+ return Syn.key.browsers[window.navigator.userAgent];
1244
+ }
1245
+ for(var browser in Syn.browser){
1246
+ if(Syn.browser[browser] && Syn.key.browsers[browser]){
1247
+ return Syn.key.browsers[browser]
1248
+ }
1249
+ }
1250
+ return Syn.key.browsers.gecko;
1251
+ })();
1252
+
1253
+ Syn.mouse.browser =
1254
+ (function(){
1255
+ if(Syn.mouse.browsers[window.navigator.userAgent]){
1256
+ return Syn.mouse.browsers[window.navigator.userAgent];
1257
+ }
1258
+ for(var browser in Syn.browser){
1259
+ if(Syn.browser[browser] && Syn.mouse.browsers[browser]){
1260
+ return Syn.mouse.browsers[browser]
1261
+ }
1262
+ }
1263
+ return Syn.mouse.browsers.gecko;
1264
+ })();
1265
+ })(true);
1266
+ (function(){
1267
+ var h = Syn.helpers,
1268
+ S = Syn,
1269
+
1270
+ // gets the selection of an input or textarea
1271
+ getSelection = function( el ) {
1272
+ // use selectionStart if we can
1273
+ if ( el.selectionStart !== undefined ) {
1274
+ // this is for opera, so we don't have to focus to type how we think we would
1275
+ if ( document.activeElement && document.activeElement != el && el.selectionStart == el.selectionEnd && el.selectionStart == 0 ) {
1276
+ return {
1277
+ start: el.value.length,
1278
+ end: el.value.length
1279
+ };
1280
+ }
1281
+ return {
1282
+ start: el.selectionStart,
1283
+ end: el.selectionEnd
1284
+ }
1285
+ } else {
1286
+ //check if we aren't focused
1287
+ try {
1288
+ //try 2 different methods that work differently (IE breaks depending on type)
1289
+ if ( el.nodeName.toLowerCase() == 'input' ) {
1290
+ var real = h.getWindow(el).document.selection.createRange(),
1291
+ r = el.createTextRange();
1292
+ r.setEndPoint("EndToStart", real);
1293
+
1294
+ var start = r.text.length
1295
+ return {
1296
+ start: start,
1297
+ end: start + real.text.length
1298
+ }
1299
+ }
1300
+ else {
1301
+ var real = h.getWindow(el).document.selection.createRange(),
1302
+ r = real.duplicate(),
1303
+ r2 = real.duplicate(),
1304
+ r3 = real.duplicate();
1305
+ r2.collapse();
1306
+ r3.collapse(false);
1307
+ r2.moveStart('character', -1)
1308
+ r3.moveStart('character', -1)
1309
+ //select all of our element
1310
+ r.moveToElementText(el)
1311
+ //now move our endpoint to the end of our real range
1312
+ r.setEndPoint('EndToEnd', real);
1313
+ var start = r.text.length - real.text.length,
1314
+ end = r.text.length;
1315
+ if ( start != 0 && r2.text == "" ) {
1316
+ start += 2;
1317
+ }
1318
+ if ( end != 0 && r3.text == "" ) {
1319
+ end += 2;
1320
+ }
1321
+ //if we aren't at the start, but previous is empty, we are at start of newline
1322
+ return {
1323
+ start: start,
1324
+ end: end
1325
+ }
1326
+ }
1327
+ } catch (e) {
1328
+ return {
1329
+ start: el.value.length,
1330
+ end: el.value.length
1331
+ };
1332
+ }
1333
+ }
1334
+ },
1335
+ // gets all focusable elements
1336
+ getFocusable = function( el ) {
1337
+ var document = h.getWindow(el).document,
1338
+ res = [];
1339
+
1340
+ var els = document.getElementsByTagName('*'),
1341
+ len = els.length;
1342
+
1343
+ for ( var i = 0; i < len; i++ ) {
1344
+ Syn.isFocusable(els[i]) && els[i] != document.documentElement && res.push(els[i])
1345
+ }
1346
+ return res;
1347
+
1348
+
1349
+ };
1350
+
1351
+ /**
1352
+ * @add Syn static
1353
+ */
1354
+ h.extend(Syn, {
1355
+ /**
1356
+ * @attribute
1357
+ * A list of the keys and their keycodes codes you can type.
1358
+ * You can add type keys with
1359
+ * @codestart
1360
+ * Syn('key','delete','title');
1361
+ *
1362
+ * //or
1363
+ *
1364
+ * Syn('type','One Two Three[left][left][delete]','title')
1365
+ * @codeend
1366
+ *
1367
+ * The following are a list of keys you can type:
1368
+ * @codestart text
1369
+ * \b - backspace
1370
+ * \t - tab
1371
+ * \r - enter
1372
+ * ' ' - space
1373
+ * a-Z 0-9 - normal characters
1374
+ * /!@#$*,.? - All other typeable characters
1375
+ * page-up - scrolls up
1376
+ * page-down - scrolls down
1377
+ * end - scrolls to bottom
1378
+ * home - scrolls to top
1379
+ * insert - changes how keys are entered
1380
+ * delete - deletes the next character
1381
+ * left - moves cursor left
1382
+ * right - moves cursor right
1383
+ * up - moves the cursor up
1384
+ * down - moves the cursor down
1385
+ * f1-12 - function buttons
1386
+ * shift, ctrl, alt - special keys
1387
+ * pause-break - the pause button
1388
+ * scroll-lock - locks scrolling
1389
+ * caps - makes caps
1390
+ * escape - escape button
1391
+ * num-lock - allows numbers on keypad
1392
+ * print - screen capture
1393
+ * @codeend
1394
+ */
1395
+ keycodes: {
1396
+ //backspace
1397
+ '\b': '8',
1398
+
1399
+ //tab
1400
+ '\t': '9',
1401
+
1402
+ //enter
1403
+ '\r': '13',
1404
+
1405
+ //special
1406
+ 'shift': '16',
1407
+ 'ctrl': '17',
1408
+ 'alt': '18',
1409
+
1410
+ //weird
1411
+ 'pause-break': '19',
1412
+ 'caps': '20',
1413
+ 'escape': '27',
1414
+ 'num-lock': '144',
1415
+ 'scroll-lock': '145',
1416
+ 'print': '44',
1417
+
1418
+ //navigation
1419
+ 'page-up': '33',
1420
+ 'page-down': '34',
1421
+ 'end': '35',
1422
+ 'home': '36',
1423
+ 'left': '37',
1424
+ 'up': '38',
1425
+ 'right': '39',
1426
+ 'down': '40',
1427
+ 'insert': '45',
1428
+ 'delete': '46',
1429
+
1430
+ //normal characters
1431
+ ' ': '32',
1432
+ '0': '48',
1433
+ '1': '49',
1434
+ '2': '50',
1435
+ '3': '51',
1436
+ '4': '52',
1437
+ '5': '53',
1438
+ '6': '54',
1439
+ '7': '55',
1440
+ '8': '56',
1441
+ '9': '57',
1442
+ 'a': '65',
1443
+ 'b': '66',
1444
+ 'c': '67',
1445
+ 'd': '68',
1446
+ 'e': '69',
1447
+ 'f': '70',
1448
+ 'g': '71',
1449
+ 'h': '72',
1450
+ 'i': '73',
1451
+ 'j': '74',
1452
+ 'k': '75',
1453
+ 'l': '76',
1454
+ 'm': '77',
1455
+ 'n': '78',
1456
+ 'o': '79',
1457
+ 'p': '80',
1458
+ 'q': '81',
1459
+ 'r': '82',
1460
+ 's': '83',
1461
+ 't': '84',
1462
+ 'u': '85',
1463
+ 'v': '86',
1464
+ 'w': '87',
1465
+ 'x': '88',
1466
+ 'y': '89',
1467
+ 'z': '90',
1468
+ //normal-characters, numpad
1469
+ 'num0': '96',
1470
+ 'num1': '97',
1471
+ 'num2': '98',
1472
+ 'num3': '99',
1473
+ 'num4': '100',
1474
+ 'num5': '101',
1475
+ 'num6': '102',
1476
+ 'num7': '103',
1477
+ 'num8': '104',
1478
+ 'num9': '105',
1479
+ '*': '106',
1480
+ '+': '107',
1481
+ '-': '109',
1482
+ '.': '110',
1483
+ //normal-characters, others
1484
+ '/': '111',
1485
+ ';': '186',
1486
+ '=': '187',
1487
+ ',': '188',
1488
+ '-': '189',
1489
+ '.': '190',
1490
+ '/': '191',
1491
+ '`': '192',
1492
+ '[': '219',
1493
+ '\\': '220',
1494
+ ']': '221',
1495
+ "'": '222',
1496
+
1497
+ //ignore these, you shouldn't use them
1498
+ 'left window key': '91',
1499
+ 'right window key': '92',
1500
+ 'select key': '93',
1501
+
1502
+
1503
+ 'f1': '112',
1504
+ 'f2': '113',
1505
+ 'f3': '114',
1506
+ 'f4': '115',
1507
+ 'f5': '116',
1508
+ 'f6': '117',
1509
+ 'f7': '118',
1510
+ 'f8': '119',
1511
+ 'f9': '120',
1512
+ 'f10': '121',
1513
+ 'f11': '122',
1514
+ 'f12': '123'
1515
+ },
1516
+
1517
+ // what we can type in
1518
+ typeable: /input|textarea/i,
1519
+
1520
+ // selects text on an element
1521
+ selectText: function( el, start, end ) {
1522
+ if ( el.setSelectionRange ) {
1523
+ if (!end ) {
1524
+ el.focus();
1525
+ el.setSelectionRange(start, start);
1526
+ } else {
1527
+ el.selectionStart = start;
1528
+ el.selectionEnd = end;
1529
+ }
1530
+ } else if ( el.createTextRange ) {
1531
+ //el.focus();
1532
+ var r = el.createTextRange();
1533
+ r.moveStart('character', start);
1534
+ end = end || start;
1535
+ r.moveEnd('character', end - el.value.length);
1536
+
1537
+ r.select();
1538
+ }
1539
+ },
1540
+ getText: function( el ) {
1541
+ //first check if the el has anything selected ..
1542
+ if ( Syn.typeable.test(el.nodeName) ) {
1543
+ var sel = getSelection(el);
1544
+ return el.value.substring(sel.start, sel.end)
1545
+ }
1546
+ //otherwise get from page
1547
+ var win = Syn.helpers.getWindow(el);
1548
+ if ( win.getSelection ) {
1549
+ return win.getSelection().toString();
1550
+ }
1551
+ else if ( win.document.getSelection ) {
1552
+ return win.document.getSelection().toString()
1553
+ }
1554
+ else {
1555
+ return win.document.selection.createRange().text;
1556
+ }
1557
+ },
1558
+ getSelection: getSelection
1559
+ });
1560
+
1561
+ h.extend(Syn.key, {
1562
+ // retrieves a description of what events for this character should look like
1563
+ data: function( key ) {
1564
+ //check if it is described directly
1565
+ if ( S.key.browser[key] ) {
1566
+ return S.key.browser[key];
1567
+ }
1568
+ for ( var kind in S.key.kinds ) {
1569
+ if ( h.inArray(key, S.key.kinds[kind]) > -1 ) {
1570
+ return S.key.browser[kind]
1571
+ }
1572
+ }
1573
+ return S.key.browser.character
1574
+ },
1575
+
1576
+ //returns the special key if special
1577
+ isSpecial: function( keyCode ) {
1578
+ var specials = S.key.kinds.special;
1579
+ for ( var i = 0; i < specials.length; i++ ) {
1580
+ if ( Syn.keycodes[specials[i]] == keyCode ) {
1581
+ return specials[i];
1582
+ }
1583
+ }
1584
+ },
1585
+ /**
1586
+ * @hide
1587
+ * gets the options for a key and event type ...
1588
+ * @param {Object} key
1589
+ * @param {Object} event
1590
+ */
1591
+ options: function( key, event ) {
1592
+ var keyData = Syn.key.data(key);
1593
+
1594
+ if (!keyData[event] ) {
1595
+ //we shouldn't be creating this event
1596
+ return null;
1597
+ }
1598
+
1599
+ var charCode = keyData[event][0],
1600
+ keyCode = keyData[event][1],
1601
+ result = {};
1602
+
1603
+ if ( keyCode == 'key' ) {
1604
+ result.keyCode = Syn.keycodes[key]
1605
+ } else if ( keyCode == 'char' ) {
1606
+ result.keyCode = key.charCodeAt(0)
1607
+ } else {
1608
+ result.keyCode = keyCode;
1609
+ }
1610
+
1611
+ if ( charCode == 'char' ) {
1612
+ result.charCode = key.charCodeAt(0)
1613
+ } else if ( charCode !== null ) {
1614
+ result.charCode = charCode;
1615
+ }
1616
+
1617
+
1618
+ return result
1619
+ },
1620
+ //types of event keys
1621
+ kinds: {
1622
+ special: ["shift", 'ctrl', 'alt', 'caps'],
1623
+ specialChars: ["\b"],
1624
+ navigation: ["page-up", 'page-down', 'end', 'home', 'left', 'up', 'right', 'down', 'insert', 'delete'],
1625
+ 'function': ['f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12']
1626
+ },
1627
+ //returns the default function
1628
+ getDefault: function( key ) {
1629
+ //check if it is described directly
1630
+ if ( Syn.key.defaults[key] ) {
1631
+ return Syn.key.defaults[key];
1632
+ }
1633
+ for ( var kind in Syn.key.kinds ) {
1634
+ if ( h.inArray(key, Syn.key.kinds[kind]) > -1 && Syn.key.defaults[kind] ) {
1635
+ return Syn.key.defaults[kind];
1636
+ }
1637
+ }
1638
+ return Syn.key.defaults.character
1639
+ },
1640
+ // default behavior when typing
1641
+ defaults: {
1642
+ 'character': function( options, scope, key, force, sel ) {
1643
+ if (/num\d+/.test(key) ) {
1644
+ key = key.match(/\d+/)[0]
1645
+ }
1646
+
1647
+ if ( force || (!S.support.keyCharacters && Syn.typeable.test(this.nodeName)) ) {
1648
+ var current = this.value,
1649
+ before = current.substr(0, sel.start),
1650
+ after = current.substr(sel.end),
1651
+ character = key;
1652
+
1653
+ this.value = before + character + after;
1654
+ //handle IE inserting \r\n
1655
+ var charLength = character == "\n" && S.support.textareaCarriage ? 2 : character.length;
1656
+ Syn.selectText(this, before.length + charLength)
1657
+ }
1658
+ },
1659
+ 'c': function( options, scope, key, force, sel ) {
1660
+ if ( Syn.key.ctrlKey ) {
1661
+ Syn.key.clipboard = Syn.getText(this)
1662
+ } else {
1663
+ Syn.key.defaults.character.apply(this, arguments);
1664
+ }
1665
+ },
1666
+ 'v': function( options, scope, key, force, sel ) {
1667
+ if ( Syn.key.ctrlKey ) {
1668
+ Syn.key.defaults.character.call(this, options, scope, Syn.key.clipboard, true, sel);
1669
+ } else {
1670
+ Syn.key.defaults.character.apply(this, arguments);
1671
+ }
1672
+ },
1673
+ 'a': function( options, scope, key, force, sel ) {
1674
+ if ( Syn.key.ctrlKey ) {
1675
+ Syn.selectText(this, 0, this.value.length)
1676
+ } else {
1677
+ Syn.key.defaults.character.apply(this, arguments);
1678
+ }
1679
+ },
1680
+ 'home': function() {
1681
+ Syn.onParents(this, function( el ) {
1682
+ if ( el.scrollHeight != el.clientHeight ) {
1683
+ el.scrollTop = 0;
1684
+ return false;
1685
+ }
1686
+ })
1687
+ },
1688
+ 'end': function() {
1689
+ Syn.onParents(this, function( el ) {
1690
+ if ( el.scrollHeight != el.clientHeight ) {
1691
+ el.scrollTop = el.scrollHeight;
1692
+ return false;
1693
+ }
1694
+ })
1695
+ },
1696
+ 'page-down': function() {
1697
+ //find the first parent we can scroll
1698
+ Syn.onParents(this, function( el ) {
1699
+ if ( el.scrollHeight != el.clientHeight ) {
1700
+ var ch = el.clientHeight
1701
+ el.scrollTop += ch;
1702
+ return false;
1703
+ }
1704
+ })
1705
+ },
1706
+ 'page-up': function() {
1707
+ Syn.onParents(this, function( el ) {
1708
+ if ( el.scrollHeight != el.clientHeight ) {
1709
+ var ch = el.clientHeight
1710
+ el.scrollTop -= ch;
1711
+ return false;
1712
+ }
1713
+ })
1714
+ },
1715
+ '\b': function( options, scope, key, force, sel ) {
1716
+ //this assumes we are deleting from the end
1717
+ if (!S.support.backspaceWorks && Syn.typeable.test(this.nodeName) ) {
1718
+ var current = this.value,
1719
+ before = current.substr(0, sel.start),
1720
+ after = current.substr(sel.end);
1721
+
1722
+ if ( sel.start == sel.end && sel.start > 0 ) {
1723
+ //remove a character
1724
+ this.value = before.substring(0, before.length - 1) + after
1725
+ Syn.selectText(this, sel.start - 1)
1726
+ } else {
1727
+ this.value = before + after;
1728
+ Syn.selectText(this, sel.start)
1729
+ }
1730
+
1731
+ //set back the selection
1732
+ }
1733
+ },
1734
+ 'delete': function( options, scope, key, force, sel ) {
1735
+ if (!S.support.backspaceWorks && Syn.typeable.test(this.nodeName) ) {
1736
+ var current = this.value,
1737
+ before = current.substr(0, sel.start),
1738
+ after = current.substr(sel.end);
1739
+ if ( sel.start == sel.end && sel.start <= this.value.length - 1 ) {
1740
+ this.value = before + after.substring(1)
1741
+ } else {
1742
+ this.value = before + after;
1743
+
1744
+ }
1745
+ Syn.selectText(this, sel.start)
1746
+ }
1747
+ },
1748
+ '\r': function( options, scope, key, force, sel ) {
1749
+
1750
+ var nodeName = this.nodeName.toLowerCase()
1751
+ // submit a form
1752
+ if (!S.support.keypressSubmits && nodeName == 'input' ) {
1753
+ var form = Syn.closest(this, "form");
1754
+ if ( form ) {
1755
+ Syn.trigger("submit", {}, form);
1756
+ }
1757
+
1758
+ }
1759
+ //newline in textarea
1760
+ if (!S.support.keyCharacters && nodeName == 'textarea' ) {
1761
+ Syn.key.defaults.character.call(this, options, scope, "\n", undefined, sel)
1762
+ }
1763
+ // 'click' hyperlinks
1764
+ if (!S.support.keypressOnAnchorClicks && nodeName == 'a' ) {
1765
+ Syn.trigger("click", {}, this);
1766
+ }
1767
+ },
1768
+ //
1769
+ // Gets all focusable elements. If the element (this)
1770
+ // doesn't have a tabindex, finds the next element after.
1771
+ // If the element (this) has a tabindex finds the element
1772
+ // with the next higher tabindex OR the element with the same
1773
+ // tabindex after it in the document.
1774
+ // @return the next element
1775
+ //
1776
+ '\t': function( options, scope ) {
1777
+ // focusable elements
1778
+ var focusEls = getFocusable(this),
1779
+ // the current element's tabindex
1780
+ tabIndex = Syn.tabIndex(this),
1781
+ // will be set to our guess for the next element
1782
+ current = null,
1783
+ // the next index we care about
1784
+ currentIndex = 1000000000,
1785
+ // set to true once we found 'this' element
1786
+ found = false,
1787
+ i = 0,
1788
+ el,
1789
+ //the tabindex of the tabable element we are looking at
1790
+ elIndex, firstNotIndexed, prev;
1791
+ orders = [];
1792
+ for (; i < focusEls.length; i++ ) {
1793
+ orders.push([focusEls[i], i]);
1794
+ }
1795
+ var sort = function( order1, order2 ) {
1796
+ var el1 = order1[0],
1797
+ el2 = order2[0],
1798
+ tab1 = Syn.tabIndex(el1) || 0,
1799
+ tab2 = Syn.tabIndex(el2) || 0;
1800
+ if ( tab1 == tab2 ) {
1801
+ return order1[1] - order2[1]
1802
+ } else {
1803
+ if ( tab1 == 0 ) {
1804
+ return 1;
1805
+ } else if ( tab2 == 0 ) {
1806
+ return -1;
1807
+ } else {
1808
+ return tab1 - tab2;
1809
+ }
1810
+ }
1811
+ }
1812
+ orders.sort(sort);
1813
+ //now find current
1814
+ for ( i = 0; i < orders.length; i++ ) {
1815
+ el = orders[i][0];
1816
+ if ( this == el ) {
1817
+ if (!Syn.key.shiftKey ) {
1818
+ current = orders[i + 1][0];
1819
+ if (!current ) {
1820
+ current = orders[0][0]
1821
+ }
1822
+ } else {
1823
+ current = orders[i - 1][0];
1824
+ if (!current ) {
1825
+ current = orders[focusEls.length - 1][0]
1826
+ }
1827
+ }
1828
+
1829
+ }
1830
+ }
1831
+
1832
+ //restart if we didn't find anything
1833
+ if (!current ) {
1834
+ current = firstNotIndexed;
1835
+ }
1836
+ current && current.focus();
1837
+ return current;
1838
+ },
1839
+ 'left': function( options, scope, key, force, sel ) {
1840
+ if ( Syn.typeable.test(this.nodeName) ) {
1841
+ if ( Syn.key.shiftKey ) {
1842
+ Syn.selectText(this, sel.start == 0 ? 0 : sel.start - 1, sel.end)
1843
+ } else {
1844
+ Syn.selectText(this, sel.start == 0 ? 0 : sel.start - 1)
1845
+ }
1846
+ }
1847
+ },
1848
+ 'right': function( options, scope, key, force, sel ) {
1849
+ if ( Syn.typeable.test(this.nodeName) ) {
1850
+ if ( Syn.key.shiftKey ) {
1851
+ Syn.selectText(this, sel.start, sel.end + 1 > this.value.length ? this.value.length : sel.end + 1)
1852
+ } else {
1853
+ Syn.selectText(this, sel.end + 1 > this.value.length ? this.value.length : sel.end + 1)
1854
+ }
1855
+ }
1856
+ },
1857
+ 'up': function() {
1858
+ if (/select/i.test(this.nodeName) ) {
1859
+
1860
+ this.selectedIndex = this.selectedIndex ? this.selectedIndex - 1 : 0;
1861
+ //set this to change on blur?
1862
+ }
1863
+ },
1864
+ 'down': function() {
1865
+ if (/select/i.test(this.nodeName) ) {
1866
+ Syn.changeOnBlur(this, "selectedIndex", this.selectedIndex)
1867
+ this.selectedIndex = this.selectedIndex + 1;
1868
+ //set this to change on blur?
1869
+ }
1870
+ },
1871
+ 'shift': function() {
1872
+ return null;
1873
+ }
1874
+ }
1875
+ });
1876
+
1877
+
1878
+ h.extend(Syn.create, {
1879
+ keydown: {
1880
+ setup: function( type, options, element ) {
1881
+ if ( h.inArray(options, Syn.key.kinds.special) != -1 ) {
1882
+ Syn.key[options + "Key"] = element;
1883
+ }
1884
+ }
1885
+ },
1886
+ keypress: {
1887
+ setup: function( type, options, element ) {
1888
+ // if this browsers supports writing keys on events
1889
+ // but doesn't write them if the element isn't focused
1890
+ // focus on the element (ignored if already focused)
1891
+ if ( S.support.keyCharacters && !S.support.keysOnNotFocused ) {
1892
+ element.focus()
1893
+ }
1894
+ }
1895
+ },
1896
+ keyup: {
1897
+ setup: function( type, options, element ) {
1898
+ if ( h.inArray(options, Syn.key.kinds.special) != -1 ) {
1899
+ Syn.key[options + "Key"] = null;
1900
+ }
1901
+ }
1902
+ },
1903
+ key: {
1904
+ // return the options for a key event
1905
+ options: function( type, options, element ) {
1906
+ //check if options is character or has character
1907
+ options = typeof options != "object" ? {
1908
+ character: options
1909
+ } : options;
1910
+
1911
+ //don't change the orignial
1912
+ options = h.extend({}, options)
1913
+ if ( options.character ) {
1914
+ h.extend(options, S.key.options(options.character, type));
1915
+ delete options.character;
1916
+ }
1917
+
1918
+ options = h.extend({
1919
+ ctrlKey: !! Syn.key.ctrlKey,
1920
+ altKey: !! Syn.key.altKey,
1921
+ shiftKey: !! Syn.key.shiftKey,
1922
+ metaKey: !! Syn.key.metaKey
1923
+ }, options)
1924
+
1925
+ return options;
1926
+ },
1927
+ // creates a key event
1928
+ event: function( type, options, element ) { //Everyone Else
1929
+ var doc = h.getWindow(element).document || document;
1930
+ if ( doc.createEvent ) {
1931
+ var event;
1932
+
1933
+ try {
1934
+
1935
+ event = doc.createEvent("KeyEvents");
1936
+ event.initKeyEvent(type, true, true, window, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode, options.charCode);
1937
+ }
1938
+ catch (e) {
1939
+ event = h.createBasicStandardEvent(type, options, doc);
1940
+ }
1941
+ event.synthetic = true;
1942
+ return event;
1943
+ }
1944
+ else {
1945
+ var event;
1946
+ try {
1947
+ event = h.createEventObject.apply(this, arguments);
1948
+ h.extend(event, options)
1949
+ }
1950
+ catch (e) {}
1951
+
1952
+ return event;
1953
+ }
1954
+ }
1955
+ }
1956
+ });
1957
+
1958
+ var convert = {
1959
+ "enter": "\r",
1960
+ "backspace": "\b",
1961
+ "tab": "\t",
1962
+ "space": " "
1963
+ }
1964
+
1965
+ /**
1966
+ * @add Syn prototype
1967
+ */
1968
+ h.extend(Syn.init.prototype, {
1969
+ /**
1970
+ * @function key
1971
+ * Types a single key. The key should be
1972
+ * a string that matches a
1973
+ * [Syn.static.keycodes].
1974
+ *
1975
+ * The following sends a carridge return
1976
+ * to the 'name' element.
1977
+ * @codestart
1978
+ * Syn.key('\r','name')
1979
+ * @codeend
1980
+ * For each character, a keydown, keypress, and keyup is triggered if
1981
+ * appropriate.
1982
+ * @param {String} options
1983
+ * @param {HTMLElement} [element]
1984
+ * @param {Function} [callback]
1985
+ * @return {HTMLElement} the element currently focused.
1986
+ */
1987
+ _key: function( options, element, callback ) {
1988
+ //first check if it is a special up
1989
+ if (/-up$/.test(options) && h.inArray(options.replace("-up", ""), Syn.key.kinds.special) != -1 ) {
1990
+ Syn.trigger('keyup', options.replace("-up", ""), element)
1991
+ callback(true, element);
1992
+ return;
1993
+ }
1994
+
1995
+
1996
+ var caret = Syn.typeable.test(element.nodeName) && getSelection(element),
1997
+ key = convert[options] || options,
1998
+ // should we run default events
1999
+ runDefaults = Syn.trigger('keydown', key, element),
2000
+
2001
+ // a function that gets the default behavior for a key
2002
+ getDefault = Syn.key.getDefault,
2003
+
2004
+ // how this browser handles preventing default events
2005
+ prevent = Syn.key.browser.prevent,
2006
+
2007
+ // the result of the default event
2008
+ defaultResult,
2009
+
2010
+ // options for keypress
2011
+ keypressOptions = Syn.key.options(key, 'keypress')
2012
+
2013
+
2014
+ if ( runDefaults ) {
2015
+ //if the browser doesn't create keypresses for this key, run default
2016
+ if (!keypressOptions ) {
2017
+ defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret)
2018
+ } else {
2019
+ //do keypress
2020
+ runDefaults = Syn.trigger('keypress', keypressOptions, element)
2021
+ if ( runDefaults ) {
2022
+ defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret)
2023
+ }
2024
+ }
2025
+ } else {
2026
+ //canceled ... possibly don't run keypress
2027
+ if ( keypressOptions && h.inArray('keypress', prevent.keydown) == -1 ) {
2028
+ Syn.trigger('keypress', keypressOptions, element)
2029
+ }
2030
+ }
2031
+ if ( defaultResult && defaultResult.nodeName ) {
2032
+ element = defaultResult
2033
+ }
2034
+
2035
+ if ( defaultResult !== null ) {
2036
+ setTimeout(function() {
2037
+ Syn.trigger('keyup', Syn.key.options(key, 'keyup'), element)
2038
+ callback(runDefaults, element)
2039
+ }, 1)
2040
+ } else {
2041
+ callback(runDefaults, element)
2042
+ }
2043
+
2044
+
2045
+ //do mouseup
2046
+ return element;
2047
+ // is there a keypress? .. if not , run default
2048
+ // yes -> did we prevent it?, if not run ...
2049
+ },
2050
+ /**
2051
+ * @function type
2052
+ * Types sequence of [Syn.key key actions]. Each
2053
+ * character is typed, one at a type.
2054
+ * Multi-character keys like 'left' should be
2055
+ * enclosed in square brackents.
2056
+ *
2057
+ * The following types 'JavaScript MVC' then deletes the space.
2058
+ * @codestart
2059
+ * Syn.type('JavaScript MVC[left][left][left]\b','name')
2060
+ * @codeend
2061
+ *
2062
+ * Type is able to handle (and move with) tabs (\t).
2063
+ * The following simulates tabing and entering values in a form and
2064
+ * eventually submitting the form.
2065
+ * @codestart
2066
+ * Syn.type("Justin\tMeyer\t27\tjustinbmeyer@gmail.com\r")
2067
+ * @codeend
2068
+ * @param {String} options the text to type
2069
+ * @param {HTMLElement} [element] an element or an id of an element
2070
+ * @param {Function} [callback] a function to callback
2071
+ */
2072
+ _type: function( options, element, callback ) {
2073
+ //break it up into parts ...
2074
+ //go through each type and run
2075
+ var parts = options.match(/(\[[^\]]+\])|([^\[])/g),
2076
+ self = this,
2077
+ runNextPart = function( runDefaults, el ) {
2078
+ var part = parts.shift();
2079
+ if (!part ) {
2080
+ callback(runDefaults, el);
2081
+ return;
2082
+ }
2083
+ el = el || element;
2084
+ if ( part.length > 1 ) {
2085
+ part = part.substr(1, part.length - 2)
2086
+ }
2087
+ self._key(part, el, runNextPart)
2088
+ }
2089
+
2090
+ runNextPart();
2091
+
2092
+ }
2093
+ });
2094
+
2095
+
2096
+ //do support code
2097
+ (function() {
2098
+ if (!document.body ) {
2099
+ setTimeout(arguments.callee, 1)
2100
+ return;
2101
+ }
2102
+
2103
+ var div = document.createElement("div"),
2104
+ checkbox, submit, form, input, submitted = false,
2105
+ anchor, textarea, inputter;
2106
+
2107
+ div.innerHTML = "<form id='outer'>" +
2108
+ "<input name='checkbox' type='checkbox'/>" +
2109
+ "<input name='radio' type='radio' />" +
2110
+ "<input type='submit' name='submitter'/>" +
2111
+ "<input type='input' name='inputter'/>" +
2112
+ "<input name='one'>" +
2113
+ "<input name='two'/>" +
2114
+ "<a href='#abc'></a>" +
2115
+ "<textarea>1\n2</textarea>" +
2116
+ "</form>";
2117
+
2118
+ document.documentElement.appendChild(div);
2119
+ form = div.firstChild;
2120
+ checkbox = form.childNodes[0];
2121
+ submit = form.childNodes[2];
2122
+ anchor = form.getElementsByTagName("a")[0];
2123
+ textarea = form.getElementsByTagName("textarea")[0];
2124
+ inputter = form.childNodes[3];
2125
+
2126
+ form.onsubmit = function( ev ) {
2127
+ if ( ev.preventDefault ) ev.preventDefault();
2128
+ S.support.keypressSubmits = true;
2129
+ ev.returnValue = false;
2130
+ return false;
2131
+ };
2132
+ // Firefox 4 won't write key events if the element isn't focused
2133
+ inputter.focus();
2134
+ Syn.trigger("keypress", "\r", inputter);
2135
+
2136
+
2137
+ Syn.trigger("keypress", "a", inputter);
2138
+ S.support.keyCharacters = inputter.value == "a";
2139
+
2140
+
2141
+ inputter.value = "a";
2142
+ Syn.trigger("keypress", "\b", inputter);
2143
+ S.support.backspaceWorks = inputter.value == "";
2144
+
2145
+
2146
+
2147
+ inputter.onchange = function() {
2148
+ S.support.focusChanges = true;
2149
+ }
2150
+ inputter.focus();
2151
+ Syn.trigger("keypress", "a", inputter);
2152
+ form.childNodes[5].focus(); // this will throw a change event
2153
+ Syn.trigger("keypress", "b", inputter);
2154
+ S.support.keysOnNotFocused = inputter.value == "ab";
2155
+
2156
+ //test keypress \r on anchor submits
2157
+ S.bind(anchor, "click", function( ev ) {
2158
+ if ( ev.preventDefault ) ev.preventDefault();
2159
+ S.support.keypressOnAnchorClicks = true;
2160
+ ev.returnValue = false;
2161
+ return false;
2162
+ })
2163
+ Syn.trigger("keypress", "\r", anchor);
2164
+
2165
+ S.support.textareaCarriage = textarea.value.length == 4
2166
+ document.documentElement.removeChild(div);
2167
+
2168
+ S.support.ready++;
2169
+ })();
2170
+ })(true);
2171
+ (function() {
2172
+
2173
+ // check if elementFromPageExists
2174
+ (function() {
2175
+
2176
+ // document body has to exists for this test
2177
+ if (!document.body ) {
2178
+ setTimeout(arguments.callee, 1)
2179
+ return;
2180
+ }
2181
+ var div = document.createElement('div')
2182
+ document.body.appendChild(div);
2183
+ Syn.helpers.extend(div.style, {
2184
+ width: "100px",
2185
+ height: "10000px",
2186
+ backgroundColor: "blue",
2187
+ position: "absolute",
2188
+ top: "10px",
2189
+ left: "0px",
2190
+ zIndex: 19999
2191
+ });
2192
+ document.body.scrollTop = 11;
2193
+ if (!document.elementFromPoint ) {
2194
+ return;
2195
+ }
2196
+ var el = document.elementFromPoint(3, 1)
2197
+ if ( el == div ) {
2198
+ Syn.support.elementFromClient = true;
2199
+ }
2200
+ else {
2201
+ Syn.support.elementFromPage = true;
2202
+ }
2203
+ document.body.removeChild(div);
2204
+ document.body.scrollTop = 0;
2205
+ })();
2206
+
2207
+
2208
+ //gets an element from a point
2209
+ var elementFromPoint = function( point, element ) {
2210
+ var clientX = point.clientX,
2211
+ clientY = point.clientY,
2212
+ win = Syn.helpers.getWindow(element),
2213
+ el;
2214
+
2215
+
2216
+
2217
+ if ( Syn.support.elementFromPage ) {
2218
+ var off = Syn.helpers.scrollOffset(win);
2219
+ clientX = clientX + off.left; //convert to pageX
2220
+ clientY = clientY + off.top; //convert to pageY
2221
+ }
2222
+ el = win.document.elementFromPoint ? win.document.elementFromPoint(clientX, clientY) : element;
2223
+ if ( el === win.document.documentElement && (point.clientY < 0 || point.clientX < 0) ) {
2224
+ return element;
2225
+ } else {
2226
+ return el;
2227
+ }
2228
+ },
2229
+ //creates an event at a certain point
2230
+ createEventAtPoint = function( event, point, element ) {
2231
+ var el = elementFromPoint(point, element)
2232
+ Syn.trigger(event, point, el || element)
2233
+ return el;
2234
+ },
2235
+ // creates a mousemove event, but first triggering mouseout / mouseover if appropriate
2236
+ mouseMove = function( point, element, last ) {
2237
+ var el = elementFromPoint(point, element)
2238
+ if ( last != el && el && last ) {
2239
+ var options = Syn.helpers.extend({}, point);
2240
+ options.relatedTarget = el;
2241
+ Syn.trigger("mouseout", options, last);
2242
+ options.relatedTarget = last;
2243
+ Syn.trigger("mouseover", options, el);
2244
+ }
2245
+
2246
+ Syn.trigger("mousemove", point, el || element)
2247
+ return el;
2248
+ },
2249
+ // start and end are in clientX, clientY
2250
+ startMove = function( start, end, duration, element, callback ) {
2251
+ var startTime = new Date(),
2252
+ distX = end.clientX - start.clientX,
2253
+ distY = end.clientY - start.clientY,
2254
+ win = Syn.helpers.getWindow(element),
2255
+ current = elementFromPoint(start, element),
2256
+ cursor = win.document.createElement('div'),
2257
+ calls = 0;
2258
+ move = function() {
2259
+ //get what fraction we are at
2260
+ var now = new Date(),
2261
+ scrollOffset = Syn.helpers.scrollOffset(win),
2262
+ fraction = (calls == 0 ? 0 : now - startTime) / duration,
2263
+ options = {
2264
+ clientX: distX * fraction + start.clientX,
2265
+ clientY: distY * fraction + start.clientY
2266
+ };
2267
+ calls++;
2268
+ if ( fraction < 1 ) {
2269
+ Syn.helpers.extend(cursor.style, {
2270
+ left: (options.clientX + scrollOffset.left + 2) + "px",
2271
+ top: (options.clientY + scrollOffset.top + 2) + "px"
2272
+ })
2273
+ current = mouseMove(options, element, current)
2274
+ setTimeout(arguments.callee, 15)
2275
+ }
2276
+ else {
2277
+ current = mouseMove(end, element, current);
2278
+ win.document.body.removeChild(cursor)
2279
+ callback();
2280
+ }
2281
+ }
2282
+ Syn.helpers.extend(cursor.style, {
2283
+ height: "5px",
2284
+ width: "5px",
2285
+ backgroundColor: "red",
2286
+ position: "absolute",
2287
+ zIndex: 19999,
2288
+ fontSize: "1px"
2289
+ })
2290
+ win.document.body.appendChild(cursor)
2291
+ move();
2292
+ },
2293
+ startDrag = function( start, end, duration, element, callback ) {
2294
+ createEventAtPoint("mousedown", start, element);
2295
+ startMove(start, end, duration, element, function() {
2296
+ createEventAtPoint("mouseup", end, element);
2297
+ callback();
2298
+ })
2299
+ },
2300
+ center = function( el ) {
2301
+ var j = Syn.jquery()(el),
2302
+ o = j.offset();
2303
+ return {
2304
+ pageX: o.left + (j.width() / 2),
2305
+ pageY: o.top + (j.height() / 2)
2306
+ }
2307
+ },
2308
+ convertOption = function( option, win, from ) {
2309
+ var page = /(\d+)[x ](\d+)/,
2310
+ client = /(\d+)X(\d+)/,
2311
+ relative = /([+-]\d+)[xX ]([+-]\d+)/
2312
+ //check relative "+22x-44"
2313
+ if ( typeof option == 'string' && relative.test(option) && from ) {
2314
+ var cent = center(from),
2315
+ parts = option.match(relative);
2316
+ option = {
2317
+ pageX: cent.pageX + parseInt(parts[1]),
2318
+ pageY: cent.pageY + parseInt(parts[2])
2319
+ }
2320
+ }
2321
+ if ( typeof option == 'string' && page.test(option) ) {
2322
+ var parts = option.match(page)
2323
+ option = {
2324
+ pageX: parseInt(parts[1]),
2325
+ pageY: parseInt(parts[2])
2326
+ }
2327
+ }
2328
+ if ( typeof option == 'string' && client.test(option) ) {
2329
+ var parts = option.match(client)
2330
+ option = {
2331
+ clientX: parseInt(parts[1]),
2332
+ clientY: parseInt(parts[2])
2333
+ }
2334
+ }
2335
+ if ( typeof option == 'string' ) {
2336
+ option = Syn.jquery()(option, win.document)[0];
2337
+ }
2338
+ if ( option.nodeName ) {
2339
+ option = center(option)
2340
+ }
2341
+ if ( option.pageX ) {
2342
+ var off = Syn.helpers.scrollOffset(win);
2343
+ option = {
2344
+ clientX: option.pageX - off.left,
2345
+ clientY: option.pageY - off.top
2346
+ }
2347
+ }
2348
+ return option;
2349
+ },
2350
+ // if the client chords are not going to be visible ... scroll the page so they will be ...
2351
+ adjust = function(from, to, win){
2352
+ if(from.clientY < 0){
2353
+ var off = Syn.helpers.scrollOffset(win);
2354
+ var dimensions = Syn.helpers.scrollDimensions(win),
2355
+ top = off.top + (from.clientY) - 100,
2356
+ diff = top - off.top
2357
+
2358
+ // first, lets see if we can scroll 100 px
2359
+ if( top > 0){
2360
+
2361
+ } else {
2362
+ top =0;
2363
+ diff = -off.top;
2364
+ }
2365
+ from.clientY = from.clientY - diff;
2366
+ to.clientY = to.clientY - diff;
2367
+ Syn.helpers.scrollOffset(win,{top: top, left: off.left});
2368
+
2369
+ //throw "out of bounds!"
2370
+ }
2371
+ }
2372
+ /**
2373
+ * @add Syn prototype
2374
+ */
2375
+ Syn.helpers.extend(Syn.init.prototype, {
2376
+ /**
2377
+ * @function move
2378
+ * Moves the cursor from one point to another.
2379
+ *
2380
+ * ### Quick Example
2381
+ *
2382
+ * The following moves the cursor from (0,0) in
2383
+ * the window to (100,100) in 1 second.
2384
+ *
2385
+ * Syn.move(
2386
+ * {
2387
+ * from: {clientX: 0, clientY: 0},
2388
+ * to: {clientX: 100, clientY: 100},
2389
+ * duration: 1000
2390
+ * },
2391
+ * document.document)
2392
+ *
2393
+ * ## Options
2394
+ *
2395
+ * There are many ways to configure the endpoints of the move.
2396
+ *
2397
+ * ### PageX and PageY
2398
+ *
2399
+ * If you pass pageX or pageY, these will get converted
2400
+ * to client coordinates.
2401
+ *
2402
+ * Syn.move(
2403
+ * {
2404
+ * from: {pageX: 0, pageY: 0},
2405
+ * to: {pageX: 100, pageY: 100}
2406
+ * },
2407
+ * document.document)
2408
+ *
2409
+ * ### String Coordinates
2410
+ *
2411
+ * You can set the pageX and pageY as strings like:
2412
+ *
2413
+ * Syn.move(
2414
+ * {
2415
+ * from: "0x0",
2416
+ * to: "100x100"
2417
+ * },
2418
+ * document.document)
2419
+ *
2420
+ * ### Element Coordinates
2421
+ *
2422
+ * If jQuery is present, you can pass an element as the from or to option
2423
+ * and the coordinate will be set as the center of the element.
2424
+
2425
+ * Syn.move(
2426
+ * {
2427
+ * from: $(".recipe")[0],
2428
+ * to: $("#trash")[0]
2429
+ * },
2430
+ * document.document)
2431
+ *
2432
+ * ### Query Strings
2433
+ *
2434
+ * If jQuery is present, you can pass a query string as the from or to option.
2435
+ *
2436
+ * Syn.move(
2437
+ * {
2438
+ * from: ".recipe",
2439
+ * to: "#trash"
2440
+ * },
2441
+ * document.document)
2442
+ *
2443
+ * ### No From
2444
+ *
2445
+ * If you don't provide a from, the element argument passed to Syn is used.
2446
+ *
2447
+ * Syn.move(
2448
+ * { to: "#trash" },
2449
+ * 'myrecipe')
2450
+ *
2451
+ * ### Relative
2452
+ *
2453
+ * You can move the drag relative to the center of the from element.
2454
+ *
2455
+ * Syn.move("+20 +30", "myrecipe");
2456
+ *
2457
+ * @param {Object} options options to configure the drag
2458
+ * @param {HTMLElement} from the element to move
2459
+ * @param {Function} callback a callback that happens after the drag motion has completed
2460
+ */
2461
+ _move: function( options, from, callback ) {
2462
+ //need to convert if elements
2463
+ var win = Syn.helpers.getWindow(from),
2464
+ fro = convertOption(options.from || from, win, from),
2465
+ to = convertOption(options.to || options, win, from);
2466
+
2467
+ options.adjust !== false && adjust(fro, to, win);
2468
+ startMove(fro, to, options.duration || 500, from, callback);
2469
+
2470
+ },
2471
+ /**
2472
+ * @function drag
2473
+ * Creates a mousedown and drags from one point to another.
2474
+ * Check out [Syn.prototype.move move] for API details.
2475
+ *
2476
+ * @param {Object} options
2477
+ * @param {Object} from
2478
+ * @param {Object} callback
2479
+ */
2480
+ _drag: function( options, from, callback ) {
2481
+ //need to convert if elements
2482
+ var win = Syn.helpers.getWindow(from),
2483
+ fro = convertOption(options.from || from, win, from),
2484
+ to = convertOption(options.to || options, win, from);
2485
+
2486
+ options.adjust !== false && adjust(fro, to, win);
2487
+ startDrag(fro, to, options.duration || 500, from, callback);
2488
+
2489
+ }
2490
+ })
2491
+ }());