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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +7 -0
- data/Rakefile +1 -0
- data/lib/syn-rails.rb +11 -0
- data/lib/syn-rails/version.rb +5 -0
- data/syn-rails.gemspec +21 -0
- data/vendor/assets/javascripts/syn.js +2491 -0
- data/vendor/assets/javascripts/syn/browsers.js +321 -0
- data/vendor/assets/javascripts/syn/drag.js +321 -0
- data/vendor/assets/javascripts/syn/key.js +1055 -0
- data/vendor/assets/javascripts/syn/mouse.js +284 -0
- data/vendor/assets/javascripts/syn/synthetic.js +830 -0
- metadata +58 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
(function(){
|
2
|
+
//handles mouse events
|
3
|
+
|
4
|
+
var h = Syn.helpers,
|
5
|
+
getWin = h.getWindow;
|
6
|
+
|
7
|
+
Syn.mouse = {};
|
8
|
+
h.extend(Syn.defaults, {
|
9
|
+
mousedown: function( options ) {
|
10
|
+
Syn.trigger("focus", {}, this)
|
11
|
+
},
|
12
|
+
click: function() {
|
13
|
+
// prevents the access denied issue in IE if the click causes the element to be destroyed
|
14
|
+
var element = this;
|
15
|
+
try {
|
16
|
+
element.nodeType;
|
17
|
+
} catch (e) {
|
18
|
+
return;
|
19
|
+
}
|
20
|
+
//get old values
|
21
|
+
var href, radioChanged = Syn.data(element, "radioChanged"),
|
22
|
+
scope = getWin(element),
|
23
|
+
nodeName = element.nodeName.toLowerCase();
|
24
|
+
|
25
|
+
//this code was for restoring the href attribute to prevent popup opening
|
26
|
+
//if ((href = Syn.data(element, "href"))) {
|
27
|
+
// element.setAttribute('href', href)
|
28
|
+
//}
|
29
|
+
|
30
|
+
//run href javascript
|
31
|
+
if (!Syn.support.linkHrefJS && /^\s*javascript:/.test(element.href) ) {
|
32
|
+
//eval js
|
33
|
+
var code = element.href.replace(/^\s*javascript:/, "")
|
34
|
+
|
35
|
+
//try{
|
36
|
+
if ( code != "//" && code.indexOf("void(0)") == -1 ) {
|
37
|
+
if ( window.selenium ) {
|
38
|
+
eval("with(selenium.browserbot.getCurrentWindow()){" + code + "}")
|
39
|
+
} else {
|
40
|
+
eval("with(scope){" + code + "}")
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
//submit a form
|
46
|
+
if (!(Syn.support.clickSubmits) && (nodeName == "input" && element.type == "submit") || nodeName == 'button' ) {
|
47
|
+
|
48
|
+
var form = Syn.closest(element, "form");
|
49
|
+
if ( form ) {
|
50
|
+
Syn.trigger("submit", {}, form)
|
51
|
+
}
|
52
|
+
|
53
|
+
}
|
54
|
+
//follow a link, probably needs to check if in an a.
|
55
|
+
if ( nodeName == "a" && element.href && !/^\s*javascript:/.test(element.href) ) {
|
56
|
+
|
57
|
+
scope.location.href = element.href;
|
58
|
+
|
59
|
+
}
|
60
|
+
|
61
|
+
//change a checkbox
|
62
|
+
if ( nodeName == "input" && element.type == "checkbox" ) {
|
63
|
+
|
64
|
+
//if(!Syn.support.clickChecks && !Syn.support.changeChecks){
|
65
|
+
// element.checked = !element.checked;
|
66
|
+
//}
|
67
|
+
if (!Syn.support.clickChanges ) {
|
68
|
+
Syn.trigger("change", {}, element);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
//change a radio button
|
73
|
+
if ( nodeName == "input" && element.type == "radio" ) { // need to uncheck others if not checked
|
74
|
+
if ( radioChanged && !Syn.support.radioClickChanges ) {
|
75
|
+
Syn.trigger("change", {}, element);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
// change options
|
79
|
+
if ( nodeName == "option" && Syn.data(element, "createChange") ) {
|
80
|
+
Syn.trigger("change", {}, element.parentNode); //does not bubble
|
81
|
+
Syn.data(element, "createChange", false)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
})
|
85
|
+
|
86
|
+
//add create and setup behavior for mosue events
|
87
|
+
h.extend(Syn.create, {
|
88
|
+
mouse: {
|
89
|
+
options: function( type, options, element ) {
|
90
|
+
var doc = document.documentElement,
|
91
|
+
body = document.body,
|
92
|
+
center = [options.pageX || 0, options.pageY || 0],
|
93
|
+
//browser might not be loaded yet (doing support code)
|
94
|
+
left = Syn.mouse.browser && Syn.mouse.browser.left[type],
|
95
|
+
right = Syn.mouse.browser && Syn.mouse.browser.right[type];
|
96
|
+
return h.extend({
|
97
|
+
bubbles: true,
|
98
|
+
cancelable: true,
|
99
|
+
view: window,
|
100
|
+
detail: 1,
|
101
|
+
screenX: 1,
|
102
|
+
screenY: 1,
|
103
|
+
clientX: options.clientX || center[0] - (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0),
|
104
|
+
clientY: options.clientY || center[1] - (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0),
|
105
|
+
ctrlKey: !! Syn.key.ctrlKey,
|
106
|
+
altKey: !! Syn.key.altKey,
|
107
|
+
shiftKey: !! Syn.key.shiftKey,
|
108
|
+
metaKey: !! Syn.key.metaKey,
|
109
|
+
button: left && left.button != null ? left.button : right && right.button || (type == 'contextmenu' ? 2 : 0),
|
110
|
+
relatedTarget: document.documentElement
|
111
|
+
}, options);
|
112
|
+
},
|
113
|
+
event: function( type, defaults, element ) { //Everyone Else
|
114
|
+
var doc = getWin(element).document || document
|
115
|
+
if ( doc.createEvent ) {
|
116
|
+
var event;
|
117
|
+
|
118
|
+
try {
|
119
|
+
event = doc.createEvent('MouseEvents');
|
120
|
+
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);
|
121
|
+
} catch (e) {
|
122
|
+
event = h.createBasicStandardEvent(type, defaults, doc)
|
123
|
+
}
|
124
|
+
event.synthetic = true;
|
125
|
+
return event;
|
126
|
+
} else {
|
127
|
+
var event;
|
128
|
+
try {
|
129
|
+
event = h.createEventObject(type, defaults, element)
|
130
|
+
}
|
131
|
+
catch (e) {}
|
132
|
+
|
133
|
+
return event;
|
134
|
+
}
|
135
|
+
|
136
|
+
}
|
137
|
+
},
|
138
|
+
click: {
|
139
|
+
setup: function( type, options, element ) {
|
140
|
+
var nodeName = element.nodeName.toLowerCase(),
|
141
|
+
type;
|
142
|
+
|
143
|
+
//we need to manually 'check' in browser that can't check
|
144
|
+
//so checked has the right value
|
145
|
+
if (!Syn.support.clickChecks && !Syn.support.changeChecks && nodeName === "input" ) {
|
146
|
+
type = element.type.toLowerCase(); //pretty sure lowercase isn't needed
|
147
|
+
if ( type === 'checkbox' ) {
|
148
|
+
element.checked = !element.checked;
|
149
|
+
}
|
150
|
+
if ( type === "radio" ) {
|
151
|
+
//do the checks manually
|
152
|
+
if (!element.checked ) { //do nothing, no change
|
153
|
+
try {
|
154
|
+
Syn.data(element, "radioChanged", true);
|
155
|
+
} catch (e) {}
|
156
|
+
element.checked = true;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
if ( nodeName == "a" && element.href && !/^\s*javascript:/.test(element.href) ) {
|
162
|
+
|
163
|
+
//save href
|
164
|
+
Syn.data(element, "href", element.href)
|
165
|
+
|
166
|
+
//remove b/c safari/opera will open a new tab instead of changing the page
|
167
|
+
// this has been removed because newer versions don't have this problem
|
168
|
+
//element.setAttribute('href', 'javascript://')
|
169
|
+
//however this breaks scripts using the href
|
170
|
+
//we need to listen to this and prevent the default behavior
|
171
|
+
//and run the default behavior ourselves. Boo!
|
172
|
+
}
|
173
|
+
//if select or option, save old value and mark to change
|
174
|
+
if (/option/i.test(element.nodeName) ) {
|
175
|
+
var child = element.parentNode.firstChild,
|
176
|
+
i = -1;
|
177
|
+
while ( child ) {
|
178
|
+
if ( child.nodeType == 1 ) {
|
179
|
+
i++;
|
180
|
+
if ( child == element ) break;
|
181
|
+
}
|
182
|
+
child = child.nextSibling;
|
183
|
+
}
|
184
|
+
if ( i !== element.parentNode.selectedIndex ) {
|
185
|
+
//shouldn't this wait on triggering
|
186
|
+
//change?
|
187
|
+
element.parentNode.selectedIndex = i;
|
188
|
+
Syn.data(element, "createChange", true)
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
}
|
193
|
+
},
|
194
|
+
mousedown: {
|
195
|
+
setup: function( type, options, element ) {
|
196
|
+
var nn = element.nodeName.toLowerCase();
|
197
|
+
//we have to auto prevent default to prevent freezing error in safari
|
198
|
+
if ( Syn.browser.safari && (nn == "select" || nn == "option") ) {
|
199
|
+
options._autoPrevent = true;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
});
|
204
|
+
//do support code
|
205
|
+
(function() {
|
206
|
+
if (!document.body ) {
|
207
|
+
setTimeout(arguments.callee, 1)
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
var oldSynth = window.__synthTest;
|
211
|
+
window.__synthTest = function() {
|
212
|
+
Syn.support.linkHrefJS = true;
|
213
|
+
}
|
214
|
+
var div = document.createElement("div"),
|
215
|
+
checkbox, submit, form, input, select;
|
216
|
+
|
217
|
+
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>";
|
218
|
+
document.documentElement.appendChild(div);
|
219
|
+
form = div.firstChild
|
220
|
+
checkbox = form.childNodes[0];
|
221
|
+
submit = form.childNodes[2];
|
222
|
+
select = form.getElementsByTagName('select')[0]
|
223
|
+
|
224
|
+
checkbox.checked = false;
|
225
|
+
checkbox.onchange = function() {
|
226
|
+
Syn.support.clickChanges = true;
|
227
|
+
}
|
228
|
+
|
229
|
+
Syn.trigger("click", {}, checkbox)
|
230
|
+
Syn.support.clickChecks = checkbox.checked;
|
231
|
+
|
232
|
+
checkbox.checked = false;
|
233
|
+
|
234
|
+
Syn.trigger("change", {}, checkbox);
|
235
|
+
|
236
|
+
Syn.support.changeChecks = checkbox.checked;
|
237
|
+
|
238
|
+
form.onsubmit = function( ev ) {
|
239
|
+
if ( ev.preventDefault ) ev.preventDefault();
|
240
|
+
Syn.support.clickSubmits = true;
|
241
|
+
return false;
|
242
|
+
}
|
243
|
+
Syn.trigger("click", {}, submit)
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
form.childNodes[1].onchange = function() {
|
248
|
+
Syn.support.radioClickChanges = true;
|
249
|
+
}
|
250
|
+
Syn.trigger("click", {}, form.childNodes[1])
|
251
|
+
|
252
|
+
|
253
|
+
Syn.bind(div, 'click', function() {
|
254
|
+
Syn.support.optionClickBubbles = true;
|
255
|
+
Syn.unbind(div, 'click', arguments.callee)
|
256
|
+
})
|
257
|
+
Syn.trigger("click", {}, select.firstChild)
|
258
|
+
|
259
|
+
|
260
|
+
Syn.support.changeBubbles = Syn.eventSupported('change');
|
261
|
+
|
262
|
+
//test if mousedown followed by mouseup causes click (opera), make sure there are no clicks after this
|
263
|
+
var clicksCount = 0
|
264
|
+
div.onclick = function() {
|
265
|
+
Syn.support.mouseDownUpClicks = true;
|
266
|
+
//we should use this to check for opera potentially, but would
|
267
|
+
//be difficult to remove element correctly
|
268
|
+
//Syn.support.mouseDownUpRepeatClicks = (2 == (++clicksCount))
|
269
|
+
}
|
270
|
+
Syn.trigger("mousedown", {}, div)
|
271
|
+
Syn.trigger("mouseup", {}, div)
|
272
|
+
|
273
|
+
//setTimeout(function(){
|
274
|
+
// Syn.trigger("mousedown",{},div)
|
275
|
+
// Syn.trigger("mouseup",{},div)
|
276
|
+
//},1)
|
277
|
+
|
278
|
+
document.documentElement.removeChild(div);
|
279
|
+
|
280
|
+
//check stuff
|
281
|
+
window.__synthTest = oldSynth;
|
282
|
+
Syn.support.ready++;
|
283
|
+
})();
|
284
|
+
})(true);
|
@@ -0,0 +1,830 @@
|
|
1
|
+
(function(){
|
2
|
+
var extend = function( d, s ) {
|
3
|
+
var p;
|
4
|
+
for (p in s) {
|
5
|
+
d[p] = s[p];
|
6
|
+
}
|
7
|
+
return d;
|
8
|
+
},
|
9
|
+
// only uses browser detection for key events
|
10
|
+
browser = {
|
11
|
+
msie: !! (window.attachEvent && !window.opera),
|
12
|
+
opera: !! window.opera,
|
13
|
+
webkit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
|
14
|
+
safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') === -1,
|
15
|
+
gecko: navigator.userAgent.indexOf('Gecko') > -1,
|
16
|
+
mobilesafari: !! navigator.userAgent.match(/Apple.*Mobile.*Safari/),
|
17
|
+
rhino: navigator.userAgent.match(/Rhino/) && true
|
18
|
+
},
|
19
|
+
createEventObject = function( type, options, element ) {
|
20
|
+
var event = element.ownerDocument.createEventObject();
|
21
|
+
return extend(event, options);
|
22
|
+
},
|
23
|
+
data = {},
|
24
|
+
id = 1,
|
25
|
+
expando = "_synthetic" + new Date().getTime(),
|
26
|
+
bind, unbind, key = /keypress|keyup|keydown/,
|
27
|
+
page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/,
|
28
|
+
//this is maintained so we can click on html and blur the active element
|
29
|
+
activeElement,
|
30
|
+
|
31
|
+
/**
|
32
|
+
* @class Syn
|
33
|
+
* @download funcunit/dist/syn.js
|
34
|
+
* @test funcunit/synthetic/qunit.html
|
35
|
+
* Syn is used to simulate user actions. It creates synthetic events and
|
36
|
+
* performs their default behaviors.
|
37
|
+
*
|
38
|
+
* <h2>Basic Use</h2>
|
39
|
+
* The following clicks an input element with <code>id='description'</code>
|
40
|
+
* and then types <code>'Hello World'</code>.
|
41
|
+
*
|
42
|
+
@codestart
|
43
|
+
Syn.click({},'description')
|
44
|
+
.type("Hello World")
|
45
|
+
@codeend
|
46
|
+
* <h2>User Actions and Events</h2>
|
47
|
+
* <p>Syn is typically used to simulate user actions as opposed to triggering events. Typing characters
|
48
|
+
* is an example of a user action. The keypress that represents an <code>'a'</code>
|
49
|
+
* character being typed is an example of an event.
|
50
|
+
* </p>
|
51
|
+
* <p>
|
52
|
+
* While triggering events is supported, it's much more useful to simulate actual user behavior. The
|
53
|
+
* following actions are supported by Syn:
|
54
|
+
* </p>
|
55
|
+
* <ul>
|
56
|
+
* <li><code>[Syn.prototype.click click]</code> - a mousedown, focus, mouseup, and click.</li>
|
57
|
+
* <li><code>[Syn.prototype.dblclick dblclick]</code> - two <code>click!</code> events followed by a <code>dblclick</code>.</li>
|
58
|
+
* <li><code>[Syn.prototype.key key]</code> - types a single character (keydown, keypress, keyup).</li>
|
59
|
+
* <li><code>[Syn.prototype.type type]</code> - types multiple characters into an element.</li>
|
60
|
+
* <li><code>[Syn.prototype.move move]</code> - moves the mouse from one position to another (triggering mouseover / mouseouts).</li>
|
61
|
+
* <li><code>[Syn.prototype.drag drag]</code> - a mousedown, followed by mousemoves, and a mouseup.</li>
|
62
|
+
* </ul>
|
63
|
+
* All actions run asynchronously.
|
64
|
+
* Click on the links above for more
|
65
|
+
* information on how to use the specific action.
|
66
|
+
* <h2>Asynchronous Callbacks</h2>
|
67
|
+
* Actions don't complete immediately. This is almost
|
68
|
+
* entirely because <code>focus()</code>
|
69
|
+
* doesn't run immediately in IE.
|
70
|
+
* If you provide a callback function to Syn, it will
|
71
|
+
* be called after the action is completed.
|
72
|
+
* <br/>The following checks that "Hello World" was entered correctly:
|
73
|
+
@codestart
|
74
|
+
Syn.click({},'description')
|
75
|
+
.type("Hello World", function(){
|
76
|
+
|
77
|
+
ok("Hello World" == document.getElementById('description').value)
|
78
|
+
})
|
79
|
+
@codeend
|
80
|
+
<h2>Asynchronous Chaining</h2>
|
81
|
+
<p>You might have noticed the [Syn.prototype.then then] method. It provides chaining
|
82
|
+
so you can do a sequence of events with a single (final) callback.
|
83
|
+
</p><p>
|
84
|
+
If an element isn't provided to then, it uses the previous Syn's element.
|
85
|
+
</p>
|
86
|
+
The following does a lot of stuff before checking the result:
|
87
|
+
@codestart
|
88
|
+
Syn.type('ice water','title')
|
89
|
+
.type('ice and water','description')
|
90
|
+
.click({},'create')
|
91
|
+
.drag({to: 'favorites'},'newRecipe',
|
92
|
+
function(){
|
93
|
+
ok($('#newRecipe').parents('#favorites').length);
|
94
|
+
})
|
95
|
+
@codeend
|
96
|
+
|
97
|
+
<h2>jQuery Helper</h2>
|
98
|
+
If jQuery is present, Syn adds a triggerSyn helper you can use like:
|
99
|
+
@codestart
|
100
|
+
$("#description").triggerSyn("type","Hello World");
|
101
|
+
@codeend
|
102
|
+
* <h2>Key Event Recording</h2>
|
103
|
+
* <p>Every browser has very different rules for dispatching key events.
|
104
|
+
* As there is no way to feature detect how a browser handles key events,
|
105
|
+
* synthetic uses a description of how the browser behaves generated
|
106
|
+
* by a recording application. </p>
|
107
|
+
* <p>
|
108
|
+
* If you want to support a browser not currently supported, you can
|
109
|
+
* record that browser's key event description and add it to
|
110
|
+
* <code>Syn.key.browsers</code> by it's navigator agent.
|
111
|
+
* </p>
|
112
|
+
@codestart
|
113
|
+
Syn.key.browsers["Envjs\ Resig/20070309 PilotFish/1.2.0.10\1.6"] = {
|
114
|
+
'prevent':
|
115
|
+
{"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
|
116
|
+
'character':
|
117
|
+
{ ... }
|
118
|
+
}
|
119
|
+
@codeend
|
120
|
+
* <h2>Limitations</h2>
|
121
|
+
* Syn fully supports IE 6+, FF 3+, Chrome, Safari, Opera 10+.
|
122
|
+
* With FF 1+, drag / move events are only partially supported. They will
|
123
|
+
* not trigger mouseover / mouseout events.<br/>
|
124
|
+
* Safari crashes when a mousedown is triggered on a select. Syn will not
|
125
|
+
* create this event.
|
126
|
+
* <h2>Contributing to Syn</h2>
|
127
|
+
* Have we missed something? We happily accept patches. The following are
|
128
|
+
* important objects and properties of Syn:
|
129
|
+
* <ul>
|
130
|
+
* <li><code>Syn.create</code> - contains methods to setup, convert options, and create an event of a specific type.</li>
|
131
|
+
* <li><code>Syn.defaults</code> - default behavior by event type (except for keys).</li>
|
132
|
+
* <li><code>Syn.key.defaults</code> - default behavior by key.</li>
|
133
|
+
* <li><code>Syn.keycodes</code> - supported keys you can type.</li>
|
134
|
+
* </ul>
|
135
|
+
* <h2>Roll Your Own Functional Test Framework</h2>
|
136
|
+
* <p>Syn is really the foundation of JavaScriptMVC's functional testing framework - [FuncUnit].
|
137
|
+
* But, we've purposely made Syn work without any dependencies in the hopes that other frameworks or
|
138
|
+
* testing solutions can use it as well.
|
139
|
+
* </p>
|
140
|
+
* @constructor
|
141
|
+
* Creates a synthetic event on the element.
|
142
|
+
* @param {Object} type
|
143
|
+
* @param {Object} options
|
144
|
+
* @param {Object} element
|
145
|
+
* @param {Object} callback
|
146
|
+
* @return Syn
|
147
|
+
*/
|
148
|
+
Syn = function( type, options, element, callback ) {
|
149
|
+
return (new Syn.init(type, options, element, callback));
|
150
|
+
};
|
151
|
+
|
152
|
+
bind = function( el, ev, f ) {
|
153
|
+
return el.addEventListener ? el.addEventListener(ev, f, false) : el.attachEvent("on" + ev, f);
|
154
|
+
};
|
155
|
+
unbind = function( el, ev, f ) {
|
156
|
+
return el.addEventListener ? el.removeEventListener(ev, f, false) : el.detachEvent("on" + ev, f);
|
157
|
+
};
|
158
|
+
|
159
|
+
/**
|
160
|
+
* @Static
|
161
|
+
*/
|
162
|
+
extend(Syn, {
|
163
|
+
/**
|
164
|
+
* Creates a new synthetic event instance
|
165
|
+
* @hide
|
166
|
+
* @param {Object} type
|
167
|
+
* @param {Object} options
|
168
|
+
* @param {Object} element
|
169
|
+
* @param {Object} callback
|
170
|
+
*/
|
171
|
+
init: function( type, options, element, callback ) {
|
172
|
+
var args = Syn.args(options, element, callback),
|
173
|
+
self = this;
|
174
|
+
this.queue = [];
|
175
|
+
this.element = args.element;
|
176
|
+
|
177
|
+
//run event
|
178
|
+
if ( typeof this[type] === "function" ) {
|
179
|
+
this[type](args.options, args.element, function( defaults, el ) {
|
180
|
+
args.callback && args.callback.apply(self, arguments);
|
181
|
+
self.done.apply(self, arguments);
|
182
|
+
});
|
183
|
+
} else {
|
184
|
+
this.result = Syn.trigger(type, args.options, args.element);
|
185
|
+
args.callback && args.callback.call(this, args.element, this.result);
|
186
|
+
}
|
187
|
+
},
|
188
|
+
jquery: function( el, fast ) {
|
189
|
+
if ( window.FuncUnit && window.FuncUnit.jquery ) {
|
190
|
+
return window.FuncUnit.jquery;
|
191
|
+
}
|
192
|
+
if ( el ) {
|
193
|
+
return Syn.helpers.getWindow(el).jQuery || window.jQuery;
|
194
|
+
}
|
195
|
+
else {
|
196
|
+
return window.jQuery;
|
197
|
+
}
|
198
|
+
},
|
199
|
+
/**
|
200
|
+
* Returns an object with the args for a Syn.
|
201
|
+
* @hide
|
202
|
+
* @return {Object}
|
203
|
+
*/
|
204
|
+
args: function() {
|
205
|
+
var res = {},
|
206
|
+
i = 0;
|
207
|
+
for ( ; i < arguments.length; i++ ) {
|
208
|
+
if ( typeof arguments[i] === 'function' ) {
|
209
|
+
res.callback = arguments[i];
|
210
|
+
} else if ( arguments[i] && arguments[i].jquery ) {
|
211
|
+
res.element = arguments[i][0];
|
212
|
+
} else if ( arguments[i] && arguments[i].nodeName ) {
|
213
|
+
res.element = arguments[i];
|
214
|
+
} else if ( res.options && typeof arguments[i] === 'string' ) { //we can get by id
|
215
|
+
res.element = document.getElementById(arguments[i]);
|
216
|
+
}
|
217
|
+
else if ( arguments[i] ) {
|
218
|
+
res.options = arguments[i];
|
219
|
+
}
|
220
|
+
}
|
221
|
+
return res;
|
222
|
+
},
|
223
|
+
click: function( options, element, callback ) {
|
224
|
+
Syn('click!', options, element, callback);
|
225
|
+
},
|
226
|
+
/**
|
227
|
+
* @attribute defaults
|
228
|
+
* Default actions for events. Each default function is called with this as its
|
229
|
+
* element. It should return true if a timeout
|
230
|
+
* should happen after it. If it returns an element, a timeout will happen
|
231
|
+
* and the next event will happen on that element.
|
232
|
+
*/
|
233
|
+
defaults: {
|
234
|
+
focus: function() {
|
235
|
+
if (!Syn.support.focusChanges ) {
|
236
|
+
var element = this,
|
237
|
+
nodeName = element.nodeName.toLowerCase();
|
238
|
+
Syn.data(element, "syntheticvalue", element.value);
|
239
|
+
|
240
|
+
//TODO, this should be textarea too
|
241
|
+
//and this might be for only text style inputs ... hmmmmm ....
|
242
|
+
if ( nodeName === "input" || nodeName === "textarea" ) {
|
243
|
+
bind(element, "blur", function() {
|
244
|
+
if ( Syn.data(element, "syntheticvalue") != element.value ) {
|
245
|
+
|
246
|
+
Syn.trigger("change", {}, element);
|
247
|
+
}
|
248
|
+
unbind(element, "blur", arguments.callee);
|
249
|
+
});
|
250
|
+
|
251
|
+
}
|
252
|
+
}
|
253
|
+
},
|
254
|
+
submit: function() {
|
255
|
+
Syn.onParents(this, function( el ) {
|
256
|
+
if ( el.nodeName.toLowerCase() === 'form' ) {
|
257
|
+
el.submit();
|
258
|
+
return false;
|
259
|
+
}
|
260
|
+
});
|
261
|
+
}
|
262
|
+
},
|
263
|
+
changeOnBlur: function( element, prop, value ) {
|
264
|
+
|
265
|
+
bind(element, "blur", function() {
|
266
|
+
if ( value !== element[prop] ) {
|
267
|
+
Syn.trigger("change", {}, element);
|
268
|
+
}
|
269
|
+
unbind(element, "blur", arguments.callee);
|
270
|
+
});
|
271
|
+
|
272
|
+
},
|
273
|
+
/**
|
274
|
+
* Returns the closest element of a particular type.
|
275
|
+
* @hide
|
276
|
+
* @param {Object} el
|
277
|
+
* @param {Object} type
|
278
|
+
*/
|
279
|
+
closest: function( el, type ) {
|
280
|
+
while ( el && el.nodeName.toLowerCase() !== type.toLowerCase() ) {
|
281
|
+
el = el.parentNode;
|
282
|
+
}
|
283
|
+
return el;
|
284
|
+
},
|
285
|
+
/**
|
286
|
+
* adds jQuery like data (adds an expando) and data exists FOREVER :)
|
287
|
+
* @hide
|
288
|
+
* @param {Object} el
|
289
|
+
* @param {Object} key
|
290
|
+
* @param {Object} value
|
291
|
+
*/
|
292
|
+
data: function( el, key, value ) {
|
293
|
+
var d;
|
294
|
+
if (!el[expando] ) {
|
295
|
+
el[expando] = id++;
|
296
|
+
}
|
297
|
+
if (!data[el[expando]] ) {
|
298
|
+
data[el[expando]] = {};
|
299
|
+
}
|
300
|
+
d = data[el[expando]];
|
301
|
+
if ( value ) {
|
302
|
+
data[el[expando]][key] = value;
|
303
|
+
} else {
|
304
|
+
return data[el[expando]][key];
|
305
|
+
}
|
306
|
+
},
|
307
|
+
/**
|
308
|
+
* Calls a function on the element and all parents of the element until the function returns
|
309
|
+
* false.
|
310
|
+
* @hide
|
311
|
+
* @param {Object} el
|
312
|
+
* @param {Object} func
|
313
|
+
*/
|
314
|
+
onParents: function( el, func ) {
|
315
|
+
var res;
|
316
|
+
while ( el && res !== false ) {
|
317
|
+
res = func(el);
|
318
|
+
el = el.parentNode;
|
319
|
+
}
|
320
|
+
return el;
|
321
|
+
},
|
322
|
+
//regex to match focusable elements
|
323
|
+
focusable: /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i,
|
324
|
+
/**
|
325
|
+
* Returns if an element is focusable
|
326
|
+
* @hide
|
327
|
+
* @param {Object} elem
|
328
|
+
*/
|
329
|
+
isFocusable: function( elem ) {
|
330
|
+
var attributeNode;
|
331
|
+
return (this.focusable.test(elem.nodeName) ||
|
332
|
+
((attributeNode = elem.getAttributeNode("tabIndex"))
|
333
|
+
&& attributeNode.specified)) && Syn.isVisible(elem);
|
334
|
+
},
|
335
|
+
/**
|
336
|
+
* Returns if an element is visible or not
|
337
|
+
* @hide
|
338
|
+
* @param {Object} elem
|
339
|
+
*/
|
340
|
+
isVisible: function( elem ) {
|
341
|
+
return (elem.offsetWidth && elem.offsetHeight) || (elem.clientWidth && elem.clientHeight);
|
342
|
+
},
|
343
|
+
/**
|
344
|
+
* Gets the tabIndex as a number or null
|
345
|
+
* @hide
|
346
|
+
* @param {Object} elem
|
347
|
+
*/
|
348
|
+
tabIndex: function( elem ) {
|
349
|
+
var attributeNode = elem.getAttributeNode("tabIndex");
|
350
|
+
return attributeNode && attributeNode.specified && (parseInt(elem.getAttribute('tabIndex')) || 0);
|
351
|
+
},
|
352
|
+
bind: bind,
|
353
|
+
unbind: unbind,
|
354
|
+
browser: browser,
|
355
|
+
//some generic helpers
|
356
|
+
helpers: {
|
357
|
+
createEventObject: createEventObject,
|
358
|
+
createBasicStandardEvent: function( type, defaults, doc ) {
|
359
|
+
var event;
|
360
|
+
try {
|
361
|
+
event = doc.createEvent("Events");
|
362
|
+
} catch (e2) {
|
363
|
+
event = doc.createEvent("UIEvents");
|
364
|
+
} finally {
|
365
|
+
event.initEvent(type, true, true);
|
366
|
+
extend(event, defaults);
|
367
|
+
}
|
368
|
+
return event;
|
369
|
+
},
|
370
|
+
inArray: function( item, array ) {
|
371
|
+
var i =0;
|
372
|
+
for ( ; i < array.length; i++ ) {
|
373
|
+
if ( array[i] === item ) {
|
374
|
+
return i;
|
375
|
+
}
|
376
|
+
}
|
377
|
+
return -1;
|
378
|
+
},
|
379
|
+
getWindow: function( element ) {
|
380
|
+
return element.ownerDocument.defaultView || element.ownerDocument.parentWindow;
|
381
|
+
},
|
382
|
+
extend: extend,
|
383
|
+
scrollOffset: function( win , set) {
|
384
|
+
var doc = win.document.documentElement,
|
385
|
+
body = win.document.body;
|
386
|
+
if(set){
|
387
|
+
window.scrollTo(set.left, set.top);
|
388
|
+
|
389
|
+
} else {
|
390
|
+
return {
|
391
|
+
left: (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
|
392
|
+
top: (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
|
393
|
+
};
|
394
|
+
}
|
395
|
+
|
396
|
+
},
|
397
|
+
scrollDimensions: function(win){
|
398
|
+
var doc = win.document.documentElement,
|
399
|
+
body = win.document.body,
|
400
|
+
docWidth = doc.clientWidth,
|
401
|
+
docHeight = doc.clientHeight,
|
402
|
+
compat = win.document.compatMode === "CSS1Compat";
|
403
|
+
|
404
|
+
return {
|
405
|
+
height: compat && docHeight ||
|
406
|
+
body.clientHeight || docHeight,
|
407
|
+
width: compat && docWidth ||
|
408
|
+
body.clientWidth || docWidth
|
409
|
+
};
|
410
|
+
},
|
411
|
+
addOffset: function( options, el ) {
|
412
|
+
var jq = Syn.jquery(el),
|
413
|
+
off;
|
414
|
+
if ( typeof options === 'object' && options.clientX === undefined && options.clientY === undefined && options.pageX === undefined && options.pageY === undefined && jq ) {
|
415
|
+
el = jq(el);
|
416
|
+
off = el.offset();
|
417
|
+
options.pageX = off.left + el.width() / 2;
|
418
|
+
options.pageY = off.top + el.height() / 2;
|
419
|
+
}
|
420
|
+
}
|
421
|
+
},
|
422
|
+
// place for key data
|
423
|
+
key: {
|
424
|
+
ctrlKey: null,
|
425
|
+
altKey: null,
|
426
|
+
shiftKey: null,
|
427
|
+
metaKey: null
|
428
|
+
},
|
429
|
+
//triggers an event on an element, returns true if default events should be run
|
430
|
+
/**
|
431
|
+
* Dispatches an event and returns true if default events should be run.
|
432
|
+
* @hide
|
433
|
+
* @param {Object} event
|
434
|
+
* @param {Object} element
|
435
|
+
* @param {Object} type
|
436
|
+
* @param {Object} autoPrevent
|
437
|
+
*/
|
438
|
+
dispatch: function( event, element, type, autoPrevent ) {
|
439
|
+
|
440
|
+
// dispatchEvent doesn't always work in IE (mostly in a popup)
|
441
|
+
if ( element.dispatchEvent && event ) {
|
442
|
+
var preventDefault = event.preventDefault,
|
443
|
+
prevents = autoPrevent ? -1 : 0;
|
444
|
+
|
445
|
+
//automatically prevents the default behavior for this event
|
446
|
+
//this is to protect agianst nasty browser freezing bug in safari
|
447
|
+
if ( autoPrevent ) {
|
448
|
+
bind(element, type, function( ev ) {
|
449
|
+
ev.preventDefault();
|
450
|
+
unbind(this, type, arguments.callee);
|
451
|
+
});
|
452
|
+
}
|
453
|
+
|
454
|
+
|
455
|
+
event.preventDefault = function() {
|
456
|
+
prevents++;
|
457
|
+
if (++prevents > 0 ) {
|
458
|
+
preventDefault.apply(this, []);
|
459
|
+
}
|
460
|
+
};
|
461
|
+
element.dispatchEvent(event);
|
462
|
+
return prevents <= 0;
|
463
|
+
} else {
|
464
|
+
try {
|
465
|
+
window.event = event;
|
466
|
+
} catch (e) {}
|
467
|
+
//source element makes sure element is still in the document
|
468
|
+
return element.sourceIndex <= 0 || (element.fireEvent && element.fireEvent('on' + type, event));
|
469
|
+
}
|
470
|
+
},
|
471
|
+
/**
|
472
|
+
* @attribute
|
473
|
+
* @hide
|
474
|
+
* An object of eventType -> function that create that event.
|
475
|
+
*/
|
476
|
+
create: {
|
477
|
+
//-------- PAGE EVENTS ---------------------
|
478
|
+
page: {
|
479
|
+
event: function( type, options, element ) {
|
480
|
+
var doc = Syn.helpers.getWindow(element).document || document,
|
481
|
+
event;
|
482
|
+
if ( doc.createEvent ) {
|
483
|
+
event = doc.createEvent("Events");
|
484
|
+
|
485
|
+
event.initEvent(type, true, true);
|
486
|
+
return event;
|
487
|
+
}
|
488
|
+
else {
|
489
|
+
try {
|
490
|
+
event = createEventObject(type, options, element);
|
491
|
+
}
|
492
|
+
catch (e) {}
|
493
|
+
return event;
|
494
|
+
}
|
495
|
+
}
|
496
|
+
},
|
497
|
+
// unique events
|
498
|
+
focus: {
|
499
|
+
event: function( type, options, element ) {
|
500
|
+
Syn.onParents(element, function( el ) {
|
501
|
+
if ( Syn.isFocusable(el) ) {
|
502
|
+
if ( el.nodeName.toLowerCase() !== 'html' ) {
|
503
|
+
el.focus();
|
504
|
+
activeElement = el;
|
505
|
+
}
|
506
|
+
else if ( activeElement ) {
|
507
|
+
// TODO: The HTML element isn't focasable in IE, but it is
|
508
|
+
// in FF. We should detect this and do a true focus instead
|
509
|
+
// of just a blur
|
510
|
+
var doc = Syn.helpers.getWindow(element).document;
|
511
|
+
if ( doc !== window.document ) {
|
512
|
+
return false;
|
513
|
+
} else if ( doc.activeElement ) {
|
514
|
+
doc.activeElement.blur();
|
515
|
+
activeElement = null;
|
516
|
+
} else {
|
517
|
+
activeElement.blur();
|
518
|
+
activeElement = null;
|
519
|
+
}
|
520
|
+
|
521
|
+
|
522
|
+
}
|
523
|
+
return false;
|
524
|
+
}
|
525
|
+
});
|
526
|
+
return true;
|
527
|
+
}
|
528
|
+
}
|
529
|
+
},
|
530
|
+
/**
|
531
|
+
* @attribute support
|
532
|
+
* Feature detected properties of a browser's event system.
|
533
|
+
* Support has the following properties:
|
534
|
+
* <ul>
|
535
|
+
* <li><code>clickChanges</code> - clicking on an option element creates a change event.</li>
|
536
|
+
* <li><code>clickSubmits</code> - clicking on a form button submits the form.</li>
|
537
|
+
* <li><code>mouseupSubmits</code> - a mouseup on a form button submits the form.</li>
|
538
|
+
* <li><code>radioClickChanges</code> - clicking a radio button changes the radio.</li>
|
539
|
+
* <li><code>focusChanges</code> - focus/blur creates a change event.</li>
|
540
|
+
* <li><code>linkHrefJS</code> - An achor's href JavaScript is run.</li>
|
541
|
+
* <li><code>mouseDownUpClicks</code> - A mousedown followed by mouseup creates a click event.</li>
|
542
|
+
* <li><code>tabKeyTabs</code> - A tab key changes tabs.</li>
|
543
|
+
* <li><code>keypressOnAnchorClicks</code> - Keying enter on an anchor triggers a click.</li>
|
544
|
+
* </ul>
|
545
|
+
*/
|
546
|
+
support: {
|
547
|
+
clickChanges: false,
|
548
|
+
clickSubmits: false,
|
549
|
+
keypressSubmits: false,
|
550
|
+
mouseupSubmits: false,
|
551
|
+
radioClickChanges: false,
|
552
|
+
focusChanges: false,
|
553
|
+
linkHrefJS: false,
|
554
|
+
keyCharacters: false,
|
555
|
+
backspaceWorks: false,
|
556
|
+
mouseDownUpClicks: false,
|
557
|
+
tabKeyTabs: false,
|
558
|
+
keypressOnAnchorClicks: false,
|
559
|
+
optionClickBubbles: false,
|
560
|
+
ready: 0
|
561
|
+
},
|
562
|
+
/**
|
563
|
+
* Creates a synthetic event and dispatches it on the element.
|
564
|
+
* This will run any default actions for the element.
|
565
|
+
* Typically you want to use Syn, but if you want the return value, use this.
|
566
|
+
* @param {String} type
|
567
|
+
* @param {Object} options
|
568
|
+
* @param {HTMLElement} element
|
569
|
+
* @return {Boolean} true if default events were run, false if otherwise.
|
570
|
+
*/
|
571
|
+
trigger: function( type, options, element ) {
|
572
|
+
options || (options = {});
|
573
|
+
|
574
|
+
var create = Syn.create,
|
575
|
+
setup = create[type] && create[type].setup,
|
576
|
+
kind = key.test(type) ? 'key' : (page.test(type) ? "page" : "mouse"),
|
577
|
+
createType = create[type] || {},
|
578
|
+
createKind = create[kind],
|
579
|
+
event, ret, autoPrevent, dispatchEl = element;
|
580
|
+
|
581
|
+
//any setup code?
|
582
|
+
Syn.support.ready === 2 && setup && setup(type, options, element);
|
583
|
+
|
584
|
+
autoPrevent = options._autoPrevent;
|
585
|
+
//get kind
|
586
|
+
delete options._autoPrevent;
|
587
|
+
|
588
|
+
if ( createType.event ) {
|
589
|
+
ret = createType.event(type, options, element);
|
590
|
+
} else {
|
591
|
+
//convert options
|
592
|
+
options = createKind.options ? createKind.options(type, options, element) : options;
|
593
|
+
|
594
|
+
if (!Syn.support.changeBubbles && /option/i.test(element.nodeName) ) {
|
595
|
+
dispatchEl = element.parentNode; //jQuery expects clicks on select
|
596
|
+
}
|
597
|
+
|
598
|
+
//create the event
|
599
|
+
event = createKind.event(type, options, dispatchEl);
|
600
|
+
|
601
|
+
//send the event
|
602
|
+
ret = Syn.dispatch(event, dispatchEl, type, autoPrevent);
|
603
|
+
}
|
604
|
+
|
605
|
+
//run default behavior
|
606
|
+
ret && Syn.support.ready === 2 && Syn.defaults[type] && Syn.defaults[type].call(element, options, autoPrevent);
|
607
|
+
return ret;
|
608
|
+
},
|
609
|
+
eventSupported: function( eventName ) {
|
610
|
+
var el = document.createElement("div");
|
611
|
+
eventName = "on" + eventName;
|
612
|
+
|
613
|
+
var isSupported = (eventName in el);
|
614
|
+
if (!isSupported ) {
|
615
|
+
el.setAttribute(eventName, "return;");
|
616
|
+
isSupported = typeof el[eventName] === "function";
|
617
|
+
}
|
618
|
+
el = null;
|
619
|
+
|
620
|
+
return isSupported;
|
621
|
+
}
|
622
|
+
|
623
|
+
});
|
624
|
+
/**
|
625
|
+
* @Prototype
|
626
|
+
*/
|
627
|
+
extend(Syn.init.prototype, {
|
628
|
+
/**
|
629
|
+
* @function then
|
630
|
+
* <p>
|
631
|
+
* Then is used to chain a sequence of actions to be run one after the other.
|
632
|
+
* This is useful when many asynchronous actions need to be performed before some
|
633
|
+
* final check needs to be made.
|
634
|
+
* </p>
|
635
|
+
* <p>The following clicks and types into the <code>id='age'</code> element and then checks that only numeric characters can be entered.</p>
|
636
|
+
* <h3>Example</h3>
|
637
|
+
* @codestart
|
638
|
+
* Syn('click',{},'age')
|
639
|
+
* .then('type','I am 12',function(){
|
640
|
+
* equals($('#age').val(),"12")
|
641
|
+
* })
|
642
|
+
* @codeend
|
643
|
+
* If the element argument is undefined, then the last element is used.
|
644
|
+
*
|
645
|
+
* @param {String} type The type of event or action to create: "_click", "_dblclick", "_drag", "_type".
|
646
|
+
* @param {Object} options Optiosn to pass to the event.
|
647
|
+
* @param {String|HTMLElement} [element] A element's id or an element. If undefined, defaults to the previous element.
|
648
|
+
* @param {Function} [callback] A function to callback after the action has run, but before any future chained actions are run.
|
649
|
+
*/
|
650
|
+
then: function( type, options, element, callback ) {
|
651
|
+
if ( Syn.autoDelay ) {
|
652
|
+
this.delay();
|
653
|
+
}
|
654
|
+
var args = Syn.args(options, element, callback),
|
655
|
+
self = this;
|
656
|
+
|
657
|
+
|
658
|
+
//if stack is empty run right away
|
659
|
+
//otherwise ... unshift it
|
660
|
+
this.queue.unshift(function( el, prevented ) {
|
661
|
+
|
662
|
+
if ( typeof this[type] === "function" ) {
|
663
|
+
this.element = args.element || el;
|
664
|
+
this[type](args.options, this.element, function( defaults, el ) {
|
665
|
+
args.callback && args.callback.apply(self, arguments);
|
666
|
+
self.done.apply(self, arguments);
|
667
|
+
});
|
668
|
+
} else {
|
669
|
+
this.result = Syn.trigger(type, args.options, args.element);
|
670
|
+
args.callback && args.callback.call(this, args.element, this.result);
|
671
|
+
return this;
|
672
|
+
}
|
673
|
+
})
|
674
|
+
return this;
|
675
|
+
},
|
676
|
+
/**
|
677
|
+
* Delays the next command a set timeout.
|
678
|
+
* @param {Number} [timeout]
|
679
|
+
* @param {Function} [callback]
|
680
|
+
*/
|
681
|
+
delay: function( timeout, callback ) {
|
682
|
+
if ( typeof timeout === 'function' ) {
|
683
|
+
callback = timeout;
|
684
|
+
timeout = null;
|
685
|
+
}
|
686
|
+
timeout = timeout || 600;
|
687
|
+
var self = this;
|
688
|
+
this.queue.unshift(function() {
|
689
|
+
setTimeout(function() {
|
690
|
+
callback && callback.apply(self, [])
|
691
|
+
self.done.apply(self, arguments);
|
692
|
+
}, timeout);
|
693
|
+
});
|
694
|
+
return this;
|
695
|
+
},
|
696
|
+
done: function( defaults, el ) {
|
697
|
+
el && (this.element = el);
|
698
|
+
if ( this.queue.length ) {
|
699
|
+
this.queue.pop().call(this, this.element, defaults);
|
700
|
+
}
|
701
|
+
|
702
|
+
},
|
703
|
+
/**
|
704
|
+
* @function click
|
705
|
+
* Clicks an element by triggering a mousedown,
|
706
|
+
* mouseup,
|
707
|
+
* and a click event.
|
708
|
+
* <h3>Example</h3>
|
709
|
+
* @codestart
|
710
|
+
* Syn.click({},'create',function(){
|
711
|
+
* //check something
|
712
|
+
* })
|
713
|
+
* @codeend
|
714
|
+
* You can also provide the coordinates of the click.
|
715
|
+
* If jQuery is present, it will set clientX and clientY
|
716
|
+
* for you. Here's how to set it yourself:
|
717
|
+
* @codestart
|
718
|
+
* Syn.click(
|
719
|
+
* {clientX: 20, clientY: 100},
|
720
|
+
* 'create',
|
721
|
+
* function(){
|
722
|
+
* //check something
|
723
|
+
* })
|
724
|
+
* @codeend
|
725
|
+
* You can also provide pageX and pageY and Syn will convert it for you.
|
726
|
+
* @param {Object} options
|
727
|
+
* @param {HTMLElement} element
|
728
|
+
* @param {Function} callback
|
729
|
+
*/
|
730
|
+
"_click": function( options, element, callback, force ) {
|
731
|
+
Syn.helpers.addOffset(options, element);
|
732
|
+
Syn.trigger("mousedown", options, element);
|
733
|
+
|
734
|
+
//timeout is b/c IE is stupid and won't call focus handlers
|
735
|
+
setTimeout(function() {
|
736
|
+
Syn.trigger("mouseup", options, element);
|
737
|
+
if (!Syn.support.mouseDownUpClicks || force ) {
|
738
|
+
Syn.trigger("click", options, element);
|
739
|
+
callback(true);
|
740
|
+
} else {
|
741
|
+
//we still have to run the default (presumably)
|
742
|
+
Syn.create.click.setup('click', options, element);
|
743
|
+
Syn.defaults.click.call(element);
|
744
|
+
//must give time for callback
|
745
|
+
setTimeout(function() {
|
746
|
+
callback(true);
|
747
|
+
}, 1);
|
748
|
+
}
|
749
|
+
|
750
|
+
}, 1);
|
751
|
+
},
|
752
|
+
/**
|
753
|
+
* Right clicks in browsers that support it (everyone but opera).
|
754
|
+
* @param {Object} options
|
755
|
+
* @param {Object} element
|
756
|
+
* @param {Object} callback
|
757
|
+
*/
|
758
|
+
"_rightClick": function( options, element, callback ) {
|
759
|
+
Syn.helpers.addOffset(options, element);
|
760
|
+
var mouseopts = extend(extend({}, Syn.mouse.browser.right.mouseup), options);
|
761
|
+
|
762
|
+
Syn.trigger("mousedown", mouseopts, element);
|
763
|
+
|
764
|
+
//timeout is b/c IE is stupid and won't call focus handlers
|
765
|
+
setTimeout(function() {
|
766
|
+
Syn.trigger("mouseup", mouseopts, element);
|
767
|
+
if ( Syn.mouse.browser.right.contextmenu ) {
|
768
|
+
Syn.trigger("contextmenu", extend(extend({}, Syn.mouse.browser.right.contextmenu), options), element);
|
769
|
+
}
|
770
|
+
callback(true);
|
771
|
+
}, 1);
|
772
|
+
},
|
773
|
+
/**
|
774
|
+
* @function dblclick
|
775
|
+
* Dblclicks an element. This runs two [Syn.prototype.click click] events followed by
|
776
|
+
* a dblclick on the element.
|
777
|
+
* <h3>Example</h3>
|
778
|
+
* @codestart
|
779
|
+
* Syn.dblclick({},'open')
|
780
|
+
* @codeend
|
781
|
+
* @param {Object} options
|
782
|
+
* @param {HTMLElement} element
|
783
|
+
* @param {Function} callback
|
784
|
+
*/
|
785
|
+
"_dblclick": function( options, element, callback ) {
|
786
|
+
Syn.helpers.addOffset(options, element);
|
787
|
+
var self = this;
|
788
|
+
this._click(options, element, function() {
|
789
|
+
setTimeout(function() {
|
790
|
+
self._click(options, element, function() {
|
791
|
+
Syn.trigger("dblclick", options, element);
|
792
|
+
callback(true);
|
793
|
+
}, true);
|
794
|
+
}, 2);
|
795
|
+
|
796
|
+
});
|
797
|
+
}
|
798
|
+
});
|
799
|
+
|
800
|
+
var actions = ["click", "dblclick", "move", "drag", "key", "type", 'rightClick'],
|
801
|
+
makeAction = function( name ) {
|
802
|
+
Syn[name] = function( options, element, callback ) {
|
803
|
+
return Syn("_" + name, options, element, callback);
|
804
|
+
};
|
805
|
+
Syn.init.prototype[name] = function( options, element, callback ) {
|
806
|
+
return this.then("_" + name, options, element, callback);
|
807
|
+
};
|
808
|
+
},
|
809
|
+
i = 0;
|
810
|
+
for ( ; i < actions.length; i++ ) {
|
811
|
+
makeAction(actions[i]);
|
812
|
+
}
|
813
|
+
/**
|
814
|
+
* Used for creating and dispatching synthetic events.
|
815
|
+
* @codestart
|
816
|
+
* new MVC.Syn('click').send(MVC.$E('id'))
|
817
|
+
* @codeend
|
818
|
+
* @constructor Sets up a synthetic event.
|
819
|
+
* @param {String} type type of event, ex: 'click'
|
820
|
+
* @param {optional:Object} options
|
821
|
+
*/
|
822
|
+
if ( (window.FuncUnit && window.FuncUnit.jQuery) || window.jQuery ) {
|
823
|
+
((window.FuncUnit && window.FuncUnit.jQuery) || window.jQuery).fn.triggerSyn = function( type, options, callback ) {
|
824
|
+
Syn(type, options, this[0], callback);
|
825
|
+
return this;
|
826
|
+
};
|
827
|
+
}
|
828
|
+
|
829
|
+
window.Syn = Syn;
|
830
|
+
})(true);
|