Patched to Drupal 8.4.8 level. See https://www.drupal.org/sa-core-2018-004 and patch...
[yaffs-website] / vendor / jcalderonzumba / gastonjs / src / Client / browser.js
1 var __indexOf = [].indexOf || function (item) {
2     for (var i = 0, l = this.length; i < l; i++) {
3       if (i in this && this[i] === item) return i;
4     }
5     return -1;
6   };
7
8 var xpathStringLiteral = function (s) {
9   if (s.indexOf('"') === -1)
10     return '"' + s + '"';
11   if (s.indexOf("'") === -1)
12     return "'" + s + "'";
13   return 'concat("' + s.replace(/"/g, '",\'"\',"') + '")';
14 };
15
16 Poltergeist.Browser = (function () {
17   /**
18    * Creates the "browser" inside phantomjs
19    * @param owner
20    * @param width
21    * @param height
22    * @param jsErrors
23    * @constructor
24    */
25   function Browser(owner, width, height, jsErrors) {
26     this.owner = owner;
27     this.width = width || 1024;
28     this.height = height || 768;
29     this.pages = [];
30     this.js_errors = (typeof jsErrors === 'boolean') ? jsErrors : true;
31     this._debug = false;
32     this._counter = 0;
33     this.resetPage();
34   }
35
36   /**
37    * Resets the browser to a clean slate
38    * @return {Function}
39    */
40   Browser.prototype.resetPage = function () {
41     var _ref;
42     var self = this;
43
44     _ref = [0, []];
45     this._counter = _ref[0];
46     this.pages = _ref[1];
47
48     if (this.page != null) {
49       if (!this.page.closed) {
50         if (this.page.currentUrl() !== 'about:blank') {
51           this.page.clearLocalStorage();
52         }
53         this.page.release();
54       }
55       phantom.clearCookies();
56     }
57
58     this.page = this.currentPage = new Poltergeist.WebPage;
59     this.page.setViewportSize({
60       width: this.width,
61       height: this.height
62     });
63     this.page.handle = "" + (this._counter++);
64     this.pages.push(this.page);
65
66     return this.page.onPageCreated = function (newPage) {
67       var page;
68       page = new Poltergeist.WebPage(newPage);
69       page.handle = "" + (self._counter++);
70       return self.pages.push(page);
71     };
72   };
73
74   /**
75    * Given a page handle id, tries to get it from the browser page list
76    * @param handle
77    * @return {WebPage}
78    */
79   Browser.prototype.getPageByHandle = function (handle) {
80     var filteredPages;
81
82     //TODO: perhaps we should throw a PageNotFoundByHandle or something like that..
83     if (handle === null || typeof handle == "undefined") {
84       return null;
85     }
86
87     filteredPages = this.pages.filter(function (p) {
88       return !p.closed && p.handle === handle;
89     });
90
91     if (filteredPages.length === 1) {
92       return filteredPages[0];
93     }
94
95     return null;
96   };
97
98   /**
99    * Sends a debug message to the console
100    * @param message
101    * @return {*}
102    */
103   Browser.prototype.debug = function (message) {
104     if (this._debug) {
105       return console.log("poltergeist [" + (new Date().getTime()) + "] " + message);
106     }
107   };
108
109   /**
110    * Given a page_id and id, gets if possible the node in such page
111    * @param page_id
112    * @param id
113    * @return {Poltergeist.Node}
114    */
115   Browser.prototype.node = function (page_id, id) {
116     if (this.currentPage.id === page_id) {
117       return this.currentPage.get(id);
118     } else {
119       throw new Poltergeist.ObsoleteNode;
120     }
121   };
122
123   /**
124    * Returns the frameUrl related to the frame given by name
125    * @param frame_name
126    * @return {*}
127    */
128   Browser.prototype.frameUrl = function (frame_name) {
129     return this.currentPage.frameUrl(frame_name);
130   };
131
132   /**
133    * This method defines the rectangular area of the web page to be rasterized when render is invoked.
134    * If no clipping rectangle is set, render will process the entire web page.
135    * @param full
136    * @param selector
137    * @return {*}
138    */
139   Browser.prototype.set_clip_rect = function (full, selector) {
140     var dimensions, clipDocument, rect, clipViewport;
141
142     dimensions = this.currentPage.validatedDimensions();
143     clipDocument = dimensions.document;
144     clipViewport = dimensions.viewport;
145
146     if (full) {
147       rect = {
148         left: 0,
149         top: 0,
150         width: clipDocument.width,
151         height: clipDocument.height
152       };
153     } else {
154       if (selector != null) {
155         rect = this.currentPage.elementBounds(selector);
156       } else {
157         rect = {
158           left: 0,
159           top: 0,
160           width: clipViewport.width,
161           height: clipViewport.height
162         };
163       }
164     }
165
166     this.currentPage.setClipRect(rect);
167     return dimensions;
168   };
169
170   /**
171    * Kill the browser, i.e kill phantomjs current process
172    * @return {int}
173    */
174   Browser.prototype.exit = function () {
175     return phantom.exit(0);
176   };
177
178   /**
179    * Do nothing
180    */
181   Browser.prototype.noop = function () {
182   };
183
184   /**
185    * Throws a new Object error
186    */
187   Browser.prototype.browser_error = function () {
188     throw new Error('zomg');
189   };
190
191   /**
192    *  Visits a page and load its content
193    * @param serverResponse
194    * @param url
195    * @return {*}
196    */
197   Browser.prototype.visit = function (serverResponse, url) {
198     var prevUrl;
199     var self = this;
200     this.currentPage.state = 'loading';
201     prevUrl = this.currentPage.source === null ? 'about:blank' : this.currentPage.currentUrl();
202     this.currentPage.open(url);
203     if (/#/.test(url) && prevUrl.split('#')[0] === url.split('#')[0]) {
204       this.currentPage.state = 'default';
205       return this.serverSendResponse({
206         status: 'success'
207       }, serverResponse);
208     } else {
209       return this.currentPage.waitState('default', function () {
210         if (self.currentPage.statusCode === null && self.currentPage.status === 'fail') {
211           return self.owner.serverSendError(new Poltergeist.StatusFailError, serverResponse);
212         } else {
213           return self.serverSendResponse({
214             status: self.currentPage.status
215           }, serverResponse);
216         }
217       });
218     }
219   };
220
221   /**
222    *  Puts the control of the browser inside the IFRAME given by name
223    * @param serverResponse
224    * @param name
225    * @param timeout
226    * @return {*}
227    */
228   Browser.prototype.push_frame = function (serverResponse, name, timeout) {
229     var _ref;
230     var self = this;
231
232     if (timeout == null) {
233       timeout = new Date().getTime() + 2000;
234     }
235
236     //TODO: WTF, else if after a if with return COMMON
237     if (_ref = this.frameUrl(name), __indexOf.call(this.currentPage.blockedUrls(), _ref) >= 0) {
238       return this.serverSendResponse(true, serverResponse);
239     } else if (this.currentPage.pushFrame(name)) {
240       if (this.currentPage.currentUrl() === 'about:blank') {
241         this.currentPage.state = 'awaiting_frame_load';
242         return this.currentPage.waitState('default', function () {
243           return self.serverSendResponse(true, serverResponse);
244         });
245       } else {
246         return this.serverSendResponse(true, serverResponse);
247       }
248     } else {
249       if (new Date().getTime() < timeout) {
250         return setTimeout((function () {
251           return self.push_frame(serverResponse, name, timeout);
252         }), 50);
253       } else {
254         return this.owner.serverSendError(new Poltergeist.FrameNotFound(name), serverResponse);
255       }
256     }
257   };
258
259   /**
260    *  Injects a javascript into the current page
261    * @param serverResponse
262    * @param extension
263    * @return {*}
264    */
265   Browser.prototype.add_extension = function (serverResponse, extension) {
266     //TODO: error control when the injection was not possible
267     this.currentPage.injectExtension(extension);
268     return this.serverSendResponse('success', serverResponse);
269   };
270
271   /**
272    *  Returns the url we are currently in
273    * @param serverResponse
274    * @return {*}
275    */
276   Browser.prototype.current_url = function (serverResponse) {
277     return this.serverSendResponse(this.currentPage.currentUrl(), serverResponse);
278   };
279
280   /**
281    *  Returns the current page window name
282    * @param serverResponse
283    * @returns {*}
284    */
285   Browser.prototype.window_name = function (serverResponse) {
286     return this.serverSendResponse(this.currentPage.windowName(), serverResponse);
287   };
288
289   /**
290    *  Returns the status code associated to the page
291    * @param serverResponse
292    * @return {*}
293    */
294   Browser.prototype.status_code = function (serverResponse) {
295     if (this.currentPage.statusCode === undefined || this.currentPage.statusCode === null) {
296       return this.owner.serverSendError(new Poltergeist.StatusFailError("status_code_error"), serverResponse);
297     }
298     return this.serverSendResponse(this.currentPage.statusCode, serverResponse);
299   };
300
301   /**
302    *  Returns the source code of the active frame, useful for when inside an IFRAME
303    * @param serverResponse
304    * @return {*}
305    */
306   Browser.prototype.body = function (serverResponse) {
307     return this.serverSendResponse(this.currentPage.content(), serverResponse);
308   };
309
310   /**
311    * Returns the source code of the page all the html
312    * @param serverResponse
313    * @return {*}
314    */
315   Browser.prototype.source = function (serverResponse) {
316     return this.serverSendResponse(this.currentPage.source, serverResponse);
317   };
318
319   /**
320    * Returns the current page title
321    * @param serverResponse
322    * @return {*}
323    */
324   Browser.prototype.title = function (serverResponse) {
325     return this.serverSendResponse(this.currentPage.title(), serverResponse);
326   };
327
328   /**
329    *  Finds the elements that match a method of selection and a selector
330    * @param serverResponse
331    * @param method
332    * @param selector
333    * @return {*}
334    */
335   Browser.prototype.find = function (serverResponse, method, selector) {
336     return this.serverSendResponse({
337       page_id: this.currentPage.id,
338       ids: this.currentPage.find(method, selector)
339     }, serverResponse);
340   };
341
342   /**
343    * Find elements within a given element
344    * @param serverResponse
345    * @param page_id
346    * @param id
347    * @param method
348    * @param selector
349    * @return {*}
350    */
351   Browser.prototype.find_within = function (serverResponse, page_id, id, method, selector) {
352     return this.serverSendResponse(this.node(page_id, id).find(method, selector), serverResponse);
353   };
354
355   /**
356    * Returns ALL the text, visible and not visible from the given element
357    * @param serverResponse
358    * @param page_id
359    * @param id
360    * @return {*}
361    */
362   Browser.prototype.all_text = function (serverResponse, page_id, id) {
363     return this.serverSendResponse(this.node(page_id, id).allText(), serverResponse);
364   };
365
366   /**
367    * Returns the inner or outer html of a given id
368    * @param serverResponse
369    * @param page_id
370    * @param id
371    * @param type
372    * @returns Object
373    */
374   Browser.prototype.all_html = function (serverResponse, page_id, id, type) {
375     return this.serverSendResponse(this.node(page_id, id).allHTML(type), serverResponse);
376   };
377
378   /**
379    *  Returns only the visible text in a given element
380    * @param serverResponse
381    * @param page_id
382    * @param id
383    * @return {*}
384    */
385   Browser.prototype.visible_text = function (serverResponse, page_id, id) {
386     return this.serverSendResponse(this.node(page_id, id).visibleText(), serverResponse);
387   };
388
389   /**
390    * Deletes the text in a given element leaving it empty
391    * @param serverResponse
392    * @param page_id
393    * @param id
394    * @return {*}
395    */
396   Browser.prototype.delete_text = function (serverResponse, page_id, id) {
397     return this.serverSendResponse(this.node(page_id, id).deleteText(), serverResponse);
398   };
399
400   /**
401    *  Gets the value of a given attribute in an element
402    * @param serverResponse
403    * @param page_id
404    * @param id
405    * @param name
406    * @return {*}
407    */
408   Browser.prototype.attribute = function (serverResponse, page_id, id, name) {
409     return this.serverSendResponse(this.node(page_id, id).getAttribute(name), serverResponse);
410   };
411
412   /**
413    *  Allows the possibility to set an attribute on a given element
414    * @param serverResponse
415    * @param page_id
416    * @param id
417    * @param name
418    * @param value
419    * @returns {*}
420    */
421   Browser.prototype.set_attribute = function (serverResponse, page_id, id, name, value) {
422     return this.serverSendResponse(this.node(page_id, id).setAttribute(name, value), serverResponse);
423   };
424
425   /**
426    *  Allows the possibility to remove an attribute on a given element
427    * @param serverResponse
428    * @param page_id
429    * @param id
430    * @param name
431    * @returns {*}
432    */
433   Browser.prototype.remove_attribute = function (serverResponse, page_id, id, name) {
434     return this.serverSendResponse(this.node(page_id, id).removeAttribute(name), serverResponse);
435   };
436
437   /**
438    * Returns all the attributes of a given element
439    * @param serverResponse
440    * @param page_id
441    * @param id
442    * @param name
443    * @return {*}
444    */
445   Browser.prototype.attributes = function (serverResponse, page_id, id, name) {
446     return this.serverSendResponse(this.node(page_id, id).getAttributes(), serverResponse);
447   };
448
449   /**
450    *  Returns all the way to the document level the parents of a given element
451    * @param serverResponse
452    * @param page_id
453    * @param id
454    * @return {*}
455    */
456   Browser.prototype.parents = function (serverResponse, page_id, id) {
457     return this.serverSendResponse(this.node(page_id, id).parentIds(), serverResponse);
458   };
459
460   /**
461    * Returns the element.value of an element given by its page and id
462    * @param serverResponse
463    * @param page_id
464    * @param id
465    * @return {*}
466    */
467   Browser.prototype.value = function (serverResponse, page_id, id) {
468     return this.serverSendResponse(this.node(page_id, id).value(), serverResponse);
469   };
470
471   /**
472    *  Sets the element.value of an element by the given value
473    * @param serverResponse
474    * @param page_id
475    * @param id
476    * @param value
477    * @return {*}
478    */
479   Browser.prototype.set = function (serverResponse, page_id, id, value) {
480     this.node(page_id, id).set(value);
481     return this.serverSendResponse(true, serverResponse);
482   };
483
484   /**
485    *  Uploads a file to an input file element
486    * @param serverResponse
487    * @param page_id
488    * @param id
489    * @param file_path
490    * @return {*}
491    */
492   Browser.prototype.select_file = function (serverResponse, page_id, id, file_path) {
493     var node = this.node(page_id, id);
494
495     this.currentPage.beforeUpload(node.id);
496     this.currentPage.uploadFile('[_poltergeist_selected]', file_path);
497     this.currentPage.afterUpload(node.id);
498
499     return this.serverSendResponse(true, serverResponse);
500   };
501
502   /**
503    * Sets a value to the selected element (to be used in select elements)
504    * @param serverResponse
505    * @param page_id
506    * @param id
507    * @param value
508    * @return {*}
509    */
510   Browser.prototype.select = function (serverResponse, page_id, id, value) {
511     return this.serverSendResponse(this.node(page_id, id).select(value), serverResponse);
512   };
513
514   /**
515    *  Selects an option with the given value
516    * @param serverResponse
517    * @param page_id
518    * @param id
519    * @param value
520    * @param multiple
521    * @return {*}
522    */
523   Browser.prototype.select_option = function (serverResponse, page_id, id, value, multiple) {
524     return this.serverSendResponse(this.node(page_id, id).select_option(value, multiple), serverResponse);
525   };
526
527   /**
528    *
529    * @param serverResponse
530    * @param page_id
531    * @param id
532    * @return {*}
533    */
534   Browser.prototype.tag_name = function (serverResponse, page_id, id) {
535     return this.serverSendResponse(this.node(page_id, id).tagName(), serverResponse);
536   };
537
538
539   /**
540    * Tells if an element is visible or not
541    * @param serverResponse
542    * @param page_id
543    * @param id
544    * @return {*}
545    */
546   Browser.prototype.visible = function (serverResponse, page_id, id) {
547     return this.serverSendResponse(this.node(page_id, id).isVisible(), serverResponse);
548   };
549
550   /**
551    *  Tells if an element is disabled
552    * @param serverResponse
553    * @param page_id
554    * @param id
555    * @return {*}
556    */
557   Browser.prototype.disabled = function (serverResponse, page_id, id) {
558     return this.serverSendResponse(this.node(page_id, id).isDisabled(), serverResponse);
559   };
560
561   /**
562    *  Evaluates a javascript and returns the outcome to the client
563    *  This will be JSON response so your script better be returning objects that can be used
564    *  in JSON.stringify
565    * @param serverResponse
566    * @param script
567    * @return {*}
568    */
569   Browser.prototype.evaluate = function (serverResponse, script) {
570     return this.serverSendResponse(this.currentPage.evaluate("function() { return " + script + " }"), serverResponse);
571   };
572
573   /**
574    *  Executes a javascript and goes back to the client with true if there were no errors
575    * @param serverResponse
576    * @param script
577    * @return {*}
578    */
579   Browser.prototype.execute = function (serverResponse, script) {
580     this.currentPage.execute("function() { " + script + " }");
581     return this.serverSendResponse(true, serverResponse);
582   };
583
584   /**
585    * If inside a frame then we will go back to the parent
586    * Not defined behaviour if you pop and are not inside an iframe
587    * @param serverResponse
588    * @return {*}
589    */
590   Browser.prototype.pop_frame = function (serverResponse) {
591     return this.serverSendResponse(this.currentPage.popFrame(), serverResponse);
592   };
593
594   /**
595    * Gets the window handle id by a given window name
596    * @param serverResponse
597    * @param name
598    * @return {*}
599    */
600   Browser.prototype.window_handle = function (serverResponse, name) {
601     var handle, pageByWindowName;
602
603     if (name === null || typeof name == "undefined" || name.length === 0) {
604       return this.serverSendResponse(this.currentPage.handle, serverResponse);
605     }
606
607     handle = null;
608
609     //Lets search the handle by the given window name
610     var filteredPages = this.pages.filter(function (p) {
611       return !p.closed && p.windowName() === name;
612     });
613
614     //A bit of error control is always good
615     if (Array.isArray(filteredPages) && filteredPages.length >= 1) {
616       pageByWindowName = filteredPages[0];
617     } else {
618       pageByWindowName = null;
619     }
620
621     if (pageByWindowName !== null && typeof pageByWindowName != "undefined") {
622       handle = pageByWindowName.handle;
623     }
624
625     return this.serverSendResponse(handle, serverResponse);
626   };
627
628   /**
629    * Returns all the window handles of opened windows
630    * @param serverResponse
631    * @return {*}
632    */
633   Browser.prototype.window_handles = function (serverResponse) {
634     var handles, filteredPages;
635
636     filteredPages = this.pages.filter(function (p) {
637       return !p.closed;
638     });
639
640     if (filteredPages.length > 0) {
641       handles = filteredPages.map(function (p) {
642         return p.handle;
643       });
644       if (handles.length === 0) {
645         handles = null;
646       }
647     } else {
648       handles = null;
649     }
650
651     return this.serverSendResponse(handles, serverResponse);
652   };
653
654   /**
655    *  Tries to switch to a window given by the handle id
656    * @param serverResponse
657    * @param handle
658    * @return {*}
659    */
660   Browser.prototype.switch_to_window = function (serverResponse, handle) {
661     var page;
662     var self = this;
663
664     page = this.getPageByHandle(handle);
665     if (page === null || typeof page == "undefined") {
666       throw new Poltergeist.NoSuchWindowError;
667     }
668
669     if (page !== this.currentPage) {
670       return page.waitState('default', function () {
671         self.currentPage = page;
672         return self.serverSendResponse(true, serverResponse);
673       });
674     }
675
676     return this.serverSendResponse(true, serverResponse);
677   };
678
679   /**
680    * Opens a new window where we can do stuff
681    * @param serverResponse
682    * @return {*}
683    */
684   Browser.prototype.open_new_window = function (serverResponse) {
685     return this.execute(serverResponse, 'window.open()');
686   };
687
688   /**
689    * Closes the window given by handle name if possible
690    * @param serverResponse
691    * @param handle
692    * @return {*}
693    */
694   Browser.prototype.close_window = function (serverResponse, handle) {
695     var page;
696
697     page = this.getPageByHandle(handle);
698     if (page === null || typeof  page == "undefined") {
699       //TODO: should we throw error since we actually could not find the window?
700       return this.serverSendResponse(false, serverResponse);
701     }
702
703     //TODO: we have to add some control here to actually asses that the release has been done
704     page.release();
705     return this.serverSendResponse(true, serverResponse);
706   };
707
708   /**
709    * Generic mouse event on an element
710    * @param serverResponse
711    * @param page_id
712    * @param id
713    * @param name
714    * @return {number}
715    */
716   Browser.prototype.mouse_event = function (serverResponse, page_id, id, name) {
717     var node;
718     var self = this;
719     node = this.node(page_id, id);
720     this.currentPage.state = 'mouse_event';
721     this.last_mouse_event = node.mouseEvent(name);
722     return setTimeout(function () {
723       if (self.currentPage.state === 'mouse_event') {
724         self.currentPage.state = 'default';
725         return self.serverSendResponse({
726           position: self.last_mouse_event
727         }, serverResponse);
728       } else {
729         return self.currentPage.waitState('default', function () {
730           return self.serverSendResponse({
731             position: self.last_mouse_event
732           }, serverResponse);
733         });
734       }
735     }, 5);
736   };
737
738   /**
739    * Simple click on the element
740    * @param serverResponse
741    * @param page_id
742    * @param id
743    * @return {*}
744    */
745   Browser.prototype.click = function (serverResponse, page_id, id) {
746     return this.mouse_event(serverResponse, page_id, id, 'click');
747   };
748
749   /**
750    * Right click on the element
751    * @param serverResponse
752    * @param page_id
753    * @param id
754    * @return {*}
755    */
756   Browser.prototype.right_click = function (serverResponse, page_id, id) {
757     return this.mouse_event(serverResponse, page_id, id, 'rightclick');
758   };
759
760   /**
761    *  Double click on the element given by page and id
762    * @param serverResponse
763    * @param page_id
764    * @param id
765    * @return {*}
766    */
767   Browser.prototype.double_click = function (serverResponse, page_id, id) {
768     return this.mouse_event(serverResponse, page_id, id, 'doubleclick');
769   };
770
771   /**
772    * Executes a mousemove event on the page and given element
773    * @param serverResponse
774    * @param page_id
775    * @param id
776    * @return {*}
777    */
778   Browser.prototype.hover = function (serverResponse, page_id, id) {
779     return this.mouse_event(serverResponse, page_id, id, 'mousemove');
780   };
781
782   /**
783    * Triggers a mouse click event on the given coordinates
784    * @param serverResponse
785    * @param x
786    * @param y
787    * @return {*}
788    */
789   Browser.prototype.click_coordinates = function (serverResponse, x, y) {
790     var response;
791
792     this.currentPage.sendEvent('click', x, y);
793     response = {
794       click: {
795         x: x,
796         y: y
797       }
798     };
799
800     return this.serverSendResponse(response, serverResponse);
801   };
802
803   /**
804    *  Drags one element into another, useful for nice javascript thingies
805    * @param serverResponse
806    * @param page_id
807    * @param id
808    * @param other_id
809    * @return {*}
810    */
811   Browser.prototype.drag = function (serverResponse, page_id, id, other_id) {
812     this.node(page_id, id).dragTo(this.node(page_id, other_id));
813     return this.serverSendResponse(true, serverResponse);
814   };
815
816   /**
817    * Triggers an event on the given page and element
818    * @param serverResponse
819    * @param page_id
820    * @param id
821    * @param event
822    * @return {*}
823    */
824   Browser.prototype.trigger = function (serverResponse, page_id, id, event) {
825     this.node(page_id, id).trigger(event);
826     return this.serverSendResponse(event, serverResponse);
827   };
828
829   /**
830    * Checks if two elements are equal on a dom level
831    * @param serverResponse
832    * @param page_id
833    * @param id
834    * @param other_id
835    * @return {*}
836    */
837   Browser.prototype.equals = function (serverResponse, page_id, id, other_id) {
838     return this.serverSendResponse(this.node(page_id, id).isEqual(this.node(page_id, other_id)), serverResponse);
839   };
840
841   /**
842    * Resets the current page to a clean slate
843    * @param serverResponse
844    * @return {*}
845    */
846   Browser.prototype.reset = function (serverResponse) {
847     this.resetPage();
848     return this.serverSendResponse(true, serverResponse);
849   };
850
851   /**
852    * Scrolls to a position given by the left, top coordinates
853    * @param serverResponse
854    * @param left
855    * @param top
856    * @return {*}
857    */
858   Browser.prototype.scroll_to = function (serverResponse, left, top) {
859     this.currentPage.setScrollPosition({
860       left: left,
861       top: top
862     });
863     return this.serverSendResponse(true, serverResponse);
864   };
865
866   /**
867    * Sends keys to an element simulating as closest as possible what a user would do
868    * when typing
869    * @param serverResponse
870    * @param page_id
871    * @param id
872    * @param keys
873    * @return {*}
874    */
875   Browser.prototype.send_keys = function (serverResponse, page_id, id, keys) {
876     var key, sequence, target, _i, _len;
877     target = this.node(page_id, id);
878     if (!target.containsSelection()) {
879       target.mouseEvent('click');
880     }
881     for (_i = 0, _len = keys.length; _i < _len; _i++) {
882       sequence = keys[_i];
883       key = sequence.key != null ? this.currentPage.keyCode(sequence.key) : sequence;
884       this.currentPage.sendEvent('keypress', key);
885     }
886     return this.serverSendResponse(true, serverResponse);
887   };
888
889   /**
890    * Sends a native phantomjs key event to element
891    * @param serverResponse
892    * @param page_id
893    * @param id
894    * @param keyEvent
895    * @param key
896    * @param modifier
897    */
898   Browser.prototype.key_event = function (serverResponse, page_id, id, keyEvent, key, modifier) {
899     var keyEventModifierMap;
900     var keyEventModifier;
901     var target;
902
903     keyEventModifierMap = {
904       'none': 0x0,
905       'shift': 0x02000000,
906       'ctrl': 0x04000000,
907       'alt': 0x08000000,
908       'meta': 0x10000000
909     };
910     keyEventModifier = keyEventModifierMap[modifier];
911
912     target = this.node(page_id, id);
913     if (!target.containsSelection()) {
914       target.mouseEvent('click');
915     }
916     target.page.sendEvent(keyEvent, key, null, null, keyEventModifier);
917
918     return this.serverSendResponse(true, serverResponse);
919   };
920
921   /**
922    *  Sends the rendered page in a base64 encoding
923    * @param serverResponse
924    * @param format
925    * @param full
926    * @param selector
927    * @return {*}
928    */
929   Browser.prototype.render_base64 = function (serverResponse, format, full, selector) {
930     var encoded_image;
931     if (selector == null) {
932       selector = null;
933     }
934     this.set_clip_rect(full, selector);
935     encoded_image = this.currentPage.renderBase64(format);
936     return this.serverSendResponse(encoded_image, serverResponse);
937   };
938
939   /**
940    * Renders the current page entirely or a given selection
941    * @param serverResponse
942    * @param path
943    * @param full
944    * @param selector
945    * @return {*}
946    */
947   Browser.prototype.render = function (serverResponse, path, full, selector) {
948     var dimensions;
949     if (selector == null) {
950       selector = null;
951     }
952     dimensions = this.set_clip_rect(full, selector);
953     this.currentPage.setScrollPosition({
954       left: 0,
955       top: 0
956     });
957     this.currentPage.render(path);
958     this.currentPage.setScrollPosition({
959       left: dimensions.left,
960       top: dimensions.top
961     });
962     return this.serverSendResponse(true, serverResponse);
963   };
964
965
966   /**
967    * Sets the paper size, useful when printing to PDF
968    * @param serverResponse
969    * @param size
970    * @return {*}
971    */
972   Browser.prototype.set_paper_size = function (serverResponse, size) {
973     this.currentPage.setPaperSize(size);
974     return this.serverSendResponse(true, serverResponse);
975   };
976
977   /**
978    *  Sets the zoom factor on the current page
979    * @param serverResponse
980    * @param zoom_factor
981    * @return {*}
982    */
983   Browser.prototype.set_zoom_factor = function (serverResponse, zoom_factor) {
984     this.currentPage.setZoomFactor(zoom_factor);
985     return this.serverSendResponse(true, serverResponse);
986   };
987
988   /**
989    * Resizes the browser viewport, useful when testing mobile stuff
990    * @param serverResponse
991    * @param width
992    * @param height
993    * @return {*}
994    */
995   Browser.prototype.resize = function (serverResponse, width, height) {
996     this.currentPage.setViewportSize({
997       width: width,
998       height: height
999     });
1000     return this.serverSendResponse(true, serverResponse);
1001   };
1002
1003   /**
1004    * Gets the browser viewport size
1005    * Because PhantomJS is headless (nothing is shown)
1006    * viewportSize effectively simulates the size of the window like in a traditional browser.
1007    * @param serverResponse
1008    * @param handle
1009    * @return {*}
1010    */
1011   Browser.prototype.window_size = function (serverResponse, handle) {
1012     //TODO: add support for window handles
1013     return this.serverSendResponse(this.currentPage.viewportSize(), serverResponse);
1014   };
1015
1016   /**
1017    * Returns the network traffic that the current page has generated
1018    * @param serverResponse
1019    * @return {*}
1020    */
1021   Browser.prototype.network_traffic = function (serverResponse) {
1022     return this.serverSendResponse(this.currentPage.networkTraffic(), serverResponse);
1023   };
1024
1025   /**
1026    * Clears the accumulated network_traffic in the current page
1027    * @param serverResponse
1028    * @return {*}
1029    */
1030   Browser.prototype.clear_network_traffic = function (serverResponse) {
1031     this.currentPage.clearNetworkTraffic();
1032     return this.serverSendResponse(true, serverResponse);
1033   };
1034
1035   /**
1036    * Gets the headers of the current page
1037    * @param serverResponse
1038    * @return {*}
1039    */
1040   Browser.prototype.get_headers = function (serverResponse) {
1041     return this.serverSendResponse(this.currentPage.getCustomHeaders(), serverResponse);
1042   };
1043
1044   /**
1045    * Set headers in the browser
1046    * @param serverResponse
1047    * @param headers
1048    * @return {*}
1049    */
1050   Browser.prototype.set_headers = function (serverResponse, headers) {
1051     if (headers['User-Agent']) {
1052       this.currentPage.setUserAgent(headers['User-Agent']);
1053     }
1054     this.currentPage.setCustomHeaders(headers);
1055     return this.serverSendResponse(true, serverResponse);
1056   };
1057
1058   /**
1059    * Given an array of headers, adds them to the page
1060    * @param serverResponse
1061    * @param headers
1062    * @return {*}
1063    */
1064   Browser.prototype.add_headers = function (serverResponse, headers) {
1065     var allHeaders, name, value;
1066     allHeaders = this.currentPage.getCustomHeaders();
1067     for (name in headers) {
1068       if (headers.hasOwnProperty(name)) {
1069         value = headers[name];
1070         allHeaders[name] = value;
1071       }
1072     }
1073     return this.set_headers(serverResponse, allHeaders);
1074   };
1075
1076   /**
1077    * Adds a header to the page temporary or permanently
1078    * @param serverResponse
1079    * @param header
1080    * @param permanent
1081    * @return {*}
1082    */
1083   Browser.prototype.add_header = function (serverResponse, header, permanent) {
1084     if (!permanent) {
1085       this.currentPage.addTempHeader(header);
1086     }
1087     return this.add_headers(serverResponse, header);
1088   };
1089
1090
1091   /**
1092    * Sends back the client the response headers sent from the browser when making
1093    * the page request
1094    * @param serverResponse
1095    * @return {*}
1096    */
1097   Browser.prototype.response_headers = function (serverResponse) {
1098     return this.serverSendResponse(this.currentPage.responseHeaders(), serverResponse);
1099   };
1100
1101   /**
1102    * Returns the cookies of the current page being browsed
1103    * @param serverResponse
1104    * @return {*}
1105    */
1106   Browser.prototype.cookies = function (serverResponse) {
1107     return this.serverSendResponse(this.currentPage.cookies(), serverResponse);
1108   };
1109
1110   /**
1111    * Sets a cookie in the browser, the format of the cookies has to be the format it says
1112    * on phantomjs documentation and as such you can set it in other domains, not on the
1113    * current page
1114    * @param serverResponse
1115    * @param cookie
1116    * @return {*}
1117    */
1118   Browser.prototype.set_cookie = function (serverResponse, cookie) {
1119     return this.serverSendResponse(phantom.addCookie(cookie), serverResponse);
1120   };
1121
1122   /**
1123    * Remove a cookie set on the current page
1124    * @param serverResponse
1125    * @param name
1126    * @return {*}
1127    */
1128   Browser.prototype.remove_cookie = function (serverResponse, name) {
1129     //TODO: add error control to check if the cookie was properly deleted
1130     this.currentPage.deleteCookie(name);
1131     phantom.deleteCookie(name);
1132     return this.serverSendResponse(true, serverResponse);
1133   };
1134
1135   /**
1136    * Clear the cookies in the browser
1137    * @param serverResponse
1138    * @return {*}
1139    */
1140   Browser.prototype.clear_cookies = function (serverResponse) {
1141     phantom.clearCookies();
1142     return this.serverSendResponse(true, serverResponse);
1143   };
1144
1145   /**
1146    * Enables / Disables the cookies on the browser
1147    * @param serverResponse
1148    * @param flag
1149    * @return {*}
1150    */
1151   Browser.prototype.cookies_enabled = function (serverResponse, flag) {
1152     phantom.cookiesEnabled = flag;
1153     return this.serverSendResponse(true, serverResponse);
1154   };
1155
1156   /**
1157    * US19: DONE
1158    * Sets a basic authentication credential to access a page
1159    * THIS SHOULD BE USED BEFORE accessing a page
1160    * @param serverResponse
1161    * @param user
1162    * @param password
1163    * @return {*}
1164    */
1165   Browser.prototype.set_http_auth = function (serverResponse, user, password) {
1166     this.currentPage.setHttpAuth(user, password);
1167     return this.serverSendResponse(true, serverResponse);
1168   };
1169
1170   /**
1171    * Sets the flag whether to fail on javascript errors or not.
1172    * @param serverResponse
1173    * @param value
1174    * @return {*}
1175    */
1176   Browser.prototype.set_js_errors = function (serverResponse, value) {
1177     this.js_errors = value;
1178     return this.serverSendResponse(true, serverResponse);
1179   };
1180
1181   /**
1182    * Sets the debug mode to boolean value
1183    * @param serverResponse
1184    * @param value
1185    * @return {*}
1186    */
1187   Browser.prototype.set_debug = function (serverResponse, value) {
1188     this._debug = value;
1189     return this.serverSendResponse(true, serverResponse);
1190   };
1191
1192   /**
1193    * Goes back in the history when possible
1194    * @param serverResponse
1195    * @return {*}
1196    */
1197   Browser.prototype.go_back = function (serverResponse) {
1198     var self = this;
1199     if (this.currentPage.canGoBack()) {
1200       this.currentPage.state = 'loading';
1201       this.currentPage.goBack();
1202       return this.currentPage.waitState('default', function () {
1203         return self.serverSendResponse(true, serverResponse);
1204       });
1205     } else {
1206       return this.serverSendResponse(false, serverResponse);
1207     }
1208   };
1209
1210   /**
1211    * Reloads the page if possible
1212    * @return {*}
1213    */
1214   Browser.prototype.reload = function (serverResponse) {
1215     var self = this;
1216     this.currentPage.state = 'loading';
1217     this.currentPage.reload();
1218     return this.currentPage.waitState('default', function () {
1219       return self.serverSendResponse(true, serverResponse);
1220     });
1221   };
1222
1223   /**
1224    * Goes forward in the browser history if possible
1225    * @param serverResponse
1226    * @return {*}
1227    */
1228   Browser.prototype.go_forward = function (serverResponse) {
1229     var self = this;
1230     if (this.currentPage.canGoForward()) {
1231       this.currentPage.state = 'loading';
1232       this.currentPage.goForward();
1233       return this.currentPage.waitState('default', function () {
1234         return self.serverSendResponse(true, serverResponse);
1235       });
1236     } else {
1237       return this.serverSendResponse(false, serverResponse);
1238     }
1239   };
1240
1241   /**
1242    *  Sets the urlBlacklist for the given urls as parameters
1243    * @return {boolean}
1244    */
1245   Browser.prototype.set_url_blacklist = function (serverResponse, blackList) {
1246     this.currentPage.urlBlacklist = Array.prototype.slice.call(blackList);
1247     return this.serverSendResponse(true, serverResponse);
1248   };
1249
1250   /**
1251    * Runs a browser command and returns the response back to the client
1252    * when the command has finished the execution
1253    * @param command
1254    * @param serverResponse
1255    * @return {*}
1256    */
1257   Browser.prototype.serverRunCommand = function (command, serverResponse) {
1258     var commandData;
1259     var commandArgs;
1260     var commandName;
1261
1262     commandName = command.name;
1263     commandArgs = command.args;
1264     this.currentPage.state = 'default';
1265     commandData = [serverResponse].concat(commandArgs);
1266
1267     if (typeof this[commandName] !== "function") {
1268       //We can not run such command
1269       throw new Poltergeist.Error();
1270     }
1271
1272     return this[commandName].apply(this, commandData);
1273   };
1274
1275   /**
1276    * Sends a response back to the client who made the request
1277    * @param response
1278    * @param serverResponse
1279    * @return {*}
1280    */
1281   Browser.prototype.serverSendResponse = function (response, serverResponse) {
1282     var errors;
1283     errors = this.currentPage.errors;
1284     this.currentPage.clearErrors();
1285     if (errors.length > 0 && this.js_errors) {
1286       return this.owner.serverSendError(new Poltergeist.JavascriptError(errors), serverResponse);
1287     } else {
1288       return this.owner.serverSendResponse(response, serverResponse);
1289     }
1290   };
1291
1292   return Browser;
1293
1294 })();