PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/casper.js

https://github.com/canadaduane/casperjs
JavaScript | 2705 lines | 1833 code | 155 blank | 717 comment | 230 complexity | 70e217597e077315cfd9dc39a9049c56 MD5 | raw file
  1. /*!
  2. * Casper is a navigation utility for PhantomJS.
  3. *
  4. * Documentation: http://n1k0.github.com/casperjs/
  5. * Repository: http://github.com/n1k0/casperjs
  6. *
  7. * Copyright (c) 2011 Nicolas Perriault
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a
  10. * copy of this software and associated documentation files (the "Software"),
  11. * to deal in the Software without restriction, including without limitation
  12. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13. * and/or sell copies of the Software, and to permit persons to whom the
  14. * Software is furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included
  17. * in all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  22. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. *
  27. */
  28. (function(phantom) {
  29. /**
  30. * Main Casper object.
  31. *
  32. * @param Object options Casper options
  33. * @return Casper
  34. */
  35. phantom.Casper = function(options) {
  36. var DEFAULT_DIE_MESSAGE = "Suite explicitely interrupted without any message given.";
  37. var DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1";
  38. // init & checks
  39. if (!(this instanceof arguments.callee)) {
  40. return new Casper(options);
  41. }
  42. // default options
  43. this.defaults = {
  44. clientScripts: [],
  45. faultTolerant: true,
  46. logLevel: "error",
  47. onDie: null,
  48. onError: null,
  49. onLoadError: null,
  50. onPageInitialized: null,
  51. page: null,
  52. pageSettings: { userAgent: DEFAULT_USER_AGENT },
  53. timeout: null,
  54. verbose: false
  55. };
  56. // privates
  57. // local properties
  58. this.checker = null;
  59. this.colorizer = new phantom.Casper.Colorizer();
  60. this.currentUrl = 'about:blank';
  61. this.currentHTTPStatus = 200;
  62. this.defaultWaitTimeout = 5000;
  63. this.delayedExecution = false;
  64. this.history = [];
  65. this.loadInProgress = false;
  66. this.logLevels = ["debug", "info", "warning", "error"];
  67. this.logStyles = {
  68. debug: 'INFO',
  69. info: 'PARAMETER',
  70. warning: 'COMMENT',
  71. error: 'ERROR'
  72. };
  73. this.options = mergeObjects(this.defaults, options);
  74. this.page = null;
  75. this.requestUrl = 'about:blank';
  76. this.result = {
  77. log: [],
  78. status: "success",
  79. time: 0
  80. };
  81. this.started = false;
  82. this.step = 0;
  83. this.steps = [];
  84. this.test = new phantom.Casper.Tester(this);
  85. };
  86. /**
  87. * Casper prototype
  88. */
  89. phantom.Casper.prototype = {
  90. /**
  91. * Go a step back in browser's history
  92. *
  93. * @return Casper
  94. */
  95. back: function() {
  96. return this.then(function(self) {
  97. self.evaluate(function() {
  98. history.back();
  99. });
  100. });
  101. },
  102. /**
  103. * Encodes a resource using the base64 algorithm synchroneously using
  104. * client-side XMLHttpRequest.
  105. *
  106. * NOTE: we cannot use window.btoa() for some strange reasons here.
  107. *
  108. * @param String url The url to download
  109. * @return string Base64 encoded result
  110. */
  111. base64encode: function(url) {
  112. return this.evaluate(function() {
  113. return __utils__.getBase64(__casper_params__.url);
  114. }, {
  115. url: url
  116. });
  117. },
  118. /**
  119. * Use Gibberish AES library to encode a string using a password.
  120. *
  121. * @param String text Some text to encode
  122. * @param String password The password to use to encode the text
  123. * @return String AES encoded result
  124. */
  125. aesEncode: function(text, password) {
  126. try {
  127. var encode = GibberishAES.enc;
  128. } catch (e) {
  129. throw 'GibberishAES library not found. Did you forget to phantom.injectJs("gibberish-aes.js")?';
  130. }
  131. return encode(text, password);
  132. },
  133. /**
  134. * Use Gibberish AES library to decode a string using a password.
  135. *
  136. * @param String text Some text to decode
  137. * @param String password The password to use to decode the text
  138. * @return String Decoded result
  139. */
  140. aesDecode: function(text, password) {
  141. try {
  142. var decode = GibberishAES.dec;
  143. } catch (e) {
  144. throw 'GibberishAES library not found. Did you forget to phantom.injectJs("gibberish-aes.js")?';
  145. }
  146. return decode(text, password);
  147. },
  148. /**
  149. * Proxy method for WebPage#render. Adds a clipRect parameter for
  150. * automatically set page clipRect setting values and sets it back once
  151. * done. If the cliprect parameter is omitted, the full page viewport
  152. * area will be rendered.
  153. *
  154. * @param String targetFile A target filename
  155. * @param mixed clipRect An optional clipRect object (optional)
  156. * @return Casper
  157. */
  158. capture: function(targetFile, clipRect) {
  159. var previousClipRect;
  160. if (clipRect) {
  161. if (!isType(clipRect, "object")) {
  162. throw new Error("clipRect must be an Object instance.");
  163. }
  164. previousClipRect = this.page.clipRect;
  165. this.page.clipRect = clipRect;
  166. this.log('Capturing page to ' + targetFile + ' with clipRect' + JSON.stringify(clipRect), "debug");
  167. } else {
  168. this.log('Capturing page to ' + targetFile, "debug");
  169. }
  170. try {
  171. this.page.render(targetFile);
  172. } catch (e) {
  173. this.log('Failed to capture screenshot as ' + targetFile + ': ' + e, "error");
  174. }
  175. if (previousClipRect) {
  176. this.page.clipRect = previousClipRect;
  177. }
  178. return this;
  179. },
  180. /**
  181. * Captures the page area containing the provided selector.
  182. *
  183. * @param String targetFile Target destination file path.
  184. * @param String selector CSS3 selector
  185. * @return Casper
  186. */
  187. captureSelector: function(targetFile, selector) {
  188. return this.capture(targetFile, this.evaluate(function() {
  189. try {
  190. var clipRect = document.querySelector(__casper_params__.selector).getBoundingClientRect();
  191. return {
  192. top: clipRect.top,
  193. left: clipRect.left,
  194. width: clipRect.width,
  195. height: clipRect.height
  196. };
  197. } catch (e) {
  198. __utils__.log("Unable to fetch bounds for element " + __casper_params__.selector, "warning");
  199. }
  200. }, {
  201. selector: selector
  202. }));
  203. },
  204. /**
  205. * Checks for any further navigation step to process.
  206. *
  207. * @param Casper self A self reference
  208. * @param function onComplete An options callback to apply on completion
  209. */
  210. checkStep: function(self, onComplete) {
  211. var step = self.steps[self.step];
  212. if (!self.loadInProgress && isType(step, "function")) {
  213. var curStepNum = self.step + 1;
  214. var stepInfo = "Step " + curStepNum + "/" + self.steps.length + ": ";
  215. self.log(stepInfo + self.page.evaluate(function() {
  216. return document.location.href;
  217. }) + ' (HTTP ' + self.currentHTTPStatus + ')', "info");
  218. try {
  219. step(self);
  220. } catch (e) {
  221. if (self.options.faultTolerant) {
  222. self.log("Step error: " + e, "error");
  223. } else {
  224. throw e;
  225. }
  226. }
  227. var time = new Date().getTime() - self.startTime;
  228. self.log(stepInfo + "done in " + time + "ms.", "info");
  229. self.step++;
  230. }
  231. if (!isType(step, "function") && !self.delayedExecution) {
  232. self.result.time = new Date().getTime() - self.startTime;
  233. self.log("Done " + self.steps.length + " steps in " + self.result.time + 'ms.', "info");
  234. clearInterval(self.checker);
  235. if (isType(onComplete, "function")) {
  236. try {
  237. onComplete(self);
  238. } catch (err) {
  239. self.log("could not complete final step: " + err, "error");
  240. }
  241. } else {
  242. // default behavior is to exit phantom
  243. self.exit();
  244. }
  245. }
  246. },
  247. /**
  248. * Emulates a click on the element from the provided selector, if
  249. * possible. In case of success, `true` is returned.
  250. *
  251. * @param String selector A DOM CSS3 compatible selector
  252. * @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true)
  253. * @return Boolean
  254. */
  255. click: function(selector, fallbackToHref) {
  256. fallbackToHref = isType(fallbackToHref, "undefined") ? true : !!fallbackToHref;
  257. this.log("click on selector: " + selector, "debug");
  258. return this.evaluate(function() {
  259. return __utils__.click(__casper_params__.selector, __casper_params__.fallbackToHref);
  260. }, {
  261. selector: selector,
  262. fallbackToHref: fallbackToHref
  263. });
  264. },
  265. /**
  266. * Logs the HTML code of the current page.
  267. *
  268. * @return Casper
  269. */
  270. debugHTML: function() {
  271. this.echo(this.evaluate(function() {
  272. return document.body.innerHTML;
  273. }));
  274. return this;
  275. },
  276. /**
  277. * Logs the textual contents of the current page.
  278. *
  279. * @return Casper
  280. */
  281. debugPage: function() {
  282. this.echo(this.evaluate(function() {
  283. return document.body.innerText;
  284. }));
  285. return this;
  286. },
  287. /**
  288. * Exit phantom on failure, with a logged error message.
  289. *
  290. * @param String message An optional error message
  291. * @param Number status An optional exit status code (must be > 0)
  292. * @return Casper
  293. */
  294. die: function(message, status) {
  295. this.result.status = 'error';
  296. this.result.time = new Date().getTime() - this.startTime;
  297. message = isType(message, "string") && message.length > 0 ? message : DEFAULT_DIE_MESSAGE;
  298. this.log(message, "error");
  299. if (isType(this.options.onDie, "function")) {
  300. this.options.onDie(this, message, status);
  301. }
  302. return this.exit(Number(status) > 0 ? Number(status) : 1);
  303. },
  304. /**
  305. * Iterates over the values of a provided array and execute a callback
  306. * for each item.
  307. *
  308. * @param Array array
  309. * @param Function fn Callback: function(self, item, index)
  310. * @return Casper
  311. */
  312. each: function(array, fn) {
  313. if (array.constructor !== Array) {
  314. self.log("each() only works with arrays", "error");
  315. return this;
  316. }
  317. (function(self) {
  318. array.forEach(function(item, i) {
  319. fn(self, item, i);
  320. });
  321. })(this);
  322. return this;
  323. },
  324. /**
  325. * Prints something to stdout.
  326. *
  327. * @param String text A string to echo to stdout
  328. * @return Casper
  329. */
  330. echo: function(text, style) {
  331. console.log(style ? this.colorizer.colorize(text, style) : text);
  332. return this;
  333. },
  334. /**
  335. * Evaluates an expression in the page context, a bit like what
  336. * WebPage#evaluate does, but can also replace values by their
  337. * placeholer names:
  338. *
  339. * casper.evaluate(function() {
  340. * document.querySelector('#username').value = '%username%';
  341. * document.querySelector('#password').value = '%password%';
  342. * document.querySelector('#submit').click();
  343. * }, {
  344. * username: 'Bazoonga',
  345. * password: 'baz00nga'
  346. * })
  347. *
  348. * As an alternative, CasperJS injects a `__casper_params__` Object
  349. * instance containing all the parameters you passed:
  350. *
  351. * casper.evaluate(function() {
  352. * document.querySelector('#username').value = __casper_params__.username;
  353. * document.querySelector('#password').value = __casper_params__.password;
  354. * document.querySelector('#submit').click();
  355. * }, {
  356. * username: 'Bazoonga',
  357. * password: 'baz00nga'
  358. * })
  359. *
  360. * FIXME: waiting for a patch of PhantomJS to allow direct passing of
  361. * arguments to the function.
  362. * TODO: don't forget to keep this backward compatible.
  363. *
  364. * @param function fn The function to be evaluated within current page DOM
  365. * @param object replacements Parameters to pass to the remote environment
  366. * @return mixed
  367. * @see WebPage#evaluate
  368. */
  369. evaluate: function(fn, replacements) {
  370. replacements = isType(replacements, "object") ? replacements : {};
  371. this.page.evaluate(replaceFunctionPlaceholders(function() {
  372. window.__casper_params__ = {};
  373. try {
  374. var jsonString = unescape(decodeURIComponent('%replacements%'));
  375. window.__casper_params__ = JSON.parse(jsonString);
  376. } catch (e) {
  377. __utils__.log("Unable to replace parameters: " + e, "error");
  378. }
  379. }, {
  380. replacements: encodeURIComponent(escape(JSON.stringify(replacements).replace("'", "\'")))
  381. }));
  382. return this.page.evaluate(replaceFunctionPlaceholders(fn, replacements));
  383. },
  384. /**
  385. * Evaluates an expression within the current page DOM and die() if it
  386. * returns false.
  387. *
  388. * @param function fn The expression to evaluate
  389. * @param String message The error message to log
  390. * @return Casper
  391. */
  392. evaluateOrDie: function(fn, message) {
  393. if (!this.evaluate(fn)) {
  394. return this.die(message);
  395. }
  396. return this;
  397. },
  398. /**
  399. * Checks if an element matching the provided CSS3 selector exists in
  400. * current page DOM.
  401. *
  402. * @param String selector A CSS3 selector
  403. * @return Boolean
  404. */
  405. exists: function(selector) {
  406. return this.evaluate(function() {
  407. return __utils__.exists(__casper_params__.selector);
  408. }, {
  409. selector: selector
  410. });
  411. },
  412. /**
  413. * Exits phantom.
  414. *
  415. * @param Number status Status
  416. * @return Casper
  417. */
  418. exit: function(status) {
  419. phantom.exit(status);
  420. return this;
  421. },
  422. /**
  423. * Fetches innerText within the element(s) matching a given CSS3
  424. * selector.
  425. *
  426. * @param String selector A CSS3 selector
  427. * @return String
  428. */
  429. fetchText: function(selector) {
  430. return this.evaluate(function() {
  431. return __utils__.fetchText(__casper_params__.selector);
  432. }, {
  433. selector: selector
  434. });
  435. },
  436. /**
  437. * Fills a form with provided field values.
  438. *
  439. * @param String selector A CSS3 selector to the target form to fill
  440. * @param Object vals Field values
  441. * @param Boolean submit Submit the form?
  442. */
  443. fill: function(selector, vals, submit) {
  444. submit = submit === true ? submit : false;
  445. if (!isType(selector, "string") || !selector.length) {
  446. throw "form selector must be a non-empty string";
  447. }
  448. if (!isType(vals, "object")) {
  449. throw "form values must be provided as an object";
  450. }
  451. var fillResults = this.evaluate(function() {
  452. return __utils__.fill(__casper_params__.selector, __casper_params__.values);
  453. }, {
  454. selector: selector,
  455. values: vals
  456. });
  457. if (!fillResults) {
  458. throw "unable to fill form";
  459. } else if (fillResults.errors.length > 0) {
  460. (function(self){
  461. fillResults.errors.forEach(function(error) {
  462. self.log("form error: " + error, "error");
  463. });
  464. })(this);
  465. if (submit) {
  466. this.log("errors encountered while filling form; submission aborted", "warning");
  467. submit = false;
  468. }
  469. }
  470. // File uploads
  471. if (fillResults.files && fillResults.files.length > 0) {
  472. (function(self) {
  473. fillResults.files.forEach(function(file) {
  474. var fileFieldSelector = [selector, 'input[name="' + file.name + '"]'].join(' ');
  475. self.page.uploadFile(fileFieldSelector, file.path);
  476. });
  477. })(this);
  478. }
  479. // Form submission?
  480. if (submit) {
  481. this.evaluate(function() {
  482. var form = document.querySelector(__casper_params__.selector);
  483. var method = form.getAttribute('method').toUpperCase() || "GET";
  484. var action = form.getAttribute('action') || "unknown";
  485. __utils__.log('submitting form to ' + action + ', HTTP ' + method, 'info');
  486. form.submit();
  487. }, {
  488. selector: selector
  489. });
  490. }
  491. },
  492. /**
  493. * Go a step forward in browser's history
  494. *
  495. * @return Casper
  496. */
  497. forward: function(then) {
  498. return this.then(function(self) {
  499. self.evaluate(function() {
  500. history.forward();
  501. });
  502. });
  503. },
  504. /**
  505. * Retrieves current document url.
  506. *
  507. * @return String
  508. */
  509. getCurrentUrl: function() {
  510. return decodeURIComponent(this.evaluate(function() {
  511. return document.location.href;
  512. }));
  513. },
  514. /**
  515. * Retrieves global variable.
  516. *
  517. * @param String name The name of the global variable to retrieve
  518. * @return mixed
  519. */
  520. getGlobal: function(name) {
  521. return this.evaluate(function() {
  522. return window[window.__casper_params__.name];
  523. }, {'name': name});
  524. },
  525. /**
  526. * Retrieves current page title, if any.
  527. *
  528. * @return String
  529. */
  530. getTitle: function() {
  531. return this.evaluate(function() {
  532. return document.title;
  533. });
  534. },
  535. /**
  536. * Logs a message.
  537. *
  538. * @param String message The message to log
  539. * @param String level The log message level (from Casper.logLevels property)
  540. * @param String space Space from where the logged event occured (default: "phantom")
  541. * @return Casper
  542. */
  543. log: function(message, level, space) {
  544. level = level && this.logLevels.indexOf(level) > -1 ? level : "debug";
  545. space = space ? space : "phantom";
  546. if (level === "error" && isType(this.options.onError, "function")) {
  547. this.options.onError(this, message, space);
  548. }
  549. if (this.logLevels.indexOf(level) < this.logLevels.indexOf(this.options.logLevel)) {
  550. return this; // skip logging
  551. }
  552. if (this.options.verbose) {
  553. var levelStr = this.colorizer.colorize('[' + level + ']', this.logStyles[level]);
  554. this.echo(levelStr + ' [' + space + '] ' + message); // direct output
  555. }
  556. this.result.log.push({
  557. level: level,
  558. space: space,
  559. message: message,
  560. date: new Date().toString()
  561. });
  562. return this;
  563. },
  564. /**
  565. * Opens a page. Takes only one argument, the url to open (using the
  566. * callback argument would defeat the whole purpose of Casper
  567. * actually).
  568. *
  569. * @param String location The url to open
  570. * @return Casper
  571. */
  572. open: function(location) {
  573. this.requestUrl = location;
  574. this.page.open(location);
  575. return this;
  576. },
  577. /**
  578. * Repeats a step a given number of times.
  579. *
  580. * @param Number times Number of times to repeat step
  581. * @aram function then The step closure
  582. * @return Casper
  583. * @see Casper#then
  584. */
  585. repeat: function(times, then) {
  586. for (var i = 0; i < times; i++) {
  587. this.then(then);
  588. }
  589. return this;
  590. },
  591. /**
  592. * Runs the whole suite of steps.
  593. *
  594. * @param function onComplete an optional callback
  595. * @param Number time an optional amount of milliseconds for interval checking
  596. * @return Casper
  597. */
  598. run: function(onComplete, time) {
  599. if (!this.steps || this.steps.length < 1) {
  600. this.log("No steps defined, aborting", "error");
  601. return this;
  602. }
  603. this.log("Running suite: " + this.steps.length + " step" + (this.steps.length > 1 ? "s" : ""), "info");
  604. this.checker = setInterval(this.checkStep, (time ? time: 250), this, onComplete);
  605. return this;
  606. },
  607. /**
  608. * Configures and starts Casper.
  609. *
  610. * @param String location An optional location to open on start
  611. * @param function then Next step function to execute on page loaded (optional)
  612. * @return Casper
  613. */
  614. start: function(location, then) {
  615. if (this.started) {
  616. this.log("start failed: Casper has already started!", "error");
  617. }
  618. this.log('Starting...', "info");
  619. this.startTime = new Date().getTime();
  620. this.steps = [];
  621. this.step = 0;
  622. // Option checks
  623. if (this.logLevels.indexOf(this.options.logLevel) < 0) {
  624. this.log("Unknown log level '" + this.options.logLevel + "', defaulting to 'warning'", "warning");
  625. this.options.logLevel = "warning";
  626. }
  627. // WebPage
  628. if (!isWebPage(this.page)) {
  629. if (isWebPage(this.options.page)) {
  630. this.page = this.options.page;
  631. } else {
  632. this.page = createPage(this);
  633. }
  634. }
  635. this.page.settings = mergeObjects(this.page.settings, this.options.pageSettings);
  636. if (isType(this.options.clipRect, "object")) {
  637. this.page.clipRect = this.options.clipRect;
  638. }
  639. if (isType(this.options.viewportSize, "object")) {
  640. this.page.viewportSize = this.options.viewportSize;
  641. }
  642. this.started = true;
  643. if (isType(this.options.timeout, "number") && this.options.timeout > 0) {
  644. self.log("execution timeout set to " + this.options.timeout + 'ms', "info");
  645. setTimeout(function(self) {
  646. self.log("timeout of " + self.options.timeout + "ms exceeded", "info").exit();
  647. }, this.options.timeout, this);
  648. }
  649. if (isType(this.options.onPageInitialized, "function")) {
  650. this.log("Post-configuring WebPage instance", "debug");
  651. this.options.onPageInitialized(this.page);
  652. }
  653. if (isType(location, "string") && location.length > 0) {
  654. if (isType(then, "function")) {
  655. return this.open(location).then(then);
  656. } else {
  657. return this.open(location);
  658. }
  659. }
  660. return this;
  661. },
  662. /**
  663. * Schedules the next step in the navigation process.
  664. *
  665. * @param function step A function to be called as a step
  666. * @return Casper
  667. */
  668. then: function(step) {
  669. if (!this.started) {
  670. throw "Casper not started; please use Casper#start";
  671. }
  672. if (!isType(step, "function")) {
  673. throw "You can only define a step as a function";
  674. }
  675. this.steps.push(step);
  676. return this;
  677. },
  678. /**
  679. * Adds a new navigation step for clicking on a provided link selector
  680. * and execute an optional next step.
  681. *
  682. * @param String selector A DOM CSS3 compatible selector
  683. * @param Function then Next step function to execute on page loaded (optional)
  684. * @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true)
  685. * @return Casper
  686. * @see Casper#click
  687. * @see Casper#then
  688. */
  689. thenClick: function(selector, then, fallbackToHref) {
  690. this.then(function(self) {
  691. self.click(selector, fallbackToHref);
  692. });
  693. return isType(then, "function") ? this.then(then) : this;
  694. },
  695. /**
  696. * Adds a new navigation step to perform code evaluation within the
  697. * current retrieved page DOM.
  698. *
  699. * @param function fn The function to be evaluated within current page DOM
  700. * @param object replacements Optional replacements to performs, eg. for '%foo%' => {foo: 'bar'}
  701. * @return Casper
  702. * @see Casper#evaluate
  703. */
  704. thenEvaluate: function(fn, replacements) {
  705. return this.then(function(self) {
  706. self.evaluate(fn, replacements);
  707. });
  708. },
  709. /**
  710. * Adds a new navigation step for opening the provided location.
  711. *
  712. * @param String location The URL to load
  713. * @param function then Next step function to execute on page loaded (optional)
  714. * @return Casper
  715. * @see Casper#open
  716. */
  717. thenOpen: function(location, then) {
  718. this.then(function(self) {
  719. self.open(location);
  720. });
  721. return isType(then, "function") ? this.then(then) : this;
  722. },
  723. /**
  724. * Adds a new navigation step for opening and evaluate an expression
  725. * against the DOM retrieved from the provided location.
  726. *
  727. * @param String location The url to open
  728. * @param function fn The function to be evaluated within current page DOM
  729. * @param object replacements Optional replacements to performs, eg. for '%foo%' => {foo: 'bar'}
  730. * @return Casper
  731. * @see Casper#evaluate
  732. * @see Casper#open
  733. */
  734. thenOpenAndEvaluate: function(location, fn, replacements) {
  735. return this.thenOpen(location).thenEvaluate(fn, replacements);
  736. },
  737. /**
  738. * Changes the current viewport size.
  739. *
  740. * @param Number width The viewport width, in pixels
  741. * @param Number height The viewport height, in pixels
  742. * @return Casper
  743. */
  744. viewport: function(width, height) {
  745. if (!isType(width, "number") || !isType(height, "number") || width <= 0 || height <= 0) {
  746. throw new Error("Invalid viewport width/height set: " + width + 'x' + height);
  747. }
  748. this.page.viewportSize = {
  749. width: width,
  750. height: height
  751. };
  752. return this;
  753. },
  754. /**
  755. * Adds a new step that will wait for a given amount of time (expressed
  756. * in milliseconds) before processing an optional next one.
  757. *
  758. * @param Number timeout The max amount of time to wait, in milliseconds
  759. * @param Function then Next step to process (optional)
  760. * @return Casper
  761. */
  762. wait: function(timeout, then) {
  763. timeout = Number(timeout, 10);
  764. if (!isType(timeout, "number") || timeout < 1) {
  765. this.die("wait() only accepts a positive integer > 0 as a timeout value");
  766. }
  767. if (then && !isType(then, "function")) {
  768. this.die("wait() a step definition must be a function");
  769. }
  770. return this.then(function(self) {
  771. self.delayedExecution = true;
  772. var start = new Date().getTime();
  773. var interval = setInterval(function(self, then) {
  774. if (new Date().getTime() - start > timeout) {
  775. self.delayedExecution = false;
  776. self.log("wait() finished wating for " + timeout + "ms.", "info");
  777. if (then) {
  778. self.then(then);
  779. }
  780. clearInterval(interval);
  781. }
  782. }, 100, self, then);
  783. });
  784. },
  785. /**
  786. * Waits until a function returns true to process a next step.
  787. *
  788. * @param Function testFx A function to be evaluated for returning condition satisfecit
  789. * @param Function then The next step to perform (optional)
  790. * @param Function onTimeout A callback function to call on timeout (optional)
  791. * @param Number timeout The max amount of time to wait, in milliseconds (optional)
  792. * @return Casper
  793. */
  794. waitFor: function(testFx, then, onTimeout, timeout) {
  795. timeout = timeout ? timeout : this.defaultWaitTimeout;
  796. if (!isType(testFx, "function")) {
  797. this.die("waitFor() needs a test function");
  798. }
  799. if (then && !isType(then, "function")) {
  800. this.die("waitFor() next step definition must be a function");
  801. }
  802. this.delayedExecution = true;
  803. var start = new Date().getTime();
  804. var condition = false;
  805. var interval = setInterval(function(self, testFx, onTimeout) {
  806. if ((new Date().getTime() - start < timeout) && !condition) {
  807. condition = testFx(self);
  808. } else {
  809. self.delayedExecution = false;
  810. if (!condition) {
  811. self.log("Casper.waitFor() timeout", "warning");
  812. if (isType(onTimeout, "function")) {
  813. onTimeout(self);
  814. } else {
  815. self.die("Expired timeout, exiting.", "error");
  816. }
  817. clearInterval(interval);
  818. } else {
  819. self.log("waitFor() finished in " + (new Date().getTime() - start) + "ms.", "info");
  820. if (then) {
  821. self.then(then);
  822. }
  823. clearInterval(interval);
  824. }
  825. }
  826. }, 100, this, testFx, onTimeout);
  827. return this;
  828. },
  829. /**
  830. * Waits until an element matching the provided CSS3 selector exists in
  831. * remote DOM to process a next step.
  832. *
  833. * @param String selector A CSS3 selector
  834. * @param Function then The next step to perform (optional)
  835. * @param Function onTimeout A callback function to call on timeout (optional)
  836. * @param Number timeout The max amount of time to wait, in milliseconds (optional)
  837. * @return Casper
  838. */
  839. waitForSelector: function(selector, then, onTimeout, timeout) {
  840. timeout = timeout ? timeout : this.defaultWaitTimeout;
  841. return this.waitFor(function(self) {
  842. return self.exists(selector);
  843. }, then, onTimeout, timeout);
  844. }
  845. };
  846. /**
  847. * Extends Casper's prototype with provided one.
  848. *
  849. * @param Object proto Prototype methods to add to Casper
  850. */
  851. phantom.Casper.extend = function(proto) {
  852. if (!isType(proto, "object")) {
  853. throw "extends() only accept objects as prototypes";
  854. }
  855. mergeObjects(phantom.Casper.prototype, proto);
  856. };
  857. /**
  858. * Casper client-side helpers.
  859. */
  860. phantom.Casper.ClientUtils = function() {
  861. /**
  862. * Clicks on the DOM element behind the provided selector.
  863. *
  864. * @param String selector A CSS3 selector to the element to click
  865. * @param Boolean fallbackToHref Whether to try to relocate to the value of any href attribute (default: true)
  866. * @return Boolean
  867. */
  868. this.click = function(selector, fallbackToHref) {
  869. fallbackToHref = typeof fallbackToHref === "undefined" ? true : !!fallbackToHref;
  870. var elem = this.findOne(selector);
  871. if (!elem) {
  872. return false;
  873. }
  874. var evt = document.createEvent("MouseEvents");
  875. evt.initMouseEvent("click", true, true, window, 1, 1, 1, 1, 1, false, false, false, false, 0, elem);
  876. if (elem.dispatchEvent(evt)) {
  877. return true;
  878. }
  879. if (fallbackToHref && elem.hasAttribute('href')) {
  880. document.location = elem.getAttribute('href');
  881. return true;
  882. }
  883. return false;
  884. };
  885. /**
  886. * Base64 encodes a string, even binary ones. Succeeds where
  887. * window.btoa() fails.
  888. *
  889. * @param String str
  890. * @return string
  891. */
  892. this.encode = function(str) {
  893. var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  894. var out = "", i = 0, len = str.length, c1, c2, c3;
  895. while (i < len) {
  896. c1 = str.charCodeAt(i++) & 0xff;
  897. if (i == len) {
  898. out += CHARS.charAt(c1 >> 2);
  899. out += CHARS.charAt((c1 & 0x3) << 4);
  900. out += "==";
  901. break;
  902. }
  903. c2 = str.charCodeAt(i++);
  904. if (i == len) {
  905. out += CHARS.charAt(c1 >> 2);
  906. out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
  907. out += CHARS.charAt((c2 & 0xF) << 2);
  908. out += "=";
  909. break;
  910. }
  911. c3 = str.charCodeAt(i++);
  912. out += CHARS.charAt(c1 >> 2);
  913. out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
  914. out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
  915. out += CHARS.charAt(c3 & 0x3F);
  916. }
  917. return out;
  918. };
  919. /**
  920. * Checks if a given DOM element exists in remote page.
  921. *
  922. * @param String selector CSS3 selector
  923. * @return Boolean
  924. */
  925. this.exists = function(selector) {
  926. try {
  927. return document.querySelectorAll(selector).length > 0;
  928. } catch (e) {
  929. return false;
  930. }
  931. };
  932. /**
  933. * Fetches innerText within the element(s) matching a given CSS3
  934. * selector.
  935. *
  936. * @param String selector A CSS3 selector
  937. * @return String
  938. */
  939. this.fetchText = function(selector) {
  940. var text = '', elements = this.findAll(selector);
  941. if (elements && elements.length) {
  942. Array.prototype.forEach.call(elements, function(element) {
  943. text += element.innerText;
  944. });
  945. }
  946. return text;
  947. };
  948. /**
  949. * Fills a form with provided field values, and optionnaly submits it.
  950. *
  951. * @param HTMLElement|String form A form element, or a CSS3 selector to a form element
  952. * @param Object vals Field values
  953. * @return Object An object containing setting result for each field, including file uploads
  954. */
  955. this.fill = function(form, vals) {
  956. var out = {
  957. errors: [],
  958. fields: [],
  959. files: []
  960. };
  961. if (!(form instanceof HTMLElement) || typeof form === "string") {
  962. __utils__.log("attempting to fetch form element from selector: '" + form + "'", "info");
  963. try {
  964. form = document.querySelector(form);
  965. } catch (e) {
  966. if (e.name === "SYNTAX_ERR") {
  967. out.errors.push("invalid form selector provided: '" + form + "'");
  968. return out;
  969. }
  970. }
  971. }
  972. if (!form) {
  973. out.errors.push("form not found");
  974. return out;
  975. }
  976. for (var name in vals) {
  977. if (!vals.hasOwnProperty(name)) {
  978. continue;
  979. }
  980. var field = form.querySelectorAll('[name="' + name + '"]');
  981. var value = vals[name];
  982. if (!field) {
  983. out.errors.push('no field named "' + name + '" in form');
  984. continue;
  985. }
  986. try {
  987. out.fields[name] = this.setField(field, value);
  988. } catch (err) {
  989. if (err.name === "FileUploadError") {
  990. out.files.push({
  991. name: name,
  992. path: err.path
  993. });
  994. } else {
  995. throw err;
  996. }
  997. }
  998. }
  999. return out;
  1000. };
  1001. /**
  1002. * Finds all DOM elements matching by the provided selector.
  1003. *
  1004. * @param String selector CSS3 selector
  1005. * @return NodeList|undefined
  1006. */
  1007. this.findAll = function(selector) {
  1008. try {
  1009. return document.querySelectorAll(selector);
  1010. } catch (e) {
  1011. this.log('findAll(): invalid selector provided "' + selector + '":' + e, "error");
  1012. }
  1013. };
  1014. /**
  1015. * Finds a DOM element by the provided selector.
  1016. *
  1017. * @param String selector CSS3 selector
  1018. * @return HTMLElement|undefined
  1019. */
  1020. this.findOne = function(selector) {
  1021. try {
  1022. return document.querySelector(selector);
  1023. } catch (e) {
  1024. this.log('findOne(): invalid selector provided "' + selector + '":' + e, "errors");
  1025. }
  1026. };
  1027. /**
  1028. * Downloads a resource behind an url and returns its base64-encoded
  1029. * contents.
  1030. *
  1031. * @param String url The resource url
  1032. * @return String Base64 contents string
  1033. */
  1034. this.getBase64 = function(url) {
  1035. return this.encode(this.getBinary(url));
  1036. };
  1037. /**
  1038. * Retrieves string contents from a binary file behind an url. Silently
  1039. * fails but log errors.
  1040. *
  1041. * @param String url
  1042. * @return string
  1043. */
  1044. this.getBinary = function(url) {
  1045. try {
  1046. var xhr = new XMLHttpRequest();
  1047. xhr.open("GET", url, false);
  1048. xhr.overrideMimeType("text/plain; charset=x-user-defined");
  1049. xhr.send(null);
  1050. return xhr.responseText;
  1051. } catch (e) {
  1052. if (e.name === "NETWORK_ERR" && e.code === 101) {
  1053. this.log("unfortunately, casperjs cannot make cross domain ajax requests", "warning");
  1054. }
  1055. this.log("error while fetching " + url + ": " + e, "error");
  1056. return "";
  1057. }
  1058. };
  1059. /**
  1060. * Logs a message.
  1061. *
  1062. * @param String message
  1063. * @param String level
  1064. */
  1065. this.log = function(message, level) {
  1066. console.log("[casper:" + (level || "debug") + "] " + message);
  1067. };
  1068. /**
  1069. * Sets a field (or a set of fields) value. Fails silently, but log
  1070. * error messages.
  1071. *
  1072. * @param HTMLElement|NodeList field One or more element defining a field
  1073. * @param mixed value The field value to set
  1074. */
  1075. this.setField = function(field, value) {
  1076. var fields, out;
  1077. value = value || "";
  1078. if (field instanceof NodeList) {
  1079. fields = field;
  1080. field = fields[0];
  1081. }
  1082. if (!field instanceof HTMLElement) {
  1083. this.log("invalid field type; only HTMLElement and NodeList are supported", "error");
  1084. }
  1085. this.log('set "' + field.getAttribute('name') + '" field value to ' + value, "debug");
  1086. try {
  1087. field.focus();
  1088. } catch (e) {
  1089. __utils__.log("Unable to focus() input field " + field.getAttribute('name') + ": " + e, "warning");
  1090. }
  1091. var nodeName = field.nodeName.toLowerCase();
  1092. switch (nodeName) {
  1093. case "input":
  1094. var type = field.getAttribute('type') || "text";
  1095. switch (type.toLowerCase()) {
  1096. case "color":
  1097. case "date":
  1098. case "datetime":
  1099. case "datetime-local":
  1100. case "email":
  1101. case "hidden":
  1102. case "month":
  1103. case "number":
  1104. case "password":
  1105. case "range":
  1106. case "search":
  1107. case "tel":
  1108. case "text":
  1109. case "time":
  1110. case "url":
  1111. case "week":
  1112. field.value = value;
  1113. break;
  1114. case "checkbox":
  1115. field.setAttribute('checked', value ? "checked" : "");
  1116. break;
  1117. case "file":
  1118. throw {
  1119. name: "FileUploadError",
  1120. message: "file field must be filled using page.uploadFile",
  1121. path: value
  1122. };
  1123. case "radio":
  1124. if (fields) {
  1125. Array.prototype.forEach.call(fields, function(e) {
  1126. e.checked = (e.value === value);
  1127. });
  1128. } else {
  1129. out = 'provided radio elements are empty';
  1130. }
  1131. break;
  1132. default:
  1133. out = "unsupported input field type: " + type;
  1134. break;
  1135. }
  1136. break;
  1137. case "select":
  1138. case "textarea":
  1139. field.value = value;
  1140. break;
  1141. default:
  1142. out = 'unsupported field type: ' + nodeName;
  1143. break;
  1144. }
  1145. try {
  1146. field.blur();
  1147. } catch (err) {
  1148. __utils__.log("Unable to blur() input field " + field.getAttribute('name') + ": " + err, "warning");
  1149. }
  1150. return out;
  1151. };
  1152. };
  1153. /**
  1154. * This is a port of lime colorizer.
  1155. * http://trac.symfony-project.org/browser/tools/lime/trunk/lib/lime.php)
  1156. *
  1157. * (c) Fabien Potencier, Symfony project, MIT license
  1158. */
  1159. phantom.Casper.Colorizer = function() {
  1160. var options = { bold: 1, underscore: 4, blink: 5, reverse: 7, conceal: 8 };
  1161. var foreground = { black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37 };
  1162. var background = { black: 40, red: 41, green: 42, yellow: 43, blue: 44, magenta: 45, cyan: 46, white: 47 };
  1163. var styles = {
  1164. 'ERROR': { bg: 'red', fg: 'white', bold: true },
  1165. 'INFO': { fg: 'green', bold: true },
  1166. 'TRACE': { fg: 'green', bold: true },
  1167. 'PARAMETER': { fg: 'cyan' },
  1168. 'COMMENT': { fg: 'yellow' },
  1169. 'WARNING': { fg: 'red', bold: true },
  1170. 'GREEN_BAR': { fg: 'white', bg: 'green', bold: true },
  1171. 'RED_BAR': { fg: 'white', bg: 'red', bold: true },
  1172. 'INFO_BAR': { fg: 'cyan', bold: true }
  1173. };
  1174. /**
  1175. * Adds a style to provided text.
  1176. *
  1177. * @params String text
  1178. * @params String styleName
  1179. * @return String
  1180. */
  1181. this.colorize = function(text, styleName) {
  1182. if (styleName in styles) {
  1183. return this.format(text, styles[styleName]);
  1184. }
  1185. return text;
  1186. };
  1187. /**
  1188. * Formats a text using a style declaration object.
  1189. *
  1190. * @param String text
  1191. * @param Object style
  1192. * @return String
  1193. */
  1194. this.format = function(text, style) {
  1195. if (typeof style !== "object") {
  1196. return text;
  1197. }
  1198. var codes = [];
  1199. if (style.fg && foreground[style.fg]) {
  1200. codes.push(foreground[style.fg]);
  1201. }
  1202. if (style.bg && background[style.bg]) {
  1203. codes.push(background[style.bg]);
  1204. }
  1205. for (var option in options) {
  1206. if (style[option] === true) {
  1207. codes.push(options[option]);
  1208. }
  1209. }
  1210. return "\033[" + codes.join(';') + 'm' + text + "\033[0m";
  1211. };
  1212. };
  1213. /**
  1214. * Casper tester: makes assertions, stores test results and display them.
  1215. *
  1216. */
  1217. phantom.Casper.Tester = function(casper, options) {
  1218. this.options = isType(options, "object") ? options : {};
  1219. if (!casper instanceof phantom.Casper) {
  1220. throw "phantom.Casper.Tester needs a phantom.Casper instance";
  1221. }
  1222. // locals
  1223. var exporter = new phantom.Casper.XUnitExporter();
  1224. var PASS = this.options.PASS || "PASS";
  1225. var FAIL = this.options.FAIL || "FAIL";
  1226. // properties
  1227. this.testResults = {
  1228. passed: 0,
  1229. failed: 0
  1230. };
  1231. // methods
  1232. /**
  1233. * Asserts a condition resolves to true.
  1234. *
  1235. * @param Boolean condition
  1236. * @param String message Test description
  1237. */
  1238. this.assert = function(condition, message) {
  1239. var status = PASS;
  1240. if (condition === true) {
  1241. style = 'INFO';
  1242. this.testResults.passed++;
  1243. exporter.addSuccess("unknown", message);
  1244. } else {
  1245. status = FAIL;
  1246. style = 'RED_BAR';
  1247. this.testResults.failed++;
  1248. exporter.addFailure("unknown", message, 'test failed', "assert");
  1249. }
  1250. casper.echo([this.colorize(status, style), this.formatMessage(message)].join(' '));
  1251. };
  1252. /**
  1253. * Asserts that two values are strictly equals.
  1254. *
  1255. * @param Boolean testValue The value to test
  1256. * @param Boolean expected The expected value
  1257. * @param String message Test description
  1258. */
  1259. this.assertEquals = function(testValue, expected, message) {
  1260. if (expected === testValue) {
  1261. casper.echo(this.colorize(PASS, 'INFO') + ' ' + this.formatMessage(message));
  1262. this.testResults.passed++;
  1263. exporter.addSuccess("unknown", message);
  1264. } else {
  1265. casper.echo(this.colorize(FAIL, 'RED_BAR') + ' ' + this.formatMessage(message, 'WARNING'));
  1266. this.comment(' got: ' + testValue);
  1267. this.comment(' expected: ' + expected);
  1268. this.testResults.failed++;
  1269. exporter.addFailure("unknown", message, "test failed; expected: " + expected + "; got: " + testValue, "assertEquals");
  1270. }
  1271. };
  1272. /**
  1273. * Asserts that a code evaluation in remote DOM resolves to true.
  1274. *
  1275. * @param Function fn A function to be evaluated in remote DOM
  1276. * @param String message Test description
  1277. */
  1278. this.assertEval = function(fn, message) {
  1279. return this.assert(casper.evaluate(fn), message);
  1280. };
  1281. /**
  1282. * Asserts that the result of a code evaluation in remote DOM equals
  1283. * an expected value.
  1284. *
  1285. * @param Function fn The function to be evaluated in remote DOM
  1286. * @param Boolean expected The expected value
  1287. * @param String message Test description
  1288. */
  1289. this.assertEvalEquals = function(fn, expected, message) {
  1290. return this.assertEquals(casper.evaluate(fn), expected, message);
  1291. };
  1292. /**
  1293. * Asserts that an element matching the provided CSS3 selector exists in
  1294. * remote DOM.
  1295. *
  1296. * @param String selector CSS3 selectore
  1297. * @param String message Test description
  1298. */
  1299. this.assertExists = function(selector, message) {
  1300. return this.assert(casper.exists(selector), message);
  1301. };
  1302. /**
  1303. * Asserts that a provided string matches a provided RegExp pattern.
  1304. *
  1305. * @param String subject The string to test
  1306. * @param RegExp pattern A RegExp object instance
  1307. * @param String message Test description
  1308. */
  1309. this.assertMatch = function(subject, pattern, message) {
  1310. if (pattern.test(subject)) {
  1311. casper.echo(this.colorize(PASS, 'INFO') + ' ' + this.formatMessage(message));
  1312. this.testResults.passed++;
  1313. exporter.addSuccess("unknown", message);
  1314. } else {
  1315. casper.echo(this.colorize(FAIL, 'RED_BAR') + ' ' + this.formatMessage(message, 'WARNING'));
  1316. this.comment(' subject: ' + subject);
  1317. this.comment(' pattern: ' + pattern.toString());
  1318. this.testResults.failed++;
  1319. exporter.addFailure("unknown", message, "test failed; subject: " + subject + "; pattern: " + pattern.toString(), "assertMatch");
  1320. }
  1321. };
  1322. /**
  1323. * Asserts that the provided function called with the given parameters
  1324. * will raise an exception.
  1325. *
  1326. * @param Function fn The function to test
  1327. * @param Array args The arguments to pass to the function
  1328. * @param String message Test description
  1329. */
  1330. this.assertRaises = function(fn, args, message) {
  1331. try {
  1332. fn.apply(null, args);
  1333. this.fail(message);
  1334. } catch (e) {
  1335. this.pass(message);
  1336. }
  1337. };
  1338. /**
  1339. * Asserts that at least an element matching the provided CSS3 selector
  1340. * exists in remote DOM.
  1341. *
  1342. * @param String selector A CSS3 selector string
  1343. * @param String message Test description
  1344. */
  1345. this.assertSelectorExists = function(selector, message) {
  1346. return this.assert(this.exists(selector), message);
  1347. };
  1348. /**
  1349. * Asserts that title of the remote page equals to the expected one.
  1350. *
  1351. * @param String expected The expected title string
  1352. * @param String message Test description
  1353. */
  1354. this.assertTitle = function(expected, message) {
  1355. return this.assertEquals(casper.getTitle(), expected, message);
  1356. };
  1357. /**
  1358. * Asserts that the provided input is of the given type.
  1359. *
  1360. * @param mixed input The value to test
  1361. * @param String type The javascript type name
  1362. * @param String message Test description
  1363. */
  1364. this.assertType = function(input, type, message) {
  1365. return this.assertEquals(betterTypeOf(input), type, message);
  1366. };
  1367. /**
  1368. * Asserts that a the current page url matches the provided RegExp
  1369. * pattern.
  1370. *
  1371. * @param RegExp pattern A RegExp object instance
  1372. * @param String message Test description
  1373. */
  1374. this.assertUrlMatch = function(pattern, message) {
  1375. return this.assertMatch(casper.getCurrentUrl(), pattern, message);
  1376. };
  1377. /**
  1378. * Render a colorized output. Basically a proxy method for
  1379. * Casper.Colorizer#colorize()
  1380. */
  1381. this.colorize = function(message, style) {
  1382. return casper.colorizer.colorize(message, style);
  1383. };
  1384. /**
  1385. * Writes a comment-style formatted message to stdout.
  1386. *
  1387. * @param String message
  1388. */
  1389. this.comment = function(message) {
  1390. casper.echo('# ' + message, 'COMMENT');
  1391. };
  1392. /**
  1393. * Writes an error-style formatted message to stdout.
  1394. *
  1395. * @param String message
  1396. */
  1397. this.error = function(message) {
  1398. casper.echo(message, 'ERROR');
  1399. };
  1400. /**
  1401. * Adds a failed test entry to the stack.
  1402. *
  1403. * @param String message
  1404. */
  1405. this.fail = function(message) {
  1406. this.assert(false, message);
  1407. };
  1408. /**
  1409. * Formats a message to highlight some parts of it.
  1410. *
  1411. * @param String message
  1412. * @param String style
  1413. */
  1414. this.formatMessage = function(message, style) {
  1415. var parts = /([a-z0-9_\.]+\(\))(.*)/i.exec(message);
  1416. if (!parts) {
  1417. return message;
  1418. }
  1419. return this.colorize(parts[1], 'PARAMETER') + this.colorize(parts[2], style);
  1420. };
  1421. /**
  1422. * Writes an info-style formatted message to stdout.
  1423. *
  1424. * @param String message
  1425. */
  1426. this.info = function(message) {
  1427. casper.echo(message, 'PARAMETER');
  1428. };
  1429. /**
  1430. * Adds a successful test entry to the stack.
  1431. *
  1432. * @param String message
  1433. */
  1434. this.pass = function(message) {
  1435. this.assert(true, message);
  1436. };
  1437. /**
  1438. * Render tests results, an optionnaly exit phantomjs.
  1439. *
  1440. * @param Boolean exit
  1441. */
  1442. this.renderResults = function(exit, status, save) {
  1443. save = isType(save, "string") ? save : this.options.save;
  1444. var total = this.testResults.passed + this.testResults.failed, statusText, style, result;
  1445. if (this.testResults.failed > 0) {
  1446. statusText = FAIL;
  1447. style = 'RED_BAR';
  1448. } else {
  1449. statusText = PASS;
  1450. style = 'GREEN_BAR';
  1451. }
  1452. result = statusText + ' ' + total + ' tests executed, ' + this.testResults.passed + ' passed, ' + this.testResults.failed + ' failed.';
  1453. if (result.length < 80) {
  1454. result += new Array(80 - result.length + 1).join(' ');
  1455. }
  1456. casper.echo(this.colorize(result, style));
  1457. if (save && isType(require, "function")) {
  1458. try {
  1459. require('fs').write(save, exporter.getXML(), 'w');
  1460. casper.echo('result log stored in ' + save, 'INFO');
  1461. } catch (e) {
  1462. casper.echo('unable to write results to ' + save + '; ' + e, 'ERROR');
  1463. }
  1464. }
  1465. if (exit === true) {
  1466. casper.exit(status || 0);
  1467. }
  1468. };
  1469. };
  1470. /**
  1471. * JUnit XML (xUnit) exporter for test results.
  1472. *
  1473. */
  1474. phantom.Casper.XUnitExporter = function() {
  1475. var node = function(name, attributes) {
  1476. var node = document.createElement(name);
  1477. for (var attrName in attributes) {
  1478. var value = attributes[attrName];
  1479. if (attributes.hasOwnProperty(attrName) && isType(attrName, "string")) {
  1480. node.setAttribute(attrName, value);
  1481. }
  1482. }
  1483. return node;
  1484. };
  1485. var xml = node('testsuite');
  1486. xml.toString = function() {
  1487. return this.outerHTML; // ouch
  1488. };
  1489. /**
  1490. * Adds a successful test result
  1491. *
  1492. * @param String classname
  1493. * @param String name
  1494. */
  1495. this.addSuccess = function(classname, name) {
  1496. xml.appendChild(node('testcase', {
  1497. classname: classname,
  1498. name: name
  1499. }));
  1500. };
  1501. /**
  1502. * Adds a failed test result
  1503. *
  1504. * @param String classname
  1505. * @param String name
  1506. * @param String message
  1507. * @param String type
  1508. */
  1509. this.addFailure = function(classname, name, message, type) {
  1510. var fnode = node('testcase', {
  1511. classname: classname,
  1512. name: name
  1513. });
  1514. var failure = node('failure', {
  1515. type: type || "unknown"
  1516. });
  1517. failure.appendChild(document.createTextNode(message || "no message left"));
  1518. fnode.appendChild(failure);
  1519. xml.appendChild(fnode);
  1520. };
  1521. /**
  1522. * Retrieves generated XML object - actually an HTMLElement.
  1523. *
  1524. * @return HTMLElement
  1525. */
  1526. this.getXML = function() {
  1527. return xml;
  1528. };
  1529. };
  1530. /**
  1531. * Provides a better typeof operator equivalent, able to retrieve the array
  1532. * type.
  1533. *
  1534. * @param mixed input
  1535. * @return String
  1536. * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
  1537. */
  1538. function betterTypeOf(input) {
  1539. try {
  1540. return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
  1541. } catch (e) {
  1542. return typeof input;
  1543. }
  1544. }
  1545. /**
  1546. * Creates a new WebPage instance for Casper use.
  1547. *
  1548. * @param Casper casper A Casper instance
  1549. * @return WebPage
  1550. */
  1551. function createPage(casper) {
  1552. var page;
  1553. if (phantom.version.major <= 1 && phantom.version.minor < 3 && isType(require, "function")) {
  1554. page = new WebPage();
  1555. } else {
  1556. page = require('webpage').create();
  1557. }
  1558. page.onConsoleMessage = function(msg) {
  1559. var level = "info", test = /^\[casper:(\w+)\]\s?(.*)/.exec(msg);
  1560. if (test && test.length === 3) {
  1561. level = test[1];
  1562. msg = test[2];
  1563. }
  1564. casper.log(msg, level, "remote");
  1565. };
  1566. page.onLoadStarted = function() {
  1567. casper.loadInProgress = true;
  1568. };
  1569. page.onLoadFinished = function(status) {
  1570. if (status !== "success") {
  1571. var message = 'Loading resource failed with status=' + status;
  1572. if (casper.currentHTTPStatus) {
  1573. message += ' (HTTP ' + casper.currentHTTPStatus + ')';
  1574. }
  1575. message += ': ' + casper.requestUrl;
  1576. casper.log(message, "warning");
  1577. if (isType(casper.options.onLoadError, "function")) {
  1578. casper.options.onLoadError(casper, casper.requestUrl, status);
  1579. }
  1580. }
  1581. if (casper.options.clientScripts) {
  1582. if (betterTypeOf(casper.options.clientScripts) !== "array") {
  1583. casper.log("The clientScripts option must be an array", "error");
  1584. } else {
  1585. for (var i = 0; i < casper.options.clientScripts.length; i++) {
  1586. var script = casper.options.clientScripts[i];
  1587. if (casper.page.injectJs(script)) {
  1588. casper.log('Automatically injected ' + script + ' client side', "debug");
  1589. } else {
  1590. casper.log('Failed injecting ' + script + ' client side', "warning");
  1591. }
  1592. }
  1593. }
  1594. }
  1595. // Client utils injection
  1596. var injected = page.evaluate(replaceFunctionPlaceholders(function() {
  1597. eval("var ClientUtils = " + decodeURIComponent("%utils%"));
  1598. __utils__ = new ClientUtils();
  1599. return __utils__ instanceof ClientUtils;
  1600. }, {
  1601. utils: encodeURIComponent(phantom.Casper.ClientUtils.toString())
  1602. }));
  1603. if (!injected) {
  1604. casper.log("Failed to inject Casper client-side utilities!", "warning");
  1605. } else {
  1606. casper.log("Successfully injected Casper client-side utilities", "debug");
  1607. }
  1608. // history
  1609. casper.history.push(casper.getCurrentUrl());
  1610. casper.loadInProgress = false;
  1611. };
  1612. page.onResourceReceived = function(resource) {
  1613. if (resource.url === casper.requestUrl) {
  1614. casper.currentHTTPStatus = resource.status;
  1615. casper.currentUrl = resource.url;
  1616. }
  1617. };
  1618. return page;
  1619. }
  1620. /**
  1621. * Shorthands for checking if a value is of the given type. Can check for
  1622. * arrays.
  1623. *
  1624. * @param mixed what The value to check
  1625. * @param String typeName The type name ("string", "number", "function", etc.)
  1626. * @return Boolean
  1627. */
  1628. function isType(what, typeName) {
  1629. return betterTypeOf(what) === typeName;
  1630. }
  1631. /**
  1632. * Checks if the provided var is a WebPage instance
  1633. *
  1634. * @param mixed what
  1635. * @return Boolean
  1636. */
  1637. function isWebPage(what) {
  1638. if (!what || !isType(what, "object")) {
  1639. return false;
  1640. }
  1641. if (phantom.version.major <= 1 && phantom.version.minor < 3 && isType(require, "function")) {
  1642. return what instanceof WebPage;
  1643. } else {
  1644. return what.toString().indexOf('WebPage(') === 0;
  1645. }
  1646. }
  1647. /**
  1648. * Object recursive merging utility.
  1649. *
  1650. * @param Object obj1 the destination object
  1651. * @param Object obj2 the source object
  1652. * @return Object
  1653. */
  1654. function mergeObjects(obj1, obj2) {
  1655. for (var p in obj2) {
  1656. try {
  1657. if (obj2[p].constructor == Object) {
  1658. obj1[p] = mergeObjects(obj1[p], obj2[p]);
  1659. } else {
  1660. obj1[p] = obj2[p];
  1661. }
  1662. } catch(e) {
  1663. obj1[p] = obj2[p];
  1664. }
  1665. }
  1666. return obj1;
  1667. }
  1668. /**
  1669. * Replaces a function string contents with placeholders provided by an
  1670. * Object.
  1671. *
  1672. * @param Function fn The function
  1673. * @param Object replacements Object containing placeholder replacements
  1674. * @return String A function string representation
  1675. */
  1676. function replaceFunctionPlaceholders(fn, replacements) {
  1677. if (replacements && isType(replacements, "object")) {
  1678. fn = fn.toString();
  1679. for (var placeholder in replacements) {
  1680. var match = '%' + placeholder + '%';
  1681. do {
  1682. fn = fn.replace(match, replacements[placeholder]);
  1683. } while(fn.indexOf(match) !== -1);
  1684. }
  1685. }
  1686. return fn;
  1687. }
  1688. /*! Gibberish-AES
  1689. * A lightweight Javascript Libray for OpenSSL compatible AES CBC encryption.
  1690. *
  1691. * Author: Mark Percival
  1692. * Email: mark@mpercival.com
  1693. * Copyright: Mark Percival - http://mpercival.com 2008
  1694. *
  1695. * With thanks to:
  1696. * Josh Davis - http://www.josh-davis.org/ecmaScrypt
  1697. * Chris Veness - http://www.movable-type.co.uk/scripts/aes.html
  1698. * Michel I. Gallant - http://www.jensign.com/
  1699. *
  1700. * License: MIT
  1701. *
  1702. * Usage: GibberishAES.enc("secret", "password")
  1703. * Outputs: AES Encrypted text encoded in Base64
  1704. */
  1705. var GibberishAES = (function(){
  1706. var Nr = 14,
  1707. /* Default to 256 Bit Encryption */
  1708. Nk = 8,
  1709. Decrypt = false,
  1710. enc_utf8 = function(s)
  1711. {
  1712. try {
  1713. return unescape(encodeURIComponent(s));
  1714. }
  1715. catch(e) {
  1716. throw 'Error on UTF-8 encode';
  1717. }
  1718. },
  1719. dec_utf8 = function(s)
  1720. {
  1721. try {
  1722. return decodeURIComponent(escape(s));
  1723. }
  1724. catch(e) {
  1725. throw ('Bad Key');
  1726. }
  1727. },
  1728. padBlock = function(byteArr)
  1729. {
  1730. var array = [], cpad, i;
  1731. if (byteArr.length < 16) {
  1732. cpad = 16 - byteArr.length;
  1733. array = [cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad, cpad];
  1734. }
  1735. for (i = 0; i < byteArr.length; i++)
  1736. {
  1737. array[i] = byteArr[i];
  1738. }
  1739. return array;
  1740. },
  1741. block2s = function(block, lastBlock)
  1742. {
  1743. var string = '', padding, i;
  1744. if (lastBlock) {
  1745. padding = block[15];
  1746. if (padding > 16) {
  1747. throw ('Decryption error: Maybe bad key');
  1748. }
  1749. if (padding == 16) {
  1750. return '';
  1751. }
  1752. for (i = 0; i < 16 - padding; i++) {
  1753. string += String.fromCharCode(block[i]);
  1754. }
  1755. } else {
  1756. for (i = 0; i < 16; i++) {
  1757. string += String.fromCharCode(block[i]);
  1758. }
  1759. }
  1760. return string;
  1761. },
  1762. a2h = function(numArr)
  1763. {
  1764. var string = '', i;
  1765. for (i = 0; i < numArr.length; i++) {
  1766. string += (numArr[i] < 16 ? '0': '') + numArr[i].toString(16);
  1767. }
  1768. return string;
  1769. },
  1770. h2a = function(s)
  1771. {
  1772. var ret = [];
  1773. s.replace(/(..)/g,
  1774. function(s) {
  1775. ret.push(parseInt(s, 16));
  1776. });
  1777. return ret;
  1778. },
  1779. s2a = function(string) {
  1780. string = enc_utf8(string);
  1781. var array = [], i;
  1782. for (i = 0; i < string.length; i++)
  1783. {
  1784. array[i] = string.charCodeAt(i);
  1785. }
  1786. return array;
  1787. },
  1788. size = function(newsize)
  1789. {
  1790. switch (newsize)
  1791. {
  1792. case 128:
  1793. Nr = 10;
  1794. Nk = 4;
  1795. break;
  1796. case 192:
  1797. Nr = 12;
  1798. Nk = 6;
  1799. break;
  1800. case 256:
  1801. Nr = 14;
  1802. Nk = 8;
  1803. break;
  1804. default:
  1805. throw ('Invalid Key Size Specified:' + newsize);
  1806. }
  1807. },
  1808. randArr = function(num) {
  1809. var result = [], i;
  1810. for (i = 0; i < num; i++) {
  1811. result = result.concat(Math.floor(Math.random() * 256));
  1812. }
  1813. return result;
  1814. },
  1815. openSSLKey = function(passwordArr, saltArr) {
  1816. // Number of rounds depends on the size of the AES in use
  1817. // 3 rounds for 256
  1818. // 2 rounds for the key, 1 for the IV
  1819. // 2 rounds for 128
  1820. // 1 round for the key, 1 round for the IV
  1821. // 3 rounds for 192 since it's not evenly divided by 128 bits
  1822. var rounds = Nr >= 12 ? 3: 2,
  1823. key = [],
  1824. iv = [],
  1825. md5_hash = [],
  1826. result = [],
  1827. data00 = passwordArr.concat(saltArr),
  1828. i;
  1829. md5_hash[0] = GibberishAES.Hash.MD5(data00);
  1830. result = md5_hash[0];
  1831. for (i = 1; i < rounds; i++) {
  1832. md5_hash[i] = GibberishAES.Hash.MD5(md5_hash[i - 1].concat(data00));
  1833. result = result.concat(md5_hash[i]);
  1834. }
  1835. key = result.slice(0, 4 * Nk);
  1836. iv = result.slice(4 * Nk, 4 * Nk + 16);
  1837. return {
  1838. key: key,
  1839. iv: iv
  1840. };
  1841. },
  1842. rawEncrypt = function(plaintext, key, iv) {
  1843. // plaintext, key and iv as byte arrays
  1844. key = expandKey(key);
  1845. var numBlocks = Math.ceil(plaintext.length / 16),
  1846. blocks = [],
  1847. i,
  1848. cipherBlocks = [];
  1849. for (i = 0; i < numBlocks; i++) {
  1850. blocks[i] = padBlock(plaintext.slice(i * 16, i * 16 + 16));
  1851. }
  1852. if (plaintext.length % 16 === 0) {
  1853. blocks.push([16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16]);
  1854. // CBC OpenSSL padding scheme
  1855. numBlocks++;
  1856. }
  1857. for (i = 0; i < blocks.length; i++) {
  1858. blocks[i] = (i === 0) ? xorBlocks(blocks[i], iv) : xorBlocks(blocks[i], cipherBlocks[i - 1]);
  1859. cipherBlocks[i] = encryptBlock(blocks[i], key);
  1860. }
  1861. return cipherBlocks;
  1862. },
  1863. rawDecrypt = function(cryptArr, key, iv, binary) {
  1864. // cryptArr, key and iv as byte arrays
  1865. key = expandKey(key);
  1866. var numBlocks = cryptArr.length / 16,
  1867. cipherBlocks = [],
  1868. i,
  1869. plainBlocks = [],
  1870. string = '';
  1871. for (i = 0; i < numBlocks; i++) {
  1872. cipherBlocks.push(cryptArr.slice(i * 16, (i + 1) * 16));
  1873. }
  1874. for (i = cipherBlocks.length - 1; i >= 0; i--) {
  1875. plainBlocks[i] = decryptBlock(cipherBlocks[i], key);
  1876. plainBlocks[i] = (i === 0) ? xorBlocks(plainBlocks[i], iv) : xorBlocks(plainBlocks[i], cipherBlocks[i - 1]);
  1877. }
  1878. for (i = 0; i < numBlocks - 1; i++) {
  1879. string += block2s(plainBlocks[i]);
  1880. }
  1881. string += block2s(plainBlocks[i], true);
  1882. return binary ? string : dec_utf8(string);
  1883. },
  1884. encryptBlock = function(block, words) {
  1885. Decrypt = false;
  1886. var state = addRoundKey(block, words, 0),
  1887. round;
  1888. for (round = 1; round < (Nr + 1); round++) {
  1889. state = subBytes(state);
  1890. state = shiftRows(state);
  1891. if (round < Nr) {
  1892. state = mixColumns(state);
  1893. }
  1894. //last round? don't mixColumns
  1895. state = addRoundKey(state, words, round);
  1896. }
  1897. return state;
  1898. },
  1899. decryptBlock = function(block, words) {
  1900. Decrypt = true;
  1901. var state = addRoundKey(block, words, Nr),
  1902. round;
  1903. for (round = Nr - 1; round > -1; round--) {
  1904. state = shiftRows(state);
  1905. state = subBytes(state);
  1906. state = addRoundKey(state, words, round);
  1907. if (round > 0) {
  1908. state = mixColumns(state);
  1909. }
  1910. //last round? don't mixColumns
  1911. }
  1912. return state;
  1913. },
  1914. subBytes = function(state) {
  1915. var S = Decrypt ? SBoxInv: SBox,
  1916. temp = [],
  1917. i;
  1918. for (i = 0; i < 16; i++) {
  1919. temp[i] = S[state[i]];
  1920. }
  1921. return temp;
  1922. },
  1923. shiftRows = function(state) {
  1924. var temp = [],
  1925. shiftBy = Decrypt ? [0, 13, 10, 7, 4, 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3] : [0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11],
  1926. i;
  1927. for (i = 0; i < 16; i++) {
  1928. temp[i] = state[shiftBy[i]];
  1929. }
  1930. return temp;
  1931. },
  1932. mixColumns = function(state) {
  1933. var t = [],
  1934. c;
  1935. if (!Decrypt) {
  1936. for (c = 0; c < 4; c++) {
  1937. t[c * 4] = G2X[state[c * 4]] ^ G3X[state[1 + c * 4]] ^ state[2 + c * 4] ^ state[3 + c * 4];
  1938. t[1 + c * 4] = state[c * 4] ^ G2X[state[1 + c * 4]] ^ G3X[state[2 + c * 4]] ^ state[3 + c * 4];
  1939. t[2 + c * 4] = state[c * 4] ^ state[1 + c * 4] ^ G2X[state[2 + c * 4]] ^ G3X[state[3 + c * 4]];
  1940. t[3 + c * 4] = G3X[state[c * 4]] ^ state[1 + c * 4] ^ state[2 + c * 4] ^ G2X[state[3 + c * 4]];
  1941. }
  1942. }else {
  1943. for (c = 0; c < 4; c++) {
  1944. t[c*4] = GEX[state[c*4]] ^ GBX[state[1+c*4]] ^ GDX[state[2+c*4]] ^ G9X[state[3+c*4]];
  1945. t[1+c*4] = G9X[state[c*4]] ^ GEX[state[1+c*4]] ^ GBX[state[2+c*4]] ^ GDX[state[3+c*4]];
  1946. t[2+c*4] = GDX[state[c*4]] ^ G9X[state[1+c*4]] ^ GEX[state[2+c*4]] ^ GBX[state[3+c*4]];
  1947. t[3+c*4] = GBX[state[c*4]] ^ GDX[state[1+c*4]] ^ G9X[state[2+c*4]] ^ GEX[state[3+c*4]];
  1948. }
  1949. }
  1950. return t;
  1951. },
  1952. addRoundKey = function(state, words, round) {
  1953. var temp = [],
  1954. i;
  1955. for (i = 0; i < 16; i++) {
  1956. temp[i] = state[i] ^ words[round][i];
  1957. }
  1958. return temp;
  1959. },
  1960. xorBlocks = function(block1, block2) {
  1961. var temp = [],
  1962. i;
  1963. for (i = 0; i < 16; i++) {
  1964. temp[i] = block1[i] ^ block2[i];
  1965. }
  1966. return temp;
  1967. },
  1968. expandKey = function(key) {
  1969. // Expects a 1d number array
  1970. var w = [],
  1971. temp = [],
  1972. i,
  1973. r,
  1974. t,
  1975. flat = [],
  1976. j;
  1977. for (i = 0; i < Nk; i++) {
  1978. r = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]];
  1979. w[i] = r;
  1980. }
  1981. for (i = Nk; i < (4 * (Nr + 1)); i++) {
  1982. w[i] = [];
  1983. for (t = 0; t < 4; t++) {
  1984. temp[t] = w[i - 1][t];
  1985. }
  1986. if (i % Nk === 0) {
  1987. temp = subWord(rotWord(temp));
  1988. temp[0] ^= Rcon[i / Nk - 1];
  1989. } else if (Nk > 6 && i % Nk == 4) {
  1990. temp = subWord(temp);
  1991. }
  1992. for (t = 0; t < 4; t++) {
  1993. w[i][t] = w[i - Nk][t] ^ temp[t];
  1994. }
  1995. }
  1996. for (i = 0; i < (Nr + 1); i++) {
  1997. flat[i] = [];
  1998. for (j = 0; j < 4; j++) {
  1999. flat[i].push(w[i * 4 + j][0], w[i * 4 + j][1], w[i * 4 + j][2], w[i * 4 + j][3]);
  2000. }
  2001. }
  2002. return flat;
  2003. },
  2004. subWord = function(w) {
  2005. // apply SBox to 4-byte word w
  2006. for (var i = 0; i < 4; i++) {
  2007. w[i] = SBox[w[i]];
  2008. }
  2009. return w;
  2010. },
  2011. rotWord = function(w) {
  2012. // rotate 4-byte word w left by one byte
  2013. var tmp = w[0],
  2014. i;
  2015. for (i = 0; i < 4; i++) {
  2016. w[i] = w[i + 1];
  2017. }
  2018. w[3] = tmp;
  2019. return w;
  2020. },
  2021. // S-box
  2022. SBox = [
  2023. 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171,
  2024. 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164,
  2025. 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113,
  2026. 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226,
  2027. 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214,
  2028. 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203,
  2029. 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69,
  2030. 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245,
  2031. 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68,
  2032. 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42,
  2033. 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73,
  2034. 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109,
  2035. 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37,
  2036. 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62,
  2037. 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225,
  2038. 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
  2039. 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187,
  2040. 22],
  2041. // Precomputed lookup table for the inverse SBox
  2042. SBoxInv = [
  2043. 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215,
  2044. 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222,
  2045. 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66,
  2046. 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73,
  2047. 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92,
  2048. 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21,
  2049. 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247,
  2050. 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2,
  2051. 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220,
  2052. 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173,
  2053. 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29,
  2054. 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75,
  2055. 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168,
  2056. 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81,
  2057. 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160,
  2058. 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97,
  2059. 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12,
  2060. 125],
  2061. // Rijndael Rcon
  2062. Rcon = [1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47, 94,
  2063. 188, 99, 198, 151, 53, 106, 212, 179, 125, 250, 239, 197, 145],
  2064. G2X = [
  2065. 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16,
  2066. 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e,
  2067. 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e, 0x40, 0x42, 0x44, 0x46,
  2068. 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
  2069. 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76,
  2070. 0x78, 0x7a, 0x7c, 0x7e, 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e,
  2071. 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa4, 0xa6,
  2072. 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
  2073. 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6,
  2074. 0xd8, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee,
  2075. 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe, 0x1b, 0x19, 0x1f, 0x1d,
  2076. 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
  2077. 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d,
  2078. 0x23, 0x21, 0x27, 0x25, 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55,
  2079. 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45, 0x7b, 0x79, 0x7f, 0x7d,
  2080. 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
  2081. 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d,
  2082. 0x83, 0x81, 0x87, 0x85, 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5,
  2083. 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5, 0xdb, 0xd9, 0xdf, 0xdd,
  2084. 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
  2085. 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed,
  2086. 0xe3, 0xe1, 0xe7, 0xe5
  2087. ],
  2088. G3X = [
  2089. 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d,
  2090. 0x14, 0x17, 0x12, 0x11, 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39,
  2091. 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21, 0x60, 0x63, 0x66, 0x65,
  2092. 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
  2093. 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d,
  2094. 0x44, 0x47, 0x42, 0x41, 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9,
  2095. 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1, 0xf0, 0xf3, 0xf6, 0xf5,
  2096. 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
  2097. 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd,
  2098. 0xb4, 0xb7, 0xb2, 0xb1, 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99,
  2099. 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81, 0x9b, 0x98, 0x9d, 0x9e,
  2100. 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
  2101. 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6,
  2102. 0xbf, 0xbc, 0xb9, 0xba, 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2,
  2103. 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea, 0xcb, 0xc8, 0xcd, 0xce,
  2104. 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
  2105. 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46,
  2106. 0x4f, 0x4c, 0x49, 0x4a, 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62,
  2107. 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a, 0x3b, 0x38, 0x3d, 0x3e,
  2108. 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
  2109. 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16,
  2110. 0x1f, 0x1c, 0x19, 0x1a
  2111. ],
  2112. G9X = [
  2113. 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
  2114. 0x6c, 0x65, 0x7e, 0x77, 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf,
  2115. 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7, 0x3b, 0x32, 0x29, 0x20,
  2116. 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
  2117. 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8,
  2118. 0xc7, 0xce, 0xd5, 0xdc, 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49,
  2119. 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01, 0xe6, 0xef, 0xf4, 0xfd,
  2120. 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
  2121. 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e,
  2122. 0x21, 0x28, 0x33, 0x3a, 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2,
  2123. 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa, 0xec, 0xe5, 0xfe, 0xf7,
  2124. 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
  2125. 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f,
  2126. 0x10, 0x19, 0x02, 0x0b, 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8,
  2127. 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0, 0x47, 0x4e, 0x55, 0x5c,
  2128. 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
  2129. 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9,
  2130. 0xf6, 0xff, 0xe4, 0xed, 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35,
  2131. 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d, 0xa1, 0xa8, 0xb3, 0xba,
  2132. 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
  2133. 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62,
  2134. 0x5d, 0x54, 0x4f, 0x46
  2135. ],
  2136. GBX = [
  2137. 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45,
  2138. 0x74, 0x7f, 0x62, 0x69, 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81,
  2139. 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9, 0x7b, 0x70, 0x6d, 0x66,
  2140. 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
  2141. 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e,
  2142. 0xbf, 0xb4, 0xa9, 0xa2, 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7,
  2143. 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f, 0x46, 0x4d, 0x50, 0x5b,
  2144. 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
  2145. 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8,
  2146. 0xf9, 0xf2, 0xef, 0xe4, 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c,
  2147. 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54, 0xf7, 0xfc, 0xe1, 0xea,
  2148. 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
  2149. 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02,
  2150. 0x33, 0x38, 0x25, 0x2e, 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd,
  2151. 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5, 0x3c, 0x37, 0x2a, 0x21,
  2152. 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
  2153. 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44,
  2154. 0x75, 0x7e, 0x63, 0x68, 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80,
  2155. 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8, 0x7a, 0x71, 0x6c, 0x67,
  2156. 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
  2157. 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f,
  2158. 0xbe, 0xb5, 0xa8, 0xa3
  2159. ],
  2160. GDX = [
  2161. 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f,
  2162. 0x5c, 0x51, 0x46, 0x4b, 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3,
  2163. 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b, 0xbb, 0xb6, 0xa1, 0xac,
  2164. 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
  2165. 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14,
  2166. 0x37, 0x3a, 0x2d, 0x20, 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e,
  2167. 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26, 0xbd, 0xb0, 0xa7, 0xaa,
  2168. 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
  2169. 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9,
  2170. 0x8a, 0x87, 0x90, 0x9d, 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25,
  2171. 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d, 0xda, 0xd7, 0xc0, 0xcd,
  2172. 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
  2173. 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75,
  2174. 0x56, 0x5b, 0x4c, 0x41, 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42,
  2175. 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a, 0xb1, 0xbc, 0xab, 0xa6,
  2176. 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
  2177. 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8,
  2178. 0xeb, 0xe6, 0xf1, 0xfc, 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44,
  2179. 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c, 0x0c, 0x01, 0x16, 0x1b,
  2180. 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
  2181. 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3,
  2182. 0x80, 0x8d, 0x9a, 0x97
  2183. ],
  2184. GEX = [
  2185. 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62,
  2186. 0x48, 0x46, 0x54, 0x5a, 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca,
  2187. 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba, 0xdb, 0xd5, 0xc7, 0xc9,
  2188. 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
  2189. 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59,
  2190. 0x73, 0x7d, 0x6f, 0x61, 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87,
  2191. 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7, 0x4d, 0x43, 0x51, 0x5f,
  2192. 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
  2193. 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14,
  2194. 0x3e, 0x30, 0x22, 0x2c, 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc,
  2195. 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc, 0x41, 0x4f, 0x5d, 0x53,
  2196. 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
  2197. 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3,
  2198. 0xe9, 0xe7, 0xf5, 0xfb, 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0,
  2199. 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0, 0x7a, 0x74, 0x66, 0x68,
  2200. 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
  2201. 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e,
  2202. 0xa4, 0xaa, 0xb8, 0xb6, 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26,
  2203. 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56, 0x37, 0x39, 0x2b, 0x25,
  2204. 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
  2205. 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5,
  2206. 0x9f, 0x91, 0x83, 0x8d
  2207. ],
  2208. enc = function(string, pass, binary) {
  2209. // string, password in plaintext
  2210. var salt = randArr(8),
  2211. pbe = openSSLKey(s2a(pass), salt),
  2212. key = pbe.key,
  2213. iv = pbe.iv,
  2214. cipherBlocks,
  2215. saltBlock = [[83, 97, 108, 116, 101, 100, 95, 95].concat(salt)];
  2216. if (!binary) {
  2217. string = s2a(string);
  2218. }
  2219. cipherBlocks = rawEncrypt(string, key, iv);
  2220. // Spells out 'Salted__'
  2221. cipherBlocks = saltBlock.concat(cipherBlocks);
  2222. return Base64.encode(cipherBlocks);
  2223. },
  2224. dec = function(string, pass, binary) {
  2225. // string, password in plaintext
  2226. var cryptArr = Base64.decode(string),
  2227. salt = cryptArr.slice(8, 16),
  2228. pbe = openSSLKey(s2a(pass), salt),
  2229. key = pbe.key,
  2230. iv = pbe.iv;
  2231. cryptArr = cryptArr.slice(16, cryptArr.length);
  2232. // Take off the Salted__ffeeddcc
  2233. string = rawDecrypt(cryptArr, key, iv, binary);
  2234. return string;
  2235. },
  2236. MD5 = function(numArr) {
  2237. function rotateLeft(lValue, iShiftBits) {
  2238. return (lValue << iShiftBits) | (lValue >>> (32 - iShiftBits));
  2239. }
  2240. function addUnsigned(lX, lY) {
  2241. var lX4,
  2242. lY4,
  2243. lX8,
  2244. lY8,
  2245. lResult;
  2246. lX8 = (lX & 0x80000000);
  2247. lY8 = (lY & 0x80000000);
  2248. lX4 = (lX & 0x40000000);
  2249. lY4 = (lY & 0x40000000);
  2250. lResult = (lX & 0x3FFFFFFF) + (lY & 0x3FFFFFFF);
  2251. if (lX4 & lY4) {
  2252. return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
  2253. }
  2254. if (lX4 | lY4) {
  2255. if (lResult & 0x40000000) {
  2256. return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
  2257. } else {
  2258. return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
  2259. }
  2260. } else {
  2261. return (lResult ^ lX8 ^ lY8);
  2262. }
  2263. }
  2264. function f(x, y, z) {
  2265. return (x & y) | ((~x) & z);
  2266. }
  2267. function g(x, y, z) {
  2268. return (x & z) | (y & (~z));
  2269. }
  2270. function h(x, y, z) {
  2271. return (x ^ y ^ z);
  2272. }
  2273. function funcI(x, y, z) {
  2274. return (y ^ (x | (~z)));
  2275. }
  2276. function ff(a, b, c, d, x, s, ac) {
  2277. a = addUnsigned(a, addUnsigned(addUnsigned(f(b, c, d), x), ac));
  2278. return addUnsigned(rotateLeft(a, s), b);
  2279. }
  2280. function gg(a, b, c, d, x, s, ac) {
  2281. a = addUnsigned(a, addUnsigned(addUnsigned(g(b, c, d), x), ac));
  2282. return addUnsigned(rotateLeft(a, s), b);
  2283. }
  2284. function hh(a, b, c, d, x, s, ac) {
  2285. a = addUnsigned(a, addUnsigned(addUnsigned(h(b, c, d), x), ac));
  2286. return addUnsigned(rotateLeft(a, s), b);
  2287. }
  2288. function ii(a, b, c, d, x, s, ac) {
  2289. a = addUnsigned(a, addUnsigned(addUnsigned(funcI(b, c, d), x), ac));
  2290. return addUnsigned(rotateLeft(a, s), b);
  2291. }
  2292. function convertToWordArray(numArr) {
  2293. var lWordCount,
  2294. lMessageLength = numArr.length,
  2295. lNumberOfWords_temp1 = lMessageLength + 8,
  2296. lNumberOfWords_temp2 = (lNumberOfWords_temp1 - (lNumberOfWords_temp1 % 64)) / 64,
  2297. lNumberOfWords = (lNumberOfWords_temp2 + 1) * 16,
  2298. lWordArray = [],
  2299. lBytePosition = 0,
  2300. lByteCount = 0;
  2301. while (lByteCount < lMessageLength) {
  2302. lWordCount = (lByteCount - (lByteCount % 4)) / 4;
  2303. lBytePosition = (lByteCount % 4) * 8;
  2304. lWordArray[lWordCount] = (lWordArray[lWordCount] | (numArr[lByteCount] << lBytePosition));
  2305. lByteCount++;
  2306. }
  2307. lWordCount = (lByteCount - (lByteCount % 4)) / 4;
  2308. lBytePosition = (lByteCount % 4) * 8;
  2309. lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80 << lBytePosition);
  2310. lWordArray[lNumberOfWords - 2] = lMessageLength << 3;
  2311. lWordArray[lNumberOfWords - 1] = lMessageLength >>> 29;
  2312. return lWordArray;
  2313. }
  2314. function wordToHex(lValue) {
  2315. var lByte,
  2316. lCount,
  2317. wordToHexArr = [];
  2318. for (lCount = 0; lCount <= 3; lCount++) {
  2319. lByte = (lValue >>> (lCount * 8)) & 255;
  2320. wordToHexArr = wordToHexArr.concat(lByte);
  2321. }
  2322. return wordToHexArr;
  2323. }
  2324. /*function utf8Encode(string) {
  2325. string = string.replace(/\r\n/g, "\n");
  2326. var utftext = "",
  2327. n,
  2328. c;
  2329. for (n = 0; n < string.length; n++) {
  2330. c = string.charCodeAt(n);
  2331. if (c < 128) {
  2332. utftext += String.fromCharCode(c);
  2333. }
  2334. else if ((c > 127) && (c < 2048)) {
  2335. utftext += String.fromCharCode((c >> 6) | 192);
  2336. utftext += String.fromCharCode((c & 63) | 128);
  2337. }
  2338. else {
  2339. utftext += String.fromCharCode((c >> 12) | 224);
  2340. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  2341. utftext += String.fromCharCode((c & 63) | 128);
  2342. }
  2343. }
  2344. return utftext;
  2345. }*/
  2346. var x = [],
  2347. k,
  2348. AA,
  2349. BB,
  2350. CC,
  2351. DD,
  2352. a,
  2353. b,
  2354. c,
  2355. d,
  2356. S11 = 7,
  2357. S12 = 12,
  2358. S13 = 17,
  2359. S14 = 22,
  2360. S21 = 5,
  2361. S22 = 9,
  2362. S23 = 14,
  2363. S24 = 20,
  2364. S31 = 4,
  2365. S32 = 11,
  2366. S33 = 16,
  2367. S34 = 23,
  2368. S41 = 6,
  2369. S42 = 10,
  2370. S43 = 15,
  2371. S44 = 21;
  2372. x = convertToWordArray(numArr);
  2373. a = 0x67452301;
  2374. b = 0xEFCDAB89;
  2375. c = 0x98BADCFE;
  2376. d = 0x10325476;
  2377. for (k = 0; k < x.length; k += 16) {
  2378. AA = a;
  2379. BB = b;
  2380. CC = c;
  2381. DD = d;
  2382. a = ff(a, b, c, d, x[k + 0], S11, 0xD76AA478);
  2383. d = ff(d, a, b, c, x[k + 1], S12, 0xE8C7B756);
  2384. c = ff(c, d, a, b, x[k + 2], S13, 0x242070DB);
  2385. b = ff(b, c, d, a, x[k + 3], S14, 0xC1BDCEEE);
  2386. a = ff(a, b, c, d, x[k + 4], S11, 0xF57C0FAF);
  2387. d = ff(d, a, b, c, x[k + 5], S12, 0x4787C62A);
  2388. c = ff(c, d, a, b, x[k + 6], S13, 0xA8304613);
  2389. b = ff(b, c, d, a, x[k + 7], S14, 0xFD469501);
  2390. a = ff(a, b, c, d, x[k + 8], S11, 0x698098D8);
  2391. d = ff(d, a, b, c, x[k + 9], S12, 0x8B44F7AF);
  2392. c = ff(c, d, a, b, x[k + 10], S13, 0xFFFF5BB1);
  2393. b = ff(b, c, d, a, x[k + 11], S14, 0x895CD7BE);
  2394. a = ff(a, b, c, d, x[k + 12], S11, 0x6B901122);
  2395. d = ff(d, a, b, c, x[k + 13], S12, 0xFD987193);
  2396. c = ff(c, d, a, b, x[k + 14], S13, 0xA679438E);
  2397. b = ff(b, c, d, a, x[k + 15], S14, 0x49B40821);
  2398. a = gg(a, b, c, d, x[k + 1], S21, 0xF61E2562);
  2399. d = gg(d, a, b, c, x[k + 6], S22, 0xC040B340);
  2400. c = gg(c, d, a, b, x[k + 11], S23, 0x265E5A51);
  2401. b = gg(b, c, d, a, x[k + 0], S24, 0xE9B6C7AA);
  2402. a = gg(a, b, c, d, x[k + 5], S21, 0xD62F105D);
  2403. d = gg(d, a, b, c, x[k + 10], S22, 0x2441453);
  2404. c = gg(c, d, a, b, x[k + 15], S23, 0xD8A1E681);
  2405. b = gg(b, c, d, a, x[k + 4], S24, 0xE7D3FBC8);
  2406. a = gg(a, b, c, d, x[k + 9], S21, 0x21E1CDE6);
  2407. d = gg(d, a, b, c, x[k + 14], S22, 0xC33707D6);
  2408. c = gg(c, d, a, b, x[k + 3], S23, 0xF4D50D87);
  2409. b = gg(b, c, d, a, x[k + 8], S24, 0x455A14ED);
  2410. a = gg(a, b, c, d, x[k + 13], S21, 0xA9E3E905);
  2411. d = gg(d, a, b, c, x[k + 2], S22, 0xFCEFA3F8);
  2412. c = gg(c, d, a, b, x[k + 7], S23, 0x676F02D9);
  2413. b = gg(b, c, d, a, x[k + 12], S24, 0x8D2A4C8A);
  2414. a = hh(a, b, c, d, x[k + 5], S31, 0xFFFA3942);
  2415. d = hh(d, a, b, c, x[k + 8], S32, 0x8771F681);
  2416. c = hh(c, d, a, b, x[k + 11], S33, 0x6D9D6122);
  2417. b = hh(b, c, d, a, x[k + 14], S34, 0xFDE5380C);
  2418. a = hh(a, b, c, d, x[k + 1], S31, 0xA4BEEA44);
  2419. d = hh(d, a, b, c, x[k + 4], S32, 0x4BDECFA9);
  2420. c = hh(c, d, a, b, x[k + 7], S33, 0xF6BB4B60);
  2421. b = hh(b, c, d, a, x[k + 10], S34, 0xBEBFBC70);
  2422. a = hh(a, b, c, d, x[k + 13], S31, 0x289B7EC6);
  2423. d = hh(d, a, b, c, x[k + 0], S32, 0xEAA127FA);
  2424. c = hh(c, d, a, b, x[k + 3], S33, 0xD4EF3085);
  2425. b = hh(b, c, d, a, x[k + 6], S34, 0x4881D05);
  2426. a = hh(a, b, c, d, x[k + 9], S31, 0xD9D4D039);
  2427. d = hh(d, a, b, c, x[k + 12], S32, 0xE6DB99E5);
  2428. c = hh(c, d, a, b, x[k + 15], S33, 0x1FA27CF8);
  2429. b = hh(b, c, d, a, x[k + 2], S34, 0xC4AC5665);
  2430. a = ii(a, b, c, d, x[k + 0], S41, 0xF4292244);
  2431. d = ii(d, a, b, c, x[k + 7], S42, 0x432AFF97);
  2432. c = ii(c, d, a, b, x[k + 14], S43, 0xAB9423A7);
  2433. b = ii(b, c, d, a, x[k + 5], S44, 0xFC93A039);
  2434. a = ii(a, b, c, d, x[k + 12], S41, 0x655B59C3);
  2435. d = ii(d, a, b, c, x[k + 3], S42, 0x8F0CCC92);
  2436. c = ii(c, d, a, b, x[k + 10], S43, 0xFFEFF47D);
  2437. b = ii(b, c, d, a, x[k + 1], S44, 0x85845DD1);
  2438. a = ii(a, b, c, d, x[k + 8], S41, 0x6FA87E4F);
  2439. d = ii(d, a, b, c, x[k + 15], S42, 0xFE2CE6E0);
  2440. c = ii(c, d, a, b, x[k + 6], S43, 0xA3014314);
  2441. b = ii(b, c, d, a, x[k + 13], S44, 0x4E0811A1);
  2442. a = ii(a, b, c, d, x[k + 4], S41, 0xF7537E82);
  2443. d = ii(d, a, b, c, x[k + 11], S42, 0xBD3AF235);
  2444. c = ii(c, d, a, b, x[k + 2], S43, 0x2AD7D2BB);
  2445. b = ii(b, c, d, a, x[k + 9], S44, 0xEB86D391);
  2446. a = addUnsigned(a, AA);
  2447. b = addUnsigned(b, BB);
  2448. c = addUnsigned(c, CC);
  2449. d = addUnsigned(d, DD);
  2450. }
  2451. return wordToHex(a).concat(wordToHex(b), wordToHex(c), wordToHex(d));
  2452. },
  2453. Base64 = (function(){
  2454. // Takes a Nx16x1 byte array and converts it to Base64
  2455. var _chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
  2456. chars = _chars.split(''),
  2457. encode = function(b, withBreaks) {
  2458. var flatArr = [],
  2459. b64 = '',
  2460. i,
  2461. broken_b64;
  2462. totalChunks = Math.floor(b.length * 16 / 3);
  2463. for (i = 0; i < b.length * 16; i++) {
  2464. flatArr.push(b[Math.floor(i / 16)][i % 16]);
  2465. }
  2466. for (i = 0; i < flatArr.length; i = i + 3) {
  2467. b64 += chars[flatArr[i] >> 2];
  2468. b64 += chars[((flatArr[i] & 3) << 4) | (flatArr[i + 1] >> 4)];
  2469. if (! (flatArr[i + 1] === undefined)) {
  2470. b64 += chars[((flatArr[i + 1] & 15) << 2) | (flatArr[i + 2] >> 6)];
  2471. } else {
  2472. b64 += '=';
  2473. }
  2474. if (! (flatArr[i + 2] === undefined)) {
  2475. b64 += chars[flatArr[i + 2] & 63];
  2476. } else {
  2477. b64 += '=';
  2478. }
  2479. }
  2480. // OpenSSL is super particular about line breaks
  2481. broken_b64 = b64.slice(0, 64) + '\n';
  2482. for (i = 1; i < (Math.ceil(b64.length / 64)); i++) {
  2483. broken_b64 += b64.slice(i * 64, i * 64 + 64) + (Math.ceil(b64.length / 64) == i + 1 ? '': '\n');
  2484. }
  2485. return broken_b64;
  2486. },
  2487. decode = function(string) {
  2488. string = string.replace(/\n/g, '');
  2489. var flatArr = [],
  2490. c = [],
  2491. b = [],
  2492. i;
  2493. for (i = 0; i < string.length; i = i + 4) {
  2494. c[0] = _chars.indexOf(string.charAt(i));
  2495. c[1] = _chars.indexOf(string.charAt(i + 1));
  2496. c[2] = _chars.indexOf(string.charAt(i + 2));
  2497. c[3] = _chars.indexOf(string.charAt(i + 3));
  2498. b[0] = (c[0] << 2) | (c[1] >> 4);
  2499. b[1] = ((c[1] & 15) << 4) | (c[2] >> 2);
  2500. b[2] = ((c[2] & 3) << 6) | c[3];
  2501. flatArr.push(b[0], b[1], b[2]);
  2502. }
  2503. flatArr = flatArr.slice(0, flatArr.length - (flatArr.length % 16));
  2504. return flatArr;
  2505. };
  2506. //internet explorer
  2507. if(typeof Array.indexOf === "function") {
  2508. _chars = chars;
  2509. }
  2510. /*
  2511. //other way to solve internet explorer problem
  2512. if(!Array.indexOf){
  2513. Array.prototype.indexOf = function(obj){
  2514. for(var i=0; i<this.length; i++){
  2515. if(this[i]===obj){
  2516. return i;
  2517. }
  2518. }
  2519. return -1;
  2520. }
  2521. }
  2522. */
  2523. return {
  2524. "encode": encode,
  2525. "decode": decode
  2526. };
  2527. })();
  2528. return {
  2529. "size": size,
  2530. "h2a":h2a,
  2531. "expandKey":expandKey,
  2532. "encryptBlock":encryptBlock,
  2533. "decryptBlock":decryptBlock,
  2534. "Decrypt":Decrypt,
  2535. "s2a":s2a,
  2536. "rawEncrypt":rawEncrypt,
  2537. "dec":dec,
  2538. "openSSLKey":openSSLKey,
  2539. "a2h":a2h,
  2540. "enc":enc,
  2541. "Hash":{"MD5":MD5},
  2542. "Base64":Base64
  2543. };
  2544. })();
  2545. })(phantom);