UserAction.js 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888
  1. /**
  2. * 测试用例库文件,提供如event mock、iframe封装等各种常用功能 部分方法来源于YUI测试框架
  3. */
  4. UserAction = {
  5. beforedispatch:null,
  6. // flag : true,
  7. isf /* is function ? */:function (value) {
  8. return value && (typeof value == 'function');
  9. },
  10. isb /* is boolean? */:function (value) {
  11. return value && (typeof value == 'boolean');
  12. },
  13. iso /* is object? */:function (value) {
  14. return value && (typeof value == 'object');
  15. },
  16. iss /* is string? */:function (value) {
  17. return value && (typeof value == 'string');
  18. },
  19. isn /* is number? */:function (value) {
  20. return value && (typeof value == 'number');
  21. },
  22. // --------------------------------------------------------------------------
  23. // Generic event methods
  24. // --------------------------------------------------------------------------
  25. /**
  26. * Simulates a key event using the given event information to populate the
  27. * generated event object. This method does browser-equalizing calculations
  28. * to account for differences in the DOM and IE event models as well as
  29. * different browser quirks. Note: keydown causes Safari 2.x to crash.
  30. *
  31. * @method simulateKeyEvent
  32. * @private
  33. * @static
  34. * @param {HTMLElement}
  35. * target The target of the given event.
  36. * @param {String}
  37. * type The type of event to fire. This can be any one of the
  38. * following: keyup, keydown, and keypress.
  39. * @param {Boolean}
  40. * bubbles (Optional) Indicates if the event can be bubbled up.
  41. * DOM Level 3 specifies that all key events bubble by default.
  42. * The default is true.
  43. * @param {Boolean}
  44. * cancelable (Optional) Indicates if the event can be canceled
  45. * using preventDefault(). DOM Level 3 specifies that all key
  46. * events can be cancelled. The default is true.
  47. * @param {Window}
  48. * view (Optional) The view containing the target. This is
  49. * typically the window object. The default is window.
  50. * @param {Boolean}
  51. * ctrlKey (Optional) Indicates if one of the CTRL keys is
  52. * pressed while the event is firing. The default is false.
  53. * @param {Boolean}
  54. * altKey (Optional) Indicates if one of the ALT keys is pressed
  55. * while the event is firing. The default is false.
  56. * @param {Boolean}
  57. * shiftKey (Optional) Indicates if one of the SHIFT keys is
  58. * pressed while the event is firing. The default is false.
  59. * @param {Boolean}
  60. * metaKey (Optional) Indicates if one of the META keys is
  61. * pressed while the event is firing. The default is false.
  62. * @param {int}
  63. * keyCode (Optional) The code for the key that is in use. The
  64. * default is 0.
  65. * @param {int}
  66. * charCode (Optional) The Unicode code for the character
  67. * associated with the key being used. The default is 0.
  68. */
  69. simulateKeyEvent:function (target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, keyCode /* :int */, charCode /* :int */) /* :Void */ {
  70. // check target
  71. target = typeof target == 'string' ? document.getElementById(target)
  72. : target;
  73. if (!target) {
  74. throw new Error("simulateKeyEvent(): Invalid target.");
  75. }
  76. // check event type
  77. if (typeof type == 'string') {
  78. type = type.toLowerCase();
  79. switch (type) {
  80. case "compositionend":
  81. case "compositionstart":
  82. case "paste":
  83. case "cut":
  84. case "keyup":
  85. case "keydown":
  86. case "keypress":
  87. break;
  88. case "textevent": // DOM Level 3
  89. type = "keypress";
  90. break;
  91. // @TODO was the fallthrough intentional, if so throw error
  92. default:
  93. throw new Error("simulateKeyEvent(): Event type '" + type
  94. + "' not supported.");
  95. }
  96. } else {
  97. throw new Error("simulateKeyEvent(): Event type must be a string.");
  98. }
  99. // setup default values
  100. if (!this.isb(bubbles)) {
  101. bubbles = true; // all key events bubble
  102. }
  103. if (!this.isb(cancelable)) {
  104. cancelable = true; // all key events can be cancelled
  105. }
  106. if (!this.iso(view)) {
  107. view = window; // view is typically window
  108. }
  109. if (!this.isb(ctrlKey)) {
  110. ctrlKey = false;
  111. }
  112. if (!this.isb(typeof altKey == 'boolean')) {
  113. altKey = false;
  114. }
  115. if (!this.isb(shiftKey)) {
  116. shiftKey = false;
  117. }
  118. if (!this.isb(metaKey)) {
  119. metaKey = false;
  120. }
  121. if (!(typeof keyCode == 'number')) {
  122. keyCode = 0;
  123. }
  124. if (!(typeof charCode == 'number')) {
  125. charCode = 0;
  126. }
  127. // try to create a mouse event
  128. var customEvent /* :MouseEvent */ = null;
  129. // check for DOM-compliant browsers first
  130. if (this.isf(document.createEvent)) {
  131. try {
  132. // try to create key event
  133. customEvent = document.createEvent("KeyEvents");
  134. /*
  135. * Interesting problem: Firefox implemented a non-standard
  136. * version of initKeyEvent() based on DOM Level 2 specs. Key
  137. * event was removed from DOM Level 2 and re-introduced in DOM
  138. * Level 3 with a different interface. Firefox is the only
  139. * browser with any implementation of Key Events, so for now,
  140. * assume it's Firefox if the above line doesn't error.
  141. */
  142. // TODO: Decipher between Firefox's implementation and a correct
  143. // one.
  144. customEvent.initKeyEvent(type, bubbles, cancelable, view,
  145. ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode);
  146. } catch (ex /* :Error */) {
  147. /*
  148. * If it got here, that means key events aren't officially
  149. * supported. Safari/WebKit is a real problem now. WebKit 522
  150. * won't let you set keyCode, charCode, or other properties if
  151. * you use a UIEvent, so we first must try to create a generic
  152. * event. The fun part is that this will throw an error on
  153. * Safari 2.x. The end result is that we need another
  154. * try...catch statement just to deal with this mess.
  155. */
  156. try {
  157. // try to create generic event - will fail in Safari 2.x
  158. customEvent = document.createEvent("Events");
  159. } catch (uierror /* :Error */) {
  160. // the above failed, so create a UIEvent for Safari 2.x
  161. customEvent = document.createEvent("UIEvents");
  162. } finally {
  163. customEvent.initEvent(type, bubbles, cancelable);
  164. // initialize
  165. customEvent.view = view;
  166. customEvent.altKey = altKey;
  167. customEvent.ctrlKey = ctrlKey;
  168. customEvent.shiftKey = shiftKey;
  169. customEvent.metaKey = metaKey;
  170. customEvent.keyCode = keyCode;
  171. customEvent.charCode = charCode;
  172. }
  173. }
  174. // before dispatch
  175. if (this.beforedispatch && typeof this.beforedispatch == 'function')
  176. this.beforedispatch(customEvent);
  177. this.beforedispatch = null;
  178. // fire the event
  179. target.dispatchEvent(customEvent);
  180. } else if (this.iso(document.createEventObject)) { // IE
  181. // create an IE event object
  182. customEvent = document.createEventObject();
  183. // assign available properties
  184. customEvent.bubbles = bubbles;
  185. customEvent.cancelable = cancelable;
  186. customEvent.view = view;
  187. customEvent.ctrlKey = ctrlKey;
  188. customEvent.altKey = altKey;
  189. customEvent.shiftKey = shiftKey;
  190. customEvent.metaKey = metaKey;
  191. /*
  192. * IE doesn't support charCode explicitly. CharCode should take
  193. * precedence over any keyCode value for accurate representation.
  194. */
  195. customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
  196. // before dispatch
  197. if (this.beforedispatch && typeof this.beforedispatch == 'function')
  198. this.beforedispatch(customEvent);
  199. this.beforedispatch = null;
  200. // fire the event
  201. target.fireEvent("on" + type, customEvent);
  202. } else {
  203. throw new Error(
  204. "simulateKeyEvent(): No event simulation framework present.");
  205. }
  206. this.beforedispatch = null;
  207. },
  208. /**
  209. * Simulates a mouse event using the given event information to populate the
  210. * generated event object. This method does browser-equalizing calculations
  211. * to account for differences in the DOM and IE event models as well as
  212. * different browser quirks.
  213. *
  214. * @method simulateMouseEvent
  215. * @private
  216. * @static
  217. * @param {HTMLElement}
  218. * target The target of the given event.
  219. * @param {String}
  220. * type The type of event to fire. This can be any one of the
  221. * following: click, dblclick, mousedown, mouseup, mouseout,
  222. * mouseover, and mousemove.
  223. * @param {Boolean}
  224. * bubbles (Optional) Indicates if the event can be bubbled up.
  225. * DOM Level 2 specifies that all mouse events bubble by default.
  226. * The default is true.
  227. * @param {Boolean}
  228. * cancelable (Optional) Indicates if the event can be canceled
  229. * using preventDefault(). DOM Level 2 specifies that all mouse
  230. * events except mousemove can be cancelled. The default is true
  231. * for all events except mousemove, for which the default is
  232. * false.
  233. * @param {Window}
  234. * view (Optional) The view containing the target. This is
  235. * typically the window object. The default is window.
  236. * @param {int}
  237. * detail (Optional) The number of times the mouse button has
  238. * been used. The default value is 1.
  239. * @param {int}
  240. * screenX (Optional) The x-coordinate on the screen at which
  241. * point the event occured. The default is 0.
  242. * @param {int}
  243. * screenY (Optional) The y-coordinate on the screen at which
  244. * point the event occured. The default is 0.
  245. * @param {int}
  246. * clientX (Optional) The x-coordinate on the client at which
  247. * point the event occured. The default is 0.
  248. * @param {int}
  249. * clientY (Optional) The y-coordinate on the client at which
  250. * point the event occured. The default is 0.
  251. * @param {Boolean}
  252. * ctrlKey (Optional) Indicates if one of the CTRL keys is
  253. * pressed while the event is firing. The default is false.
  254. * @param {Boolean}
  255. * altKey (Optional) Indicates if one of the ALT keys is pressed
  256. * while the event is firing. The default is false.
  257. * @param {Boolean}
  258. * shiftKey (Optional) Indicates if one of the SHIFT keys is
  259. * pressed while the event is firing. The default is false.
  260. * @param {Boolean}
  261. * metaKey (Optional) Indicates if one of the META keys is
  262. * pressed while the event is firing. The default is false.
  263. * @param {int}
  264. * button (Optional) The button being pressed while the event is
  265. * executing. The value should be 0 for the primary mouse button
  266. * (typically the left button), 1 for the terciary mouse button
  267. * (typically the middle button), and 2 for the secondary mouse
  268. * button (typically the right button). The default is 0.
  269. * @param {HTMLElement}
  270. * relatedTarget (Optional) For mouseout events, this is the
  271. * element that the mouse has moved to. For mouseover events,
  272. * this is the element that the mouse has moved from. This
  273. * argument is ignored for all other events. The default is null.
  274. */
  275. simulateMouseEvent:function (target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, detail /* :int */, screenX /* :int */, screenY /* :int */, clientX /* :int */, clientY /* :int */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, button /* :int */, relatedTarget /* :HTMLElement */,button) /* :Void */ {
  276. // check target
  277. target = typeof target == 'string' ? document.getElementById(target)
  278. : target;
  279. if (!target) {
  280. throw new Error("simulateMouseEvent(): Invalid target.");
  281. }
  282. // check event type
  283. if (this.iss(type)) {
  284. type = type.toLowerCase();
  285. switch (type) {
  286. case "mouseover":
  287. case "mouseout":
  288. case "mousedown":
  289. case "mouseup":
  290. case "click":
  291. case "dblclick":
  292. case "mousemove":
  293. case "mouseenter":// 非标准支持,仅为测试提供,该项仅IE下work
  294. case "mouseleave":
  295. case "contextmenu":
  296. case "dragend":
  297. case "blur":
  298. break;
  299. default:
  300. throw new Error("simulateMouseEvent(): Event type '" + type
  301. + "' not supported.");
  302. }
  303. } else {
  304. throw new Error(
  305. "simulateMouseEvent(): Event type must be a string.");
  306. }
  307. // setup default values
  308. if (!this.isb(bubbles)) {
  309. bubbles = true; // all mouse events bubble
  310. }
  311. if (!this.isb(cancelable)) {
  312. cancelable = (type != "mousemove"); // mousemove is the only one
  313. // that can't be cancelled
  314. }
  315. if (!this.iso(view)) {
  316. view = window; // view is typically window
  317. }
  318. if (!this.isn(detail)) {
  319. detail = 1; // number of mouse clicks must be at least one
  320. }
  321. if (!this.isn(screenX)) {
  322. screenX = 0;
  323. }
  324. if (!this.isn(screenY)) {
  325. screenY = 0;
  326. }
  327. if (!this.isn(clientX)) {
  328. clientX = 0;
  329. }
  330. if (!this.isn(clientY)) {
  331. clientY = 0;
  332. }
  333. if (!this.isb(ctrlKey)) {
  334. ctrlKey = false;
  335. }
  336. if (!this.isb(altKey)) {
  337. altKey = false;
  338. }
  339. if (!this.isb(shiftKey)) {
  340. shiftKey = false;
  341. }
  342. if (!this.isb(metaKey)) {
  343. metaKey = false;
  344. }
  345. if (!this.isn(button)) {
  346. button = 0;
  347. }
  348. // try to create a mouse event
  349. var customEvent /* :MouseEvent */ = null;
  350. // check for DOM-compliant browsers first
  351. if (this.isf(document.createEvent)) {
  352. customEvent = document.createEvent("MouseEvents");
  353. // Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
  354. if (this.browser.ie < 9 && customEvent.initMouseEvent) {
  355. customEvent.initMouseEvent(type, bubbles, cancelable, view,
  356. detail, screenX, screenY, clientX, clientY, ctrlKey,
  357. altKey, shiftKey, metaKey, button, relatedTarget);
  358. } else { // Safari
  359. // the closest thing available in Safari 2.x is UIEvents
  360. customEvent = document.createEvent("UIEvents");
  361. customEvent.initEvent(type, bubbles, cancelable);
  362. customEvent.view = view;
  363. customEvent.detail = detail;
  364. customEvent.screenX = screenX;
  365. customEvent.screenY = screenY;
  366. customEvent.clientX = clientX;
  367. customEvent.clientY = clientY;
  368. customEvent.ctrlKey = ctrlKey;
  369. customEvent.altKey = altKey;
  370. customEvent.metaKey = metaKey;
  371. customEvent.shiftKey = shiftKey;
  372. customEvent.button = button;
  373. customEvent.relatedTarget = relatedTarget;
  374. }
  375. /*
  376. * Check to see if relatedTarget has been assigned. Firefox versions
  377. * less than 2.0 don't allow it to be assigned via initMouseEvent()
  378. * and the property is readonly after event creation, so in order to
  379. * keep YAHOO.util.getRelatedTarget() working, assign to the IE
  380. * proprietary toElement property for mouseout event and fromElement
  381. * property for mouseover event.
  382. */
  383. if (relatedTarget && !customEvent.relatedTarget) {
  384. if (type == "mouseout") {
  385. customEvent.toElement = relatedTarget;
  386. } else if (type == "mouseover") {
  387. customEvent.fromElement = relatedTarget;
  388. }
  389. }
  390. // before dispatch
  391. if (this.beforedispatch && typeof this.beforedispatch == 'function')
  392. this.beforedispatch(customEvent);
  393. this.beforedispatch = null;
  394. // fire the event
  395. target.dispatchEvent(customEvent);
  396. } else if (this.iso(document.createEventObject)) { // IE
  397. // create an IE event object
  398. customEvent = document.createEventObject();
  399. // assign available properties
  400. customEvent.bubbles = bubbles;
  401. customEvent.cancelable = cancelable;
  402. customEvent.view = view;
  403. customEvent.detail = detail;
  404. customEvent.screenX = screenX;
  405. customEvent.screenY = screenY;
  406. customEvent.clientX = clientX;
  407. customEvent.clientY = clientY;
  408. customEvent.ctrlKey = ctrlKey;
  409. customEvent.altKey = altKey;
  410. customEvent.metaKey = metaKey;
  411. customEvent.shiftKey = shiftKey;
  412. // fix button property for IE's wacky implementation
  413. switch (button) {
  414. case 0:
  415. customEvent.button = 1;
  416. break;
  417. case 1:
  418. customEvent.button = 4;
  419. break;
  420. case 2:
  421. customEvent.button = 2;
  422. // leave as is
  423. break;
  424. default:
  425. customEvent.button = 0;
  426. }
  427. /*
  428. * Have to use relatedTarget because IE won't allow assignment to
  429. * toElement or fromElement on generic events. This keeps
  430. * YAHOO.util.customEvent.getRelatedTarget() functional.
  431. */
  432. customEvent.relatedTarget = relatedTarget;
  433. // before dispatch
  434. if (this.beforedispatch && typeof this.beforedispatch == 'function')
  435. this.beforedispatch(customEvent);
  436. this.beforedispatch = null;
  437. // fire the event
  438. target.fireEvent("on" + type, customEvent);
  439. } else {
  440. throw new Error(
  441. "simulateMouseEvent(): No event simulation framework present.");
  442. }
  443. },
  444. // --------------------------------------------------------------------------
  445. // Mouse events
  446. // --------------------------------------------------------------------------
  447. /**
  448. * Simulates a mouse event on a particular element.
  449. *
  450. * @param {HTMLElement}
  451. * target The element to click on.
  452. * @param {String}
  453. * type The type of event to fire. This can be any one of the
  454. * following: click, dblclick, mousedown, mouseup, mouseout,
  455. * mouseover, and mousemove.
  456. * @param {Object}
  457. * options Additional event options (use DOM standard names).
  458. * @method mouseEvent
  459. * @static
  460. */
  461. fireMouseEvent:function (target /* :HTMLElement */, type /* :String */, options /* :Object */) /* :Void */ {
  462. options = options || {};
  463. this.simulateMouseEvent(target, type, options.bubbles,
  464. options.cancelable, options.view, options.detail,
  465. options.screenX, options.screenY, options.clientX,
  466. options.clientY, options.ctrlKey, options.altKey,
  467. options.shiftKey, options.metaKey, options.button,
  468. options.relatedTarget,options.button);
  469. },
  470. /**
  471. * Simulates a click on a particular element.
  472. *
  473. * @param {HTMLElement}
  474. * target The element to click on.
  475. * @param {Object}
  476. * options Additional event options (use DOM standard names).
  477. * @method click
  478. * @static
  479. */
  480. click:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  481. this.fireMouseEvent(target, "click", options);
  482. },
  483. /**
  484. * Simulates a double click on a particular element.
  485. *
  486. * @param {HTMLElement}
  487. * target The element to double click on.
  488. * @param {Object}
  489. * options Additional event options (use DOM standard names).
  490. * @method dblclick
  491. * @static
  492. */
  493. dblclick:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  494. this.fireMouseEvent(target, "dblclick", options);
  495. },
  496. /**
  497. * Simulates a mousedown on a particular element.
  498. *
  499. * @param {HTMLElement}
  500. * target The element to act on.
  501. * @param {Object}
  502. * options Additional event options (use DOM standard names).
  503. * @method mousedown
  504. * @static
  505. */
  506. mousedown:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  507. this.fireMouseEvent(target, "mousedown", options);
  508. },
  509. /**
  510. * Simulates a mousemove on a particular element.
  511. *
  512. * @param {HTMLElement}
  513. * target The element to act on.
  514. * @param {Object}
  515. * options Additional event options (use DOM standard names).
  516. * @method mousemove
  517. * @static
  518. */
  519. mousemove:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  520. this.fireMouseEvent(target, "mousemove", options);
  521. },
  522. /**
  523. * Simulates a mouseout event on a particular element. Use "relatedTarget"
  524. * on the options object to specify where the mouse moved to. Quirks:
  525. * Firefox less than 2.0 doesn't set relatedTarget properly, so toElement is
  526. * assigned in its place. IE doesn't allow toElement to be be assigned, so
  527. * relatedTarget is assigned in its place. Both of these concessions allow
  528. * YAHOO.util.Event.getRelatedTarget() to work correctly in both browsers.
  529. *
  530. * @param {HTMLElement}
  531. * target The element to act on.
  532. * @param {Object}
  533. * options Additional event options (use DOM standard names).
  534. * @method mouseout
  535. * @static
  536. */
  537. mouseout:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  538. this.fireMouseEvent(target, "mouseout", options);
  539. },
  540. /**
  541. * Simulates a mouseover event on a particular element. Use "relatedTarget"
  542. * on the options object to specify where the mouse moved from. Quirks:
  543. * Firefox less than 2.0 doesn't set relatedTarget properly, so fromElement
  544. * is assigned in its place. IE doesn't allow fromElement to be be assigned,
  545. * so relatedTarget is assigned in its place. Both of these concessions
  546. * allow YAHOO.util.Event.getRelatedTarget() to work correctly in both
  547. * browsers.
  548. *
  549. * @param {HTMLElement}
  550. * target The element to act on.
  551. * @param {Object}
  552. * options Additional event options (use DOM standard names).
  553. * @method mouseover
  554. * @static
  555. */
  556. mouseover:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  557. this.fireMouseEvent(target, "mouseover", options);
  558. },
  559. /**
  560. * Simulates a mouseup on a particular element.
  561. *
  562. * @param {HTMLElement}
  563. * target The element to act on.
  564. * @param {Object}
  565. * options Additional event options (use DOM standard names).
  566. * @method mouseup
  567. * @static
  568. */
  569. mouseup:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  570. this.fireMouseEvent(target, "mouseup", options);
  571. },
  572. mouseenter:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  573. this.fireMouseEvent(target, "mouseenter", options);
  574. },
  575. mouseleave:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  576. this.fireMouseEvent(target, "mouseleave", options);
  577. },
  578. /**
  579. * Simulates a contextmenu on a particular element.
  580. *
  581. * @param {HTMLElement}
  582. * target The element to show contextmenu.
  583. * @param {Object}
  584. * options Additional event options (use DOM standard names).
  585. * @method contextmenu
  586. * @static
  587. */
  588. contextmenu:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  589. this.fireMouseEvent(target, "contextmenu", options);
  590. },
  591. /**
  592. * Simulates a dragend on a particular element.
  593. *
  594. * @param {HTMLElement}
  595. * target The element to show dragend.
  596. * @param {Object}
  597. * options Additional event options (use DOM standard names).
  598. * @method dragend
  599. * @static
  600. */
  601. dragend:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  602. this.fireMouseEvent(target, "dragend", options);
  603. },
  604. /**
  605. * Simulates a blur on a particular element.
  606. *
  607. * @param {HTMLElement}
  608. * target The element to show blur.
  609. * @param {Object}
  610. * options Additional event options (use DOM standard names).
  611. * @method blur
  612. * @static
  613. */
  614. blur:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  615. this.fireMouseEvent(target, "blur", options);
  616. },
  617. dragto:function (target, options) {
  618. var me = this;
  619. me.mousemove(target, {
  620. clientX:options.startX,
  621. clientY:options.startY
  622. });
  623. setTimeout(function () {
  624. me.mousedown(target, {
  625. clientX:options.startX,
  626. clientY:options.startY
  627. });
  628. setTimeout(function () {
  629. me.mousemove(target, {
  630. clientX:options.endX,
  631. clientY:options.endY
  632. });
  633. setTimeout(function () {
  634. me.mouseup(target, {
  635. clientX:options.endX,
  636. clientY:options.endY
  637. });
  638. if (options.callback)
  639. options.callback();
  640. }, options.aftermove || 20);
  641. }, options.beforemove || 20);
  642. }, options.beforestart || 50);
  643. },
  644. // --------------------------------------------------------------------------
  645. // Key events
  646. // --------------------------------------------------------------------------
  647. /**
  648. * Fires an event that normally would be fired by the keyboard (keyup,
  649. * keydown, keypress). Make sure to specify either keyCode or charCode as an
  650. * option.
  651. *
  652. * @private
  653. * @param {String}
  654. * type The type of event ("keyup", "keydown" or "keypress").
  655. * @param {HTMLElement}
  656. * target The target of the event.
  657. * @param {Object}
  658. * options Options for the event. Either keyCode or charCode are
  659. * required.
  660. * @method fireKeyEvent
  661. * @static
  662. */
  663. fireKeyEvent:function (type /* :String */, target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  664. options = options || {};
  665. this.simulateKeyEvent(target, type, options.bubbles,
  666. options.cancelable, options.view, options.ctrlKey,
  667. options.altKey, options.shiftKey, options.metaKey,
  668. options.keyCode, options.charCode);
  669. },
  670. /**
  671. * Simulates a cut event on a particular element.
  672. *
  673. * @param {HTMLElement}
  674. * target The element to act on.
  675. * @param {Object}
  676. * options Additional event options (use DOM standard names).
  677. * @method cut
  678. * @static
  679. */
  680. cut:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  681. this.fireKeyEvent("cut", target, options);
  682. },
  683. /**
  684. * Simulates a paste event on a particular element.
  685. *
  686. * @param {HTMLElement}
  687. * target The element to act on.
  688. * @param {Object}
  689. * options Additional event options (use DOM standard names).
  690. * @method paste
  691. * @static
  692. */
  693. paste:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ {
  694. this.fireKeyEvent( "paste", target, options );
  695. },
  696. /**
  697. * Simulates a keydown event on a particular element.
  698. *
  699. * @param {HTMLElement}
  700. * target The element to act on.
  701. * @param {Object}
  702. * options Additional event options (use DOM standard names).
  703. * @method keydown
  704. * @static
  705. */
  706. keydown:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  707. this.fireKeyEvent("keydown", target, options);
  708. },
  709. /**
  710. * Simulates a keypress on a particular element.
  711. *
  712. * @param {HTMLElement}
  713. * target The element to act on.
  714. * @param {Object}
  715. * options Additional event options (use DOM standard names).
  716. * @method keypress
  717. * @static
  718. */
  719. keypress:function (target /* :HTMLElement */, options /* :Object */) /* :Void */ {
  720. this.fireKeyEvent("keypress", target, options);
  721. },
  722. /**
  723. * Simulates a keyup event on a particular element.
  724. *
  725. * @param {HTMLElement}
  726. * target The element to act on.
  727. * @param {Object}
  728. * options Additional event options (use DOM standard names).
  729. * @method keyup
  730. * @static
  731. */
  732. keyup:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  733. this.fireKeyEvent("keyup", target, options);
  734. },
  735. /**
  736. * Simulates a compositionstart event on a particular element.
  737. *
  738. * @param {HTMLElement}
  739. * target The element to act on.
  740. * @param {Object}
  741. * options Additional event options (use DOM standard names).
  742. * @method compositionstart
  743. * @static
  744. */
  745. compositionstart:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  746. this.fireKeyEvent("compositionstart", target, options);
  747. },
  748. /**
  749. * Simulates a compositionstart event on a particular element.
  750. *
  751. * @param {HTMLElement}
  752. * target The element to act on.
  753. * @param {Object}
  754. * options Additional event options (use DOM standard names).
  755. * @method compositionstart
  756. * @static
  757. */
  758. compositionend:function (target /* :HTMLElement */, options /* Object */) /* :Void */ {
  759. this.fireKeyEvent("compositionend", target, options);
  760. },
  761. /**
  762. * 提供iframe扩展支持,用例测试需要独立场景的用例,由于异步支持,通过finish方法触发start
  763. * <li>事件绑定在frame上,包括afterfinish和jsloaded
  764. *
  765. * @param op.win
  766. * @param op.nojs
  767. * 不加载额外js
  768. * @param op.ontest
  769. * 测试步骤
  770. * @param op.onbeforestart
  771. * 测试启动前处理步骤,默认为QUnit.stop();
  772. * @param op.onafterfinish
  773. * 测试完毕执行步骤,默认为QUnit.start()
  774. *
  775. */
  776. frameExt:function (op) {
  777. stop();
  778. op = typeof op == 'function' ? {
  779. ontest:op
  780. } : op;
  781. var pw = op.win || window, w, f, url = '', id = typeof op.id == 'undefined' ? 'f'
  782. : op.id, fid = 'iframe#' + id;
  783. op.finish = function () {
  784. pw.$(fid).unbind();
  785. setTimeout(function () {
  786. pw.$('div#div' + id).remove();
  787. start();
  788. }, 20);
  789. };
  790. if (pw.$(fid).length == 0) {
  791. /* 添加frame,部分情况下,iframe没有边框,为了可以看到效果,添加一个带边框的div */
  792. pw.$(pw.document.body).append('<div id="div' + id + '"></div>');
  793. pw.$('div#div' + id).append('<iframe id="' + id + '"></iframe>');
  794. }
  795. op.onafterstart && op.onafterstart($('iframe#f')[0]);
  796. pw.$('script').each(function () {
  797. if (this.src && this.src.indexOf('import.php') >= 0) {
  798. url = this.src.split('import.php')[1];
  799. }
  800. });
  801. pw.$(fid).one('load',
  802. function (e) {
  803. var w = e.target.contentWindow;
  804. var h = setInterval(function () {
  805. if (w.baidu) {// 等待加载完成,IE6下这地方总出问题
  806. clearInterval(h);
  807. op.ontest(w, w.frameElement);
  808. }
  809. }, 20);
  810. // 找到当前操作的iframe,然后call ontest
  811. }).attr('src', cpath + 'frame.php' + url);
  812. },
  813. /**
  814. *
  815. * 判断2个数组是否相等
  816. *
  817. * @static
  818. */
  819. isEqualArray:function (array1, array2) {
  820. if ('[object Array]' != Object.prototype.toString.call(array1)
  821. || '[object Array]' != Object.prototype.toString.call(array2))
  822. return (array1 === array2);
  823. else if (array1.length != array2.length)
  824. return false;
  825. else {
  826. for (var i in array1) {
  827. if (array1[i] != array2[i])
  828. return false;
  829. }
  830. return true;
  831. }
  832. },
  833. /***************************************************************************
  834. *
  835. * 通用数据模块
  836. *
  837. * @static
  838. *
  839. **************************************************************************/
  840. commonData:{// 针对测试文件的路径而不是UserAction的路径
  841. "testdir":'../../',
  842. datadir:(function () {
  843. return location.href.split("/_test/")[0] + "/_test/tools/data/";
  844. })(),
  845. currentPath:function () {
  846. var params = location.search.substring(1).split('&');
  847. for (var i = 0; i < params.length; i++) {
  848. var p = params[i];
  849. if (p.split('=')[0] == 'case') {
  850. var casepath = p.split('=')[1].split('.').join('/');
  851. return location.href.split('/_test/')[0] + '/_test/'
  852. + casepath.substring(0, casepath.lastIndexOf('/'))
  853. + '/';
  854. }
  855. }
  856. return "";
  857. }
  858. },
  859. importsrc:function (src, callback, matcher, exclude, win) {
  860. win = win || window;
  861. var doc = win.document;
  862. var srcpath = location.href.split("/_test/")[0]
  863. + "/_test/tools/br/import.php";
  864. var param0 = src;
  865. var ps = {
  866. f:src
  867. };
  868. if (exclude)
  869. ps.e = exclude;
  870. var param1 = exclude || "";
  871. /**
  872. * IE下重复载入会出现无法执行情况
  873. */
  874. if (win.execScript) {
  875. $.get(srcpath, ps, function (data) {
  876. win.execScript(data);
  877. });
  878. } else {
  879. var head = doc.getElementsByTagName('head')[0];
  880. var sc = doc.createElement('script');
  881. sc.type = 'text/javascript';
  882. sc.src = srcpath + "?f=" + param0 + "&e=" + param1;
  883. head.appendChild(sc);
  884. }
  885. matcher = matcher || src;
  886. var mm = matcher.split(",")[0].split(".");
  887. var h = setInterval(function () {
  888. var p = win;
  889. for (var i = 0; i < mm.length; i++) {
  890. if (typeof (p[mm[i]]) == 'undefined') {
  891. // console.log(mm[i]);
  892. return;
  893. }
  894. p = p[mm[i]];
  895. }
  896. clearInterval(h);
  897. if (callback && 'function' == typeof callback)
  898. callback();
  899. }, 20);
  900. },
  901. /* 用于加载css文件,如果没有加载完毕则不执行回调函数 */
  902. loadcss:function (url, callback, classname, style, value) {
  903. var links = document.getElementsByTagName('link');
  904. for (var link in links) {
  905. if (link.href == url) {
  906. callback();
  907. return;
  908. }
  909. }
  910. var head = document.getElementsByTagName('head')[0];
  911. var link = head.appendChild(document.createElement('link'));
  912. link.setAttribute("rel", "stylesheet");
  913. link.setAttribute("type", "text/css");
  914. link.setAttribute("href", url);
  915. var div = document.body.appendChild(document.createElement("div"));
  916. $(document).ready(
  917. function () {
  918. div.className = classname || 'cssloaded';
  919. var h = setInterval(function () {
  920. if ($(div).css(style || 'width') == value
  921. || $(div).css(style || 'width') == '20px') {
  922. clearInterval(h);
  923. document.body.removeChild(div);
  924. setTimeout(callback, 20);
  925. }
  926. }, 20);
  927. });
  928. },
  929. /**
  930. * options supported
  931. */
  932. delayhelper:function (oncheck, onsuccess, onfail, timeout) {
  933. onsuccess = onsuccess || oncheck.onsuccess;
  934. onfail = onfail || oncheck.onfail || function () {
  935. window.QUnit.fail('timeout wait for timeout : ' + timeout + 'ms');
  936. start();
  937. };
  938. timeout = timeout || oncheck.timeout || 10000;
  939. oncheck = (typeof oncheck == 'function') ? oncheck : oncheck.oncheck;
  940. var h1 = setInterval(function () {
  941. if (!oncheck())
  942. return;
  943. else {
  944. clearInterval(h1);
  945. clearTimeout(h2);
  946. typeof onsuccess == "function" && onsuccess();
  947. }
  948. }, 20);
  949. var h2 = setTimeout(function () {
  950. clearInterval(h1);
  951. clearTimeout(h2);
  952. onfail();
  953. }, timeout);
  954. },
  955. browser:(function () {
  956. var win = window;
  957. var numberify = function (s) {
  958. var c = 0;
  959. return parseFloat(s.replace(/\./g, function () {
  960. return (c++ == 1) ? '' : '.';
  961. }));
  962. },
  963. nav = win && win.navigator,
  964. o = {
  965. /**
  966. * Internet Explorer version number or 0. Example: 6
  967. *
  968. * @property ie
  969. * @type float
  970. * @static
  971. */
  972. ie:0,
  973. /**
  974. * Opera version number or 0. Example: 9.2
  975. *
  976. * @property opera
  977. * @type float
  978. * @static
  979. */
  980. opera:0,
  981. /**
  982. * Gecko engine revision number. Will evaluate to 1 if Gecko is
  983. * detected but the revision could not be found. Other browsers will
  984. * be 0. Example: 1.8
  985. *
  986. * <pre>
  987. * Firefox 1.0.0.4: 1.7.8 &lt;-- Reports 1.7
  988. * Firefox 1.5.0.9: 1.8.0.9 &lt;-- 1.8
  989. * Firefox 2.0.0.3: 1.8.1.3 &lt;-- 1.81
  990. * Firefox 3.0 &lt;-- 1.9
  991. * Firefox 3.5 &lt;-- 1.91
  992. * </pre>
  993. *
  994. * @property gecko
  995. * @type float
  996. * @static
  997. */
  998. gecko:0,
  999. /**
  1000. * AppleWebKit version. KHTML browsers that are not WebKit browsers
  1001. * will evaluate to 1, other browsers 0. Example: 418.9
  1002. *
  1003. * <pre>
  1004. * Safari 1.3.2 (312.6): 312.8.1 &lt;-- Reports 312.8 -- currently the
  1005. * latest available for Mac OSX 10.3.
  1006. * Safari 2.0.2: 416 &lt;-- hasOwnProperty introduced
  1007. * Safari 2.0.4: 418 &lt;-- preventDefault fixed
  1008. * Safari 2.0.4 (419.3): 418.9.1 &lt;-- One version of Safari may run
  1009. * different versions of webkit
  1010. * Safari 2.0.4 (419.3): 419 &lt;-- Tiger installations that have been
  1011. * updated, but not updated
  1012. * to the latest patch.
  1013. * Webkit 212 nightly: 522+ &lt;-- Safari 3.0 precursor (with native SVG
  1014. * and many major issues fixed).
  1015. * Safari 3.0.4 (523.12) 523.12 &lt;-- First Tiger release - automatic update
  1016. * from 2.x via the 10.4.11 OS patch
  1017. * Webkit nightly 1/2008:525+ &lt;-- Supports DOMContentLoaded event.
  1018. * yahoo.com user agent hack removed.
  1019. * </pre>
  1020. *
  1021. * http://en.wikipedia.org/wiki/Safari_version_history
  1022. *
  1023. * @property webkit
  1024. * @type float
  1025. * @static
  1026. */
  1027. webkit:0,
  1028. /**
  1029. * Chrome will be detected as webkit, but this property will also be
  1030. * populated with the Chrome version number
  1031. *
  1032. * @property chrome
  1033. * @type float
  1034. * @static
  1035. */
  1036. chrome:0,
  1037. safari:0,
  1038. firefox:0,
  1039. maxthon:0,
  1040. maxthonIE:0,
  1041. /**
  1042. * The mobile property will be set to a string containing any
  1043. * relevant user agent information when a modern mobile browser is
  1044. * detected. Currently limited to Safari on the iPhone/iPod Touch,
  1045. * Nokia N-series devices with the WebKit-based browser, and Opera
  1046. * Mini.
  1047. *
  1048. * @property mobile
  1049. * @type string
  1050. * @static
  1051. */
  1052. mobile:null,
  1053. /**
  1054. * Adobe AIR version number or 0. Only populated if webkit is
  1055. * detected. Example: 1.0
  1056. *
  1057. * @property air
  1058. * @type float
  1059. */
  1060. air:0,
  1061. /**
  1062. * Google Caja version number or 0.
  1063. *
  1064. * @property caja
  1065. * @type float
  1066. */
  1067. caja:nav && nav.cajaVersion,
  1068. /**
  1069. * Set to true if the pagebreak appears to be in SSL
  1070. *
  1071. * @property secure
  1072. * @type boolean
  1073. * @static
  1074. */
  1075. secure:false,
  1076. /**
  1077. * The operating system. Currently only detecting windows or
  1078. * macintosh
  1079. *
  1080. * @property os
  1081. * @type string
  1082. * @static
  1083. */
  1084. os:null
  1085. },
  1086. ua = nav && nav.userAgent,
  1087. loc = win && win.location,
  1088. href = loc && loc.href,
  1089. m;
  1090. o.secure = href && (href.toLowerCase().indexOf("https") === 0);
  1091. if (ua) {
  1092. if ((/windows|win32/i).test(ua)) {
  1093. o.os = 'windows';
  1094. } else if ((/macintosh/i).test(ua)) {
  1095. o.os = 'macintosh';
  1096. } else if ((/rhino/i).test(ua)) {
  1097. o.os = 'rhino';
  1098. }
  1099. // Modern KHTML browsers should qualify as Safari X-Grade
  1100. if ((/KHTML/).test(ua)) {
  1101. o.webkit = 1;
  1102. }
  1103. if (window.external && /(\d+\.\d)/.test(external.max_version)) {
  1104. o.maxthon = parseFloat(RegExp['\x241']);
  1105. if (/MSIE/.test(ua)) {
  1106. o.maxthonIE = 1;
  1107. o.maxthon = 0;
  1108. }
  1109. }
  1110. // Modern WebKit browsers are at least X-Grade
  1111. m = ua.match(/AppleWebKit\/([^\s]*)/);
  1112. if (m && m[1]) {
  1113. o.webkit = numberify(m[1]);
  1114. // Mobile browser check
  1115. if (/ Mobile\//.test(ua)) {
  1116. o.mobile = "Apple"; // iPhone or iPod Touch
  1117. } else {
  1118. m = ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
  1119. if (m) {
  1120. o.mobile = m[0]; // Nokia N-series, Android, webOS,
  1121. // ex:
  1122. // NokiaN95
  1123. }
  1124. }
  1125. var m1 = ua.match(/Safari\/([^\s]*)/);
  1126. if (m1 && m1[1]) // Safari
  1127. o.safari = numberify(m1[1]);
  1128. m = ua.match(/Chrome\/([^\s]*)/);
  1129. if (o.safari && m && m[1]) {
  1130. o.chrome = numberify(m[1]); // Chrome
  1131. } else {
  1132. m = ua.match(/AdobeAIR\/([^\s]*)/);
  1133. if (m) {
  1134. o.air = m[0]; // Adobe AIR 1.0 or better
  1135. }
  1136. }
  1137. }
  1138. if (!o.webkit) { // not webkit
  1139. // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316;
  1140. // fi; U;
  1141. // try get firefox and it's ver
  1142. // ssr)
  1143. m = ua.match(/Opera[\s\/]([^\s]*)/);
  1144. if (m && m[1]) {
  1145. m = ua.match(/Version[\s\/]([^\s]*)/);
  1146. o.opera = numberify(m[1]);
  1147. m = ua.match(/Opera Mini[^;]*/);
  1148. if (m) {
  1149. o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
  1150. }
  1151. } else { // not opera or webkit
  1152. m = ua.match(/MSIE\s([^;]*)/);
  1153. if (m && m[1]) {
  1154. o.ie = numberify(m[1]);
  1155. }else if (ua.match(/Gecko([^\s]*)/)&&ua.match(/rv:11/)){//todo
  1156. o.ie = 11;
  1157. } else { // not opera, webkit, or ie
  1158. m = ua.match(/Gecko\/([^\s]*)/);
  1159. if (m) {
  1160. o.gecko = 1; // Gecko detected, look for revision
  1161. m = ua.match(/rv:([^\s\)]*)/);
  1162. if (m && m[1]) {
  1163. o.gecko = numberify(m[1]);
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. }
  1170. return o;
  1171. })
  1172. (),
  1173. /**
  1174. * 提供队列方式执行用例的方案,接口包括start、add、next,方法全部执行完毕时会启动用例继续执行
  1175. */
  1176. functionListHelper:function () {
  1177. var check = {
  1178. list:[],
  1179. start:function () {
  1180. var self = this;
  1181. $(this).bind('next', function () {
  1182. setTimeout(function () {// 避免太深的堆栈
  1183. if (self.list.length == 0)
  1184. start();
  1185. else
  1186. self.list.shift()();
  1187. }, 0);
  1188. });
  1189. self.next();
  1190. },
  1191. add:function (func) {
  1192. this.list.push(func);
  1193. },
  1194. next:function (delay) {
  1195. var self = this;
  1196. if (delay) {
  1197. setTimeout(function () {
  1198. $(self).trigger('next');
  1199. }, delay);
  1200. } else
  1201. $(this).trigger('next');
  1202. }
  1203. };
  1204. return check;
  1205. },
  1206. getHTML:function (co) {
  1207. var div = document.createElement('div'), h;
  1208. if (!co)
  1209. return 'null';
  1210. div.appendChild(co.cloneNode(true));
  1211. h = div.innerHTML.toLowerCase();
  1212. h = h.replace(/[\r\n\t\u200b\ufeff]/g, ''); // Remove line feeds and tabs
  1213. h = h.replace(/ (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"'); // Restore
  1214. // attribs on IE
  1215. return h;
  1216. },
  1217. getChildHTML:function (co) {
  1218. var h = co.innerHTML.toLowerCase();
  1219. h = h.replace(/[\r\n\t\u200b\ufeff]/g, ''); // Remove line feeds and tabs
  1220. h = h.replace(/ (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"'); // Restore attribs on IE
  1221. return h.replace(/\u200B/g, '');
  1222. },
  1223. getIndex:function (node) {
  1224. var childNodes = node.parentNode.childNodes, i = 0;
  1225. while (childNodes[i] !== node)
  1226. i++;
  1227. return i;
  1228. },
  1229. checkResult:function (range, sc, ec, so, eo, collapsed, descript) {
  1230. descript = descript ? descript : '';
  1231. equal(range.collapsed, collapsed, "check collapsed --" + descript);
  1232. ok(range.startContainer === sc, "check startContainer--" + descript);
  1233. ok(range.endContainer === ec, "check endContainer--" + descript);
  1234. equal(range.startOffset, so, "check startOffset--" + descript);
  1235. equal(range.endOffset, eo, "check endOffset--" + descript);
  1236. },
  1237. isSameRange:function (rangeA, rangeB, descript) {
  1238. descript = descript ? descript : '';
  1239. equal(rangeA.collapsed, rangeB.collapsed, "check collapsed --" + descript);
  1240. ok(rangeA.document === rangeB.document, "check document--" + descript);
  1241. ok(rangeA.startContainer === rangeB.startContainer, "check startContainer--" + descript);
  1242. ok(rangeA.endContainer === rangeB.endContainer, "check endContainer--" + descript);
  1243. equal(rangeA.startOffset, rangeB.startOffset, "check startOffset--" + descript);
  1244. equal(rangeA.endOffset, rangeB.endOffset, "check endOffset--" + descript);
  1245. },
  1246. manualDeleteFillData:function (node) {
  1247. var childs = node.childNodes;
  1248. for (var i = 0; i < childs.length; i++) {
  1249. var fillData = childs[i];
  1250. if ((fillData.nodeType == 3) && ( fillData.data == domUtils.fillChar )) {
  1251. domUtils.remove(fillData);
  1252. fillData = null;
  1253. }
  1254. else
  1255. this.manualDeleteFillData(fillData);
  1256. }
  1257. },
  1258. cssStyleToDomStyle:function (cssName) {
  1259. var test = document.createElement('div').style,
  1260. cssFloat = test.cssFloat != undefined ? 'cssFloat'
  1261. : test.styleFloat != undefined ? 'styleFloat'
  1262. : 'float',
  1263. cache = { 'float':cssFloat };
  1264. function replacer(match) {
  1265. return match.charAt(1).toUpperCase();
  1266. }
  1267. // return function( cssName ) {
  1268. return cache[cssName] || (cache[cssName] = cssName.replace(/-./g, replacer) );
  1269. // };
  1270. },
  1271. isSameStyle:function (elementA, elementB) {
  1272. // var styleA = elementA.style.cssText,
  1273. // styleB = elementB.style.cssText;
  1274. // if ( this.browser.ie && this.browser.version == 6 ) {
  1275. // styleA = styleA.toLowerCase();
  1276. // styleB = styleB.toLowerCase();
  1277. // }
  1278. // if ( !styleA && !styleB ) {
  1279. // return true;
  1280. // } else if ( !styleA || !styleB ) {
  1281. // return false;
  1282. // }
  1283. // var styleNameMap = {},
  1284. // record = [],
  1285. // exit = {};
  1286. // styleA.replace( /[\w-]+\s*(?=:)/g, function ( name ) {
  1287. // styleNameMap[name] = record.push( name );
  1288. // } );
  1289. // try {
  1290. // styleB.replace( /[\w-]+\s*(?=:)/g, function ( name ) {
  1291. // var index = styleNameMap[name];
  1292. // if ( index ) {
  1293. //// name = this.cssStyleToDomStyle( name );
  1294. // if ( elementA.style[name] !== elementB.style[name] ) {
  1295. // throw exit;
  1296. // }
  1297. // record[index - 1] = '';
  1298. // } else {
  1299. // throw exit;
  1300. // }
  1301. // } );
  1302. // } catch ( ex ) {
  1303. // if ( ex === exit ) {
  1304. // return false;
  1305. // }
  1306. // }
  1307. // return !record.join( '' );
  1308. function indexOf(array, item, at) {
  1309. for (var i = at || 0, l = array.length; i < l; i++) {
  1310. if (array[i] === item) {
  1311. return i;
  1312. }
  1313. }
  1314. return -1;
  1315. }
  1316. var styleA = elementA.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':'),
  1317. styleB = elementB.style.cssText.replace(/( ?; ?)/g, ';').replace(/( ?: ?)/g, ':');
  1318. if (browser.opera) {
  1319. styleA = elementA.style;
  1320. styleB = elementB.style;
  1321. if (styleA.length != styleB.length)
  1322. return 0;
  1323. for (var p in styleA) {
  1324. if (/^(\d+|csstext)$/i.test(p))
  1325. continue;
  1326. if (styleA[p] != styleB[p])
  1327. return 0;
  1328. }
  1329. return 1;
  1330. }
  1331. if (!styleA || !styleB) {
  1332. return styleA == styleB ? 1 : 0;
  1333. }
  1334. styleA = styleA.split(';');
  1335. styleB = styleB.split(';');
  1336. if (styleA.length != styleB.length)
  1337. return 0;
  1338. for (var j = 0; j < styleB.length; j++) {
  1339. if (styleB[j].toLowerCase().indexOf("color") > -1 && styleB[j].toLowerCase().indexOf("rgb") > -1) {
  1340. var color = this.formatColor(styleB[j].substr(styleB[j].indexOf("rgb"), styleB[j].length));
  1341. styleB[j] = styleB[j].replace(styleB[j].substr(styleB[j].indexOf("rgb"), styleB[j].length), color);
  1342. }
  1343. }
  1344. for (var i = 0, ci; ci = styleA[i++];) {
  1345. if (ci.toLowerCase().indexOf("color") > -1 && ci.toLowerCase().indexOf("rgb") > -1) {
  1346. var color = this.formatColor(ci.substr(ci.indexOf("rgb"), ci.length));
  1347. ci = ci.replace(ci.substr(ci.indexOf("rgb"), ci.length), color);
  1348. }
  1349. if (indexOf(styleB, ci) == -1) {
  1350. return 0;
  1351. }//styleA[0].substr(styleA[0].indexOf("rga"),styleA[0].length)
  1352. }
  1353. return 1;
  1354. },
  1355. formatColor:function (color) {
  1356. var reg1 = /^\#[\da-f]{6}$/i,
  1357. reg2 = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/,
  1358. keyword = {
  1359. black:'#000000',
  1360. silver:'#c0c0c0',
  1361. gray:'#808080',
  1362. white:'#ffffff',
  1363. maroon:'#800000',
  1364. red:'#ff0000',
  1365. purple:'#800080',
  1366. fuchsia:'#ff00ff',
  1367. green:'#008000',
  1368. lime:'#00ff00',
  1369. olive:'#808000',
  1370. yellow:'#ffff0',
  1371. navy:'#000080',
  1372. blue:'#0000ff',
  1373. teal:'#008080',
  1374. aqua:'#00ffff'
  1375. };
  1376. if (reg1.test(color)) {
  1377. // #RRGGBB 直接返回
  1378. return color;
  1379. } else if (reg2.test(color)) {
  1380. // 非IE中的 rgb(0, 0, 0)
  1381. for (var s, i = 1, color = "#"; i < 4; i++) {
  1382. s = parseInt(RegExp["\x24" + i]).toString(16);
  1383. color += ("00" + s).substr(s.length);
  1384. }
  1385. return color;
  1386. } else if (/^\#[\da-f]{3}$/.test(color)) {
  1387. // 简写的颜色值: #F00
  1388. var s1 = color.charAt(1),
  1389. s2 = color.charAt(2),
  1390. s3 = color.charAt(3);
  1391. return "#" + s1 + s1 + s2 + s2 + s3 + s3;
  1392. } else if (keyword[color])
  1393. return keyword[color];
  1394. return "";
  1395. },
  1396. hasSameAttrs:function (nodeA, nodeB) {
  1397. if (nodeA.tagName != nodeB.tagName)
  1398. return 0;
  1399. var thisAttribs = nodeA.attributes,
  1400. otherAttribs = nodeB.attributes;
  1401. if (thisAttribs.length != otherAttribs.length)
  1402. return 0;
  1403. if (thisAttribs.length == 0)
  1404. return 1;
  1405. var attrA, attrB;
  1406. for (var i = 0; attrA = thisAttribs[i++];) {
  1407. if (attrA.nodeName == 'style') {
  1408. if (this.isSameStyle(nodeA, nodeB)) {
  1409. continue
  1410. } else {
  1411. return 0;
  1412. }
  1413. }
  1414. if (!ua.browser.ie || attrA.specified) {
  1415. attrB = nodeB.attributes[attrA.nodeName];
  1416. if (!attrB) {
  1417. return 0;
  1418. }
  1419. }
  1420. return 1;
  1421. }
  1422. return 1;
  1423. },
  1424. /**
  1425. *清除空Text节点
  1426. */
  1427. clearWhiteNode:function (node) {
  1428. for (var i = 0; i < node.childNodes.length; i++) {
  1429. var tmpNode = node.childNodes[i];
  1430. if (tmpNode.nodeType == 3 && !tmpNode.length) {
  1431. tmpNode.parentNode.removeChild(tmpNode);
  1432. i--;
  1433. }
  1434. }
  1435. },
  1436. /**
  1437. *检查两个节点(包含所有子节点)是否具有相同的属性
  1438. */
  1439. flag:true,
  1440. checkAllChildAttribs:function (nodeA, nodeB) {
  1441. var k = nodeA.childNodes.length;
  1442. if (k != nodeB.childNodes.length) {
  1443. if (ua.browser.opera) {
  1444. this.clearWhiteNode(nodeA);
  1445. k = nodeA.childNodes.length;
  1446. if (k != nodeB.childNodes.length)
  1447. this.flag = false;
  1448. }
  1449. else
  1450. this.flag = false;
  1451. }
  1452. if (!this.flag)
  1453. return this.flag;
  1454. while (k) {
  1455. var tmpNodeA = nodeA.childNodes[k - 1];
  1456. var tmpNodeB = nodeB.childNodes[k - 1];
  1457. k--;
  1458. if (tmpNodeA.nodeType == 3 || tmpNodeB.nodeType == 3 || tmpNodeA.nodeType == 8 || tmpNodeB.nodeType == 8)
  1459. continue;
  1460. if (!this.hasSameAttrs(tmpNodeA, tmpNodeB)) {
  1461. this.flag = false;
  1462. break;
  1463. }
  1464. this.checkAllChildAttribs(tmpNodeA, tmpNodeB);
  1465. }
  1466. return this.flag;
  1467. },
  1468. haveSameAllChildAttribs:function (nodeA, nodeB) {
  1469. this.flag = true;
  1470. return this.checkAllChildAttribs(nodeA, nodeB);
  1471. },
  1472. /*查看传入的html是否与传入的元素ele具有相同的style*/
  1473. checkHTMLSameStyle:function (html, doc, ele, descript) {
  1474. var tagEle = doc.createElement(ele.tagName);
  1475. tagEle.innerHTML = html;
  1476. /*会有一些不可见字符,在比较前提前删掉*/
  1477. this.manualDeleteFillData(ele);
  1478. ok(this.haveSameAllChildAttribs(ele, tagEle), descript);
  1479. // ok(this.equalsNode(ele.innerHMTL,html),descript);
  1480. },
  1481. equalsNode:function (na, nb) {
  1482. function compare(nodeA, nodeB) {
  1483. if (nodeA.nodeType != nodeB.nodeType) {
  1484. return 0;
  1485. }
  1486. if (nodeA.nodeType == 3) {
  1487. return nodeA.nodeValue == nodeB.nodeValue
  1488. }
  1489. if (domUtils.isSameElement(nodeA, nodeB)) {
  1490. if (!nodeA.firstChild && !nodeB.firstChild) {
  1491. return 1;
  1492. }
  1493. if (nodeA.firstChild && !nodeB.firstChild || !nodeA.firstChild && nodeB.firstChild) {
  1494. return 0
  1495. }
  1496. for (var i = 0, ai, bi; ai = nodeA.childNodes[i], bi = nodeB.childNodes[i++];) {
  1497. if (!compare(ai, bi)) {
  1498. return 0
  1499. }
  1500. }
  1501. return 1;
  1502. } else {
  1503. return 0;
  1504. }
  1505. }
  1506. return compare(domUtils.createElement(document, 'div', {
  1507. 'innerHTML':na
  1508. }), domUtils.createElement(document, 'div', {
  1509. 'innerHTML':nb
  1510. }));
  1511. },
  1512. getSelectedText:function () {
  1513. if (window.getSelection) {
  1514. // This technique is the most likely to be standardized.
  1515. // getSelection() returns a Selection object, which we do not document.
  1516. return window.getSelection().toString();
  1517. }
  1518. else if (document.getSelection) {
  1519. // This is an older, simpler technique that returns a string
  1520. return document.getSelection();
  1521. }
  1522. else if (document.selection) {
  1523. // This is the IE-specific technique.
  1524. // We do not document the IE selection property or TextRange objects.
  1525. return document.selection.createRange().text;
  1526. }
  1527. },
  1528. findPosition:function (oElement) {
  1529. var x2 = 0;
  1530. var y2 = 0;
  1531. var width = oElement.offsetWidth;
  1532. var height = oElement.offsetHeight;
  1533. if (typeof( oElement.offsetParent ) != 'undefined') {
  1534. for (var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent) {
  1535. posX += oElement.offsetLeft;
  1536. posY += oElement.offsetTop;
  1537. }
  1538. x2 = posX + width;
  1539. y2 = posY + height;
  1540. return [ posX, posY , x2, y2];
  1541. } else {
  1542. x2 = oElement.x + width;
  1543. y2 = oElement.y + height;
  1544. return [ oElement.x, oElement.y, x2, y2];
  1545. }
  1546. },
  1547. checkElementPath:function (arr1, arr2, descript) {
  1548. if (!descript)
  1549. descript = '';
  1550. var index = arr1.length;
  1551. if (index != arr2.length)
  1552. ok(false, '路径深度不相同');
  1553. else {
  1554. while (index > 0)
  1555. equal(arr1[--index ], arr2[index ], descript + '---第' + index + '个元素' + arr1[index]);
  1556. }
  1557. },
  1558. getBrowser:function () {
  1559. var browser = "";
  1560. if (this.browser.ie == 6)
  1561. browser = 'ie6';
  1562. if (this.browser.ie == 7)
  1563. browser = 'ie7';
  1564. if (this.browser.ie == 8)
  1565. browser = 'ie8';
  1566. if (this.browser.ie == 9)
  1567. browser = 'ie9';
  1568. if (this.browser.safari)
  1569. browser = 'safari';
  1570. if (this.browser.firefox)
  1571. browser = 'firefox';
  1572. if (this.browser.chrome)
  1573. browser = 'chrome';
  1574. if (this.browser.maxthon) {
  1575. browser = 'maxthon';
  1576. }
  1577. if (this.browser.maxthonIE)
  1578. browser = 'maxIE';
  1579. if (this.browser.opera)
  1580. browser = 'opera';
  1581. return browser;
  1582. },
  1583. getFloatStyle:function (ele) {
  1584. if (this.browser.ie)
  1585. return ele.style['styleFloat'];
  1586. else
  1587. return ele.style['cssFloat'];
  1588. },
  1589. getComputedStyle:function(ele ){
  1590. if(this.browser.ie&&ua.browser.ie<9){
  1591. return ele.currentStyle;
  1592. }else{
  1593. return window.getComputedStyle(ele);
  1594. }
  1595. },
  1596. readFile:function (name, f) {
  1597. var args = {};
  1598. args['name'] = name;
  1599. $.ajax({
  1600. url:'read.php',
  1601. type:'post',
  1602. data:args,
  1603. success:function (msg) {
  1604. f(msg);
  1605. },
  1606. error:function (xhr, msg) {
  1607. f(null);
  1608. }
  1609. });
  1610. },
  1611. readTxt:function (name, f) {
  1612. var args = {};
  1613. args['name'] = './txt/' + name;
  1614. $.ajax({
  1615. url:'read.php',
  1616. type:'post',
  1617. data:args,
  1618. success:function (msg) {
  1619. f(msg);
  1620. },
  1621. error:function (xhr, msg) {
  1622. f(null);
  1623. }
  1624. });
  1625. }, checkLowerCase:function (stringA, stringB) {
  1626. if (!(stringA || stringB))
  1627. return true;
  1628. else if (!stringA || !stringB)
  1629. return false;
  1630. else {
  1631. return stringA.toLowerCase() == stringB.toLowerCase();
  1632. }
  1633. }, removeEndSemicolon:function (styleValue) {
  1634. if (styleValue.length - 1 == styleValue.lastIndexOf(';'))
  1635. styleValue = styleValue.substring(0, styleValue.length - 1);
  1636. return styleValue;
  1637. }, checkNodeStyle:function (nodeA, nodeB) {
  1638. var nodeAStyle = this.removeEndSemicolon(nodeA.getAttr("style").replace(/\s+/g, "")).replace(/&quot;/g,'').split(";");
  1639. var nodeBStyle = this.removeEndSemicolon(nodeB.getAttr("style").replace(/\s+/g, "")).replace(/&quot;/g,'').split(";");
  1640. var lengthA = nodeAStyle.length;
  1641. var lengthB = nodeBStyle.length;
  1642. if (!(lengthA && lengthB))
  1643. return true;
  1644. else if (lengthA != lengthB)
  1645. return false;
  1646. else {
  1647. for (var i = 0; i < lengthA; i++) {
  1648. if (nodeAStyle[i].match(/[-\w]+\s*:/) ) {
  1649. var styleName = nodeAStyle[i].match(/[-\w]+\s*:/)[0].replace(/\s*:/, "");
  1650. nodeA.attrs.style = nodeA.attrs.style.replace(/&quot;/g,'');
  1651. nodeB.attrs.style = nodeB.attrs.style.replace(/&quot;/g,'');
  1652. var styleValueA = nodeA.getStyle(styleName).toLowerCase().replace(/\s+/g, "");
  1653. var styleValueB = nodeB.getStyle(styleName).toLowerCase().replace(/\s+/g, "");
  1654. if(/color/.test(styleName)){
  1655. styleValueA = this.formatColor(styleValueA);
  1656. styleValueB = this.formatColor(styleValueB);
  1657. }
  1658. else;
  1659. if (styleValueA != styleValueB)
  1660. return false;
  1661. }
  1662. }
  1663. }
  1664. return true;
  1665. }, getPropertyCount:function (o) {
  1666. var n, count = 0;
  1667. for (n in o) {
  1668. if (o.hasOwnProperty(n)) {
  1669. count++;
  1670. }
  1671. }
  1672. return count;
  1673. },formHref:function(str){
  1674. if(str.lastIndexOf('/')== str.length-1){
  1675. str = str.substring(0,str.length-1);
  1676. }
  1677. return str;
  1678. },checkSameNodeAttrs:function (nodeA, nodeB) {
  1679. var lengthA = this.getPropertyCount(nodeA.attrs);
  1680. var lengthB = this.getPropertyCount(nodeB.attrs);
  1681. if (!(lengthA && lengthB))
  1682. return true;
  1683. else if (lengthA != lengthB)
  1684. return false;
  1685. else {
  1686. for (var p in nodeA.attrs) {
  1687. if(!nodeB.getAttr(p)&&!nodeA.getAttr(p))
  1688. return true;
  1689. else if (!nodeB.getAttr(p)||!nodeA.getAttr(p))
  1690. return false;
  1691. else if (p.toLowerCase() == "style") {
  1692. if (!this.checkNodeStyle(nodeA, nodeB))
  1693. return false;
  1694. }
  1695. else if(p.toLowerCase() == "href"){
  1696. if (this.formHref(nodeA.getAttr(p).toLowerCase()) != this.formHref(nodeB.getAttr(p).toLowerCase()))
  1697. return false;
  1698. }
  1699. else {
  1700. if (nodeA.getAttr(p).toLowerCase().replace(/^\s+|\s+$/g, "") != nodeB.getAttr(p).toLowerCase().replace(/^\s+|\s+$/g, ""))
  1701. return false;
  1702. }
  1703. }
  1704. }
  1705. return true;
  1706. }, checkChildren:function (nodeA, nodeB) {
  1707. if (!(nodeA.children || nodeB.children))
  1708. return true;
  1709. else if (!(nodeA.children && nodeB.children))
  1710. return false;
  1711. else if (nodeA.children.length != nodeB.children.length)
  1712. return false;
  1713. else {
  1714. var lengthA = nodeA.children.length;
  1715. for (var i = 0; i < lengthA; i++) {
  1716. if (!this.checkSameNode(nodeA.children[i], nodeB.children[i]))
  1717. return false;
  1718. }
  1719. }
  1720. return true;
  1721. }, checkSameNode:function (nodeA, nodeB) {
  1722. if (!this.checkSameNodeAttrs(nodeA, nodeB))
  1723. return false;
  1724. else if (!this.checkChildren(nodeA, nodeB))
  1725. return false;
  1726. else if (nodeA.data != nodeB.data)
  1727. return false;
  1728. else if (!this.checkLowerCase(nodeA.tagName, nodeB.tagName))
  1729. return false;
  1730. else if (!this.checkLowerCase(nodeA.type, nodeB.type))
  1731. return false;
  1732. else
  1733. return true;
  1734. }, checkSameHtml:function (stringA, stringB, scholium) {
  1735. ok(this.checkSameNode(UE.htmlparser(stringA), UE.htmlparser(stringB)), scholium);
  1736. },
  1737. getContextmenuIndexByName:function(contextmenu,name){
  1738. for(var i=0;i<contextmenu.length;i++){
  1739. if(contextmenu[i].innerText ==name || contextmenu[i].textContent ==name)
  1740. return i;
  1741. }
  1742. return 0;
  1743. }
  1744. };
  1745. var ua = UserAction;
  1746. var upath = ua.commonData.currentPath();
  1747. var cpath = ua.commonData.datadir;