syn-rails 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }());