<commit_before>(function($){
  $.fn.rowGrid = function( options ) {
    return this.each(function() {
      $this = $(this);
      if(options === 'appended') {
        options = $this.data('grid-options');
        var $lastRow = $this.children('.' + options.lastRowClass);
        var items = $lastRow.nextAll(options.itemSelector).add($lastRow);
        layout(this, options, items);
      } else {
        options = $.extend( {}, $.fn.rowGrid.defaults, options );
        $this.data('grid-options', options);
        layout(this, options);

        if(options.resize) {
          $(window).on('resize.rowGrid', {container: this}, function(event) {
            layout(event.data.container, options);
          });
        }
      }
    });
  };

  $.fn.rowGrid.defaults = {
    minMargin: null,
    maxMargin: null,
    resize: true,
    lastRowClass: 'last-row',
    firstItemClass: null
  };

  function layout(container, options, items) {
    var rowWidth = 0,
        rowElems = [],
        items = jQuery.makeArray(items || container.querySelectorAll(options.itemSelector)),
        itemsSize = items.length;
    // read
    var containerWidth = container.clientWidth-parseFloat($(container).css('padding-left'))-parseFloat($(container).css('padding-right'));
    var itemAttrs = [];
    var theImage;
    for(var i = 0; i < itemsSize; ++i) {
      theImage = items[i].getElementsByTagName('img')[0];
      if (!theImage) {
        items.splice(i, 1);
        --i;
        --itemsSize;
        continue;
      }
      // get width and height via attribute or js value
      itemAttrs[i] = {
        width: parseInt(theImage.getAttribute('width')) || theImage.offsetWidth,
        height: parseInt(theImage.getAttribute('height')) || theImage.offsetHeight
      };
    }
    itemsSize = items.length;

    // write
    for(var index = 0; index < itemsSize; ++index) {
      if (items[index].classList) {
        items[index].classList.remove(options.firstItemClass);
        items[index].classList.remove(options.lastRowClass);
      } else {
        // IE <10
        items[index].className = items[index].className.replace(new RegExp('(^|\\b)' + options.firstItemClass + '|' + options.lastRowClass + '(\\b|$)', 'gi'), ' ');
      }

      rowWidth += itemAttrs[index].width;
      rowElems.push(items[index]);

      // check if it is the last element
      if(index === itemsSize - 1) {
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          // if first element in row
          if(rowElemIndex === 0) {
            rowElems[rowElemIndex].className += ' ' + options.lastRowClass;
          }
          rowElems[rowElemIndex].style.cssText =
              'width: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width + 'px;' +
              'height: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height + 'px;' +
              'margin-right:' + ((rowElemIndex < rowElems.length - 1)?options.minMargin+'px' : 0);
        }
      }

      // check whether width of row is too high
      if(rowWidth + options.maxMargin * (rowElems.length - 1) > containerWidth) {
        var diff = rowWidth + options.maxMargin * (rowElems.length - 1) - containerWidth;
        var nrOfElems = rowElems.length;
        // change margin
        var maxSave = (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        if(maxSave < diff) {
          var rowMargin = options.minMargin;
          diff -= (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        } else {
          var rowMargin = options.maxMargin - diff / (nrOfElems - 1);
          diff = 0;
        }
        var rowElem,
          widthDiff = 0;
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          rowElem = rowElems[rowElemIndex];
          var rowElemWidth = itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width;
          var newWidth = rowElemWidth - (rowElemWidth / rowWidth) * diff;
          var newHeight = Math.round(itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height * (newWidth / rowElemWidth));
          if (widthDiff + 1 - newWidth % 1 >= 0.5 ) {
            widthDiff -= newWidth % 1;
            newWidth = Math.floor(newWidth);
          } else {
            widthDiff += 1 - newWidth % 1;
            newWidth = Math.ceil(newWidth);
          }
          rowElem.style.cssText =
              'width: ' + newWidth + 'px;' +
              'height: ' + newHeight + 'px;' +
              'margin-right: ' + ((rowElemIndex < rowElems.length - 1)?rowMargin : 0) + 'px';
          if(rowElemIndex === 0) {
            rowElem.className += ' ' + options.firstItemClass;
          }
        }
        rowElems = [],
          rowWidth = 0;
      }
    }
  }
})(jQuery);<commit_msg>set width/height attribute if not available<commit_after>(function($){
  $.fn.rowGrid = function( options ) {
    return this.each(function() {
      $this = $(this);
      if(options === 'appended') {
        options = $this.data('grid-options');
        var $lastRow = $this.children('.' + options.lastRowClass);
        var items = $lastRow.nextAll(options.itemSelector).add($lastRow);
        layout(this, options, items);
      } else {
        options = $.extend( {}, $.fn.rowGrid.defaults, options );
        $this.data('grid-options', options);
        layout(this, options);

        if(options.resize) {
          $(window).on('resize.rowGrid', {container: this}, function(event) {
            layout(event.data.container, options);
          });
        }
      }
    });
  };

  $.fn.rowGrid.defaults = {
    minMargin: null,
    maxMargin: null,
    resize: true,
    lastRowClass: 'last-row',
    firstItemClass: null
  };

  function layout(container, options, items) {
    var rowWidth = 0,
        rowElems = [],
        items = jQuery.makeArray(items || container.querySelectorAll(options.itemSelector)),
        itemsSize = items.length;
    // read
    var containerWidth = container.clientWidth-parseFloat($(container).css('padding-left'))-parseFloat($(container).css('padding-right'));
    var itemAttrs = [];
    var theImage, w, h;
    for(var i = 0; i < itemsSize; ++i) {
      theImage = items[i].getElementsByTagName('img')[0];
      if (!theImage) {
        items.splice(i, 1);
        --i;
        --itemsSize;
        continue;
      }
      // get width and height via attribute or js value
      if (!(w = parseInt(theImage.getAttribute('width')))) {
        theImage.setAttribute('width', w = theImage.offsetWidth);
      }
      if (!(h = parseInt(theImage.getAttribute('height')))) {
        theImage.setAttribute('height', h = theImage.offsetHeight);
      }

      itemAttrs[i] = {
        width: w,
        height: h
      };
    }
    itemsSize = items.length;

    // write
    for(var index = 0; index < itemsSize; ++index) {
      if (items[index].classList) {
        items[index].classList.remove(options.firstItemClass);
        items[index].classList.remove(options.lastRowClass);
      } else {
        // IE <10
        items[index].className = items[index].className.replace(new RegExp('(^|\\b)' + options.firstItemClass + '|' + options.lastRowClass + '(\\b|$)', 'gi'), ' ');
      }

      rowWidth += itemAttrs[index].width;
      rowElems.push(items[index]);

      // check if it is the last element
      if(index === itemsSize - 1) {
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          // if first element in row
          if(rowElemIndex === 0) {
            rowElems[rowElemIndex].className += ' ' + options.lastRowClass;
          }
          rowElems[rowElemIndex].style.cssText =
              'width: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width + 'px;' +
              'height: ' + itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height + 'px;' +
              'margin-right:' + ((rowElemIndex < rowElems.length - 1)?options.minMargin+'px' : 0);
        }
      }

      // check whether width of row is too high
      if(rowWidth + options.maxMargin * (rowElems.length - 1) > containerWidth) {
        var diff = rowWidth + options.maxMargin * (rowElems.length - 1) - containerWidth;
        var nrOfElems = rowElems.length;
        // change margin
        var maxSave = (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        if(maxSave < diff) {
          var rowMargin = options.minMargin;
          diff -= (options.maxMargin - options.minMargin) * (nrOfElems - 1);
        } else {
          var rowMargin = options.maxMargin - diff / (nrOfElems - 1);
          diff = 0;
        }
        var rowElem,
          widthDiff = 0;
        for(var rowElemIndex = 0; rowElemIndex<rowElems.length; rowElemIndex++) {
          rowElem = rowElems[rowElemIndex];
          var rowElemWidth = itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].width;
          var newWidth = rowElemWidth - (rowElemWidth / rowWidth) * diff;
          var newHeight = Math.round(itemAttrs[index+parseInt(rowElemIndex)-rowElems.length+1].height * (newWidth / rowElemWidth));
          if (widthDiff + 1 - newWidth % 1 >= 0.5 ) {
            widthDiff -= newWidth % 1;
            newWidth = Math.floor(newWidth);
          } else {
            widthDiff += 1 - newWidth % 1;
            newWidth = Math.ceil(newWidth);
          }
          rowElem.style.cssText =
              'width: ' + newWidth + 'px;' +
              'height: ' + newHeight + 'px;' +
              'margin-right: ' + ((rowElemIndex < rowElems.length - 1)?rowMargin : 0) + 'px';
          if(rowElemIndex === 0) {
            rowElem.className += ' ' + options.firstItemClass;
          }
        }
        rowElems = [],
          rowWidth = 0;
      }
    }
  }
})(jQuery);<|endoftext|>


<commit_before>// ==UserScript==
// @name         CheckItForMe
// @version      0.16
// @match        https://scrap.tf/raffles
// @match        https://scrap.tf/raffles/ending
// @require      https://code.jquery.com/jquery-2.2.4.min.js#sha256=BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=
// @updateURL    https://raw.githubusercontent.com/GuilloOme/CheckThisForMe/master/checkItForMe.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var RELOAD_DELAY = 20,
        ENTERING_DELAY = 1.5;

    var todoRaffleList = [],
        raffleIndex = 0,
        interval = randomInterval(RELOAD_DELAY);

    $(document).ready(function() {
        console.info('Bot: Started');
        scanHash();
    });

    function scanHash() {
        var hash = window.location.hash,
            url = window.location.href,
            baseUrl = /https:\/\/scrap.tf\/raffles/;

        if (url.match(baseUrl) && isThereNewRaffles()) {
            scanRaffles();
        } else {
            console.info('Bot: Nothing to do, waiting ' + (interval / 1000) + ' sec before reloading…');

            setTimeout(function() {
                location.reload();
            }, interval);
        }

    }

    function scanRaffles() {
        //console.info('Bot: Loading all the raffles…');

        $.when(loadAllRaffles()).then(function() {
            var activePanel = $('div.panel');
            $(activePanel[activePanel.length - 1]).find('div.panel-raffle').each(function(id, item) {
                if ($(item).css('opacity') === '1') {
                    todoRaffleList.push($(item).find('div.raffle-name > a').attr('href'));
                }
            });

            if (todoRaffleList.length > 0) {
                //console.info('Bot: Start entering raffles.');

                $.when(enterRaffles()).then(function() {
                    //console.info('Bot: Done entering raffles, reloading…');
                    location.reload();
                });
            } else {
                console.info('Bot: No raffle to enter, waiting ' + (interval / 1000) + ' sec before reloading…');
                setTimeout(function() {
                    location.reload();
                }, interval);
            }
        });

    }

    function enterRaffles() {
        var deferred = jQuery.Deferred();

        function joinRaffle(url) {

            var id, hash;

            ScrapTF.Raffles.EnterRaffle = function(idArg, hashArg) {
                id = idArg;
                hash = hashArg;
            }

            $.get(url, function(responseData) {

                var request,
                    raffleDeferred = jQuery.Deferred(),
                    raffleKey = $(responseData).find("#raffle-key").val(),
                    enterButton = $(responseData).find('button#raffle-enter');

                if (enterButton.length > 0 && $(responseData).find('button#raffle-enter>i18n').html() === 'Enter Raffle') {
                    console.info('Bot: Entrering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

                    $(responseData).find('button#raffle-enter').click();

                    request = {
                        raffle: id,
                        captcha: '',
                        rafflekey: raffleKey,
                        password: '',
                        hash: hash
                    };

                    ScrapTF.Ajax('viewraffle/EnterRaffle', {
                        raffle: id,
                        captcha: '',
                        rafflekey: raffleKey,
                        password: '',
                        hash: hash
                    }, function() {
                        console.info('Bot: Done entering raffle');

                        raffleDeferred.resolve();
                    }, function() {
                        console.warn('Bot: Error when entering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

                        raffleDeferred.resolve();
                    });

                } else {
                    console.warn('Bot: Can\'t enter raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);
                    raffleDeferred.resolve();
                }

                $.when(raffleDeferred.promise()).then(function() {
                    raffleIndex++;
                    if (raffleIndex < todoRaffleList.length) {
                        setTimeout(function() {
                            joinRaffle(todoRaffleList[raffleIndex]);
                        }, randomInterval(ENTERING_DELAY));
                    } else {
                        deferred.resolve();
                    }
                });

            });

            /* var currentChildFrame = window.open(url);

             $(currentChildFrame.document).ready(function() {
             var waitInterval;
             console.log('Bot: Entrering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

             waitInterval = setInterval(function() {
             console.log('Bot: Waiting confirmation…');

             if ($(currentChildFrame.document).find('button#raffle-enter>i18n').html() === 'Leave Raffle' || $(currentChildFrame.document).find('div.alert-error').length > 0) {

             console.log('Bot: Closing raffle window');

             currentChildFrame.close();

             raffleIndex++;
             if (raffleIndex < todoRaffleList.length) {
             setTimeout(function() {
             joinRaffle(todoRaffleList[raffleIndex]);
             }, randomInterval(ENTERING_DELAY));
             } else {
             deferred.resolve();
             }

             clearInterval(waitInterval);

             } else if ($(currentChildFrame.document).find('button#raffle-enter>i18n').html() === 'Enter Raffle') {

             $(currentChildFrame.document).find('button#raffle-enter').trigger('click');

             }
             }, randomInterval(ENTERING_DELAY));

             });*/

        }

        joinRaffle(todoRaffleList[raffleIndex]);

        return deferred.promise();
    }

    function isThereNewRaffles() {

        var value = $('div.panel-body>div.text-center>i18n>var').text(),
            ar = value.match(/[0-9]+/gi),
            raffleToEnterNumber = (parseInt(ar[1]) - parseInt(ar[0]));

        console.info('Bot: ' + value + ', There is ' + raffleToEnterNumber + ' raffle(s) to enter.');

        return (ar.length > 1 && raffleToEnterNumber > 0);
    }

    function loadAllRaffles() {
        var deferred = jQuery.Deferred();

        var loadInterval = setInterval(function() {

            if (!ScrapTF.Raffles.Pagination.isDone) {
                ScrapTF.Raffles.Pagination.LoadNext();
            } else {
                deferred.resolve();
                clearInterval(loadInterval);
            }

        }, randomInterval());

        return deferred.promise();

    }

    function randomInterval(delay) {
        if (!delay) {
            delay = 0.5;
        }
        return Math.floor(delay * 1000) + Math.floor(Math.random() * (delay * 1000));
    }

})
();
<commit_msg>update version number<commit_after>// ==UserScript==
// @name         CheckItForMe
// @version      0.17
// @match        https://scrap.tf/raffles
// @match        https://scrap.tf/raffles/ending
// @require      https://code.jquery.com/jquery-2.2.4.min.js#sha256=BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=
// @updateURL    https://raw.githubusercontent.com/GuilloOme/CheckThisForMe/master/checkItForMe.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var RELOAD_DELAY = 20,
        ENTERING_DELAY = 1.5;

    var todoRaffleList = [],
        raffleIndex = 0,
        interval = randomInterval(RELOAD_DELAY);

    $(document).ready(function() {
        console.info('Bot: Started');
        scanHash();
    });

    function scanHash() {
        var hash = window.location.hash,
            url = window.location.href,
            baseUrl = /https:\/\/scrap.tf\/raffles/;

        if (url.match(baseUrl) && isThereNewRaffles()) {
            scanRaffles();
        } else {
            console.info('Bot: Nothing to do, waiting ' + (interval / 1000) + ' sec before reloading…');

            setTimeout(function() {
                location.reload();
            }, interval);
        }

    }

    function scanRaffles() {
        //console.info('Bot: Loading all the raffles…');

        $.when(loadAllRaffles()).then(function() {
            var activePanel = $('div.panel');
            $(activePanel[activePanel.length - 1]).find('div.panel-raffle').each(function(id, item) {
                if ($(item).css('opacity') === '1') {
                    todoRaffleList.push($(item).find('div.raffle-name > a').attr('href'));
                }
            });

            if (todoRaffleList.length > 0) {
                //console.info('Bot: Start entering raffles.');

                $.when(enterRaffles()).then(function() {
                    //console.info('Bot: Done entering raffles, reloading…');
                    location.reload();
                });
            } else {
                console.info('Bot: No raffle to enter, waiting ' + (interval / 1000) + ' sec before reloading…');
                setTimeout(function() {
                    location.reload();
                }, interval);
            }
        });

    }

    function enterRaffles() {
        var deferred = jQuery.Deferred();

        function joinRaffle(url) {

            var id, hash;

            ScrapTF.Raffles.EnterRaffle = function(idArg, hashArg) {
                id = idArg;
                hash = hashArg;
            }

            $.get(url, function(responseData) {

                var request,
                    raffleDeferred = jQuery.Deferred(),
                    raffleKey = $(responseData).find("#raffle-key").val(),
                    enterButton = $(responseData).find('button#raffle-enter');

                if (enterButton.length > 0 && $(responseData).find('button#raffle-enter>i18n').html() === 'Enter Raffle') {
                    console.info('Bot: Entrering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

                    $(responseData).find('button#raffle-enter').click();

                    request = {
                        raffle: id,
                        captcha: '',
                        rafflekey: raffleKey,
                        password: '',
                        hash: hash
                    };

                    ScrapTF.Ajax('viewraffle/EnterRaffle', {
                        raffle: id,
                        captcha: '',
                        rafflekey: raffleKey,
                        password: '',
                        hash: hash
                    }, function() {
                        console.info('Bot: Done entering raffle');

                        raffleDeferred.resolve();
                    }, function() {
                        console.warn('Bot: Error when entering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

                        raffleDeferred.resolve();
                    });

                } else {
                    console.warn('Bot: Can\'t enter raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);
                    raffleDeferred.resolve();
                }

                $.when(raffleDeferred.promise()).then(function() {
                    raffleIndex++;
                    if (raffleIndex < todoRaffleList.length) {
                        setTimeout(function() {
                            joinRaffle(todoRaffleList[raffleIndex]);
                        }, randomInterval(ENTERING_DELAY));
                    } else {
                        deferred.resolve();
                    }
                });

            });

            /* var currentChildFrame = window.open(url);

             $(currentChildFrame.document).ready(function() {
             var waitInterval;
             console.log('Bot: Entrering raffle: ' + (raffleIndex + 1) + '/' + todoRaffleList.length);

             waitInterval = setInterval(function() {
             console.log('Bot: Waiting confirmation…');

             if ($(currentChildFrame.document).find('button#raffle-enter>i18n').html() === 'Leave Raffle' || $(currentChildFrame.document).find('div.alert-error').length > 0) {

             console.log('Bot: Closing raffle window');

             currentChildFrame.close();

             raffleIndex++;
             if (raffleIndex < todoRaffleList.length) {
             setTimeout(function() {
             joinRaffle(todoRaffleList[raffleIndex]);
             }, randomInterval(ENTERING_DELAY));
             } else {
             deferred.resolve();
             }

             clearInterval(waitInterval);

             } else if ($(currentChildFrame.document).find('button#raffle-enter>i18n').html() === 'Enter Raffle') {

             $(currentChildFrame.document).find('button#raffle-enter').trigger('click');

             }
             }, randomInterval(ENTERING_DELAY));

             });*/

        }

        joinRaffle(todoRaffleList[raffleIndex]);

        return deferred.promise();
    }

    function isThereNewRaffles() {

        var value = $('div.panel-body>div.text-center>i18n>var').text(),
            ar = value.match(/[0-9]+/gi),
            raffleToEnterNumber = (parseInt(ar[1]) - parseInt(ar[0]));

        console.info('Bot: ' + value + ', There is ' + raffleToEnterNumber + ' raffle(s) to enter.');

        return (ar.length > 1 && raffleToEnterNumber > 0);
    }

    function loadAllRaffles() {
        var deferred = jQuery.Deferred();

        var loadInterval = setInterval(function() {

            if (!ScrapTF.Raffles.Pagination.isDone) {
                ScrapTF.Raffles.Pagination.LoadNext();
            } else {
                deferred.resolve();
                clearInterval(loadInterval);
            }

        }, randomInterval());

        return deferred.promise();

    }

    function randomInterval(delay) {
        if (!delay) {
            delay = 0.5;
        }
        return Math.floor(delay * 1000) + Math.floor(Math.random() * (delay * 1000));
    }

})
();
<|endoftext|>


<commit_before>// vim:set noet fdm=syntax fdl=0 fdc=3 fdn=2:
//---------------------------------------------------------
// QWeb javascript
//---------------------------------------------------------

/*
	TODO

		String parsing
			if (window.DOMParser) {
				parser=new DOMParser();
				xmlDoc=parser.parseFromString(text,"text/xml");
			} else {
				xmlDoc=new ActiveXObject("Msxml2.DOMDocument.4.0");
				xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
					Which versions to try, it's confusing...
				xmlDoc.async="false";
				xmlDoc.async=false;
				xmlDoc.preserveWhiteSpace=true;
				xmlDoc.load("f.xml");
				xmlDoc.loadXML(text);  ?
			}

		Support space in IE by reparsing the responseText
			xmlhttp.responseXML.loadXML(xmlhttp.responseText); ?

		Preprocess: (nice optimization)
			preprocess by flattening all non t- element to a TEXT_NODE.
			count the number of "\n" in text nodes to give an aproximate LINE NUMBER on elements for error reporting
			if from IE HTMLDOM use if(a[i].specified) to avoid 88 empty attributes per element during the preprocess,

		implement t-trim 'left' 'right' 'both', is it needed ? inner=render_trim(l_inner.join(), t_att)

		Ruby/python: to backport from javascript to python/ruby render_node to use regexp, factorize foreach %var, t-att test for tuple(attname,value)

	DONE
		we reintroduced t-att-id, no more t-esc-id because of the new convention t-att="["id","val"]"
*/

var QWeb = {
    templates:{},
    prefix:"t",
    reg:new RegExp(),
    tag:{},
    att:{},
    eval_object:function(e, v) {
        // TODO: Currently this will also replace and, or, ... in strings. Try
        // 'hi boys and girls' != '' and 1 == 1  -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
        // try to find a solution without tokenizing
        e = e.replace(/\Wand\W/g, " && ");
        e = e.replace(/\Wor\W/g, " and ");
        e = e.replace(/\Wgt\W/g, " > ");
        e = e.replace(/\Wgte\W/g, " >= ");
        e = e.replace(/\Wlt\W/g, " < ");
        e = e.replace(/\Wlte\W/g, " <= ");
        if (v[e] != undefined) {
            return v[e];
        } else {
            with (v) return eval(e);
        }
    },
    eval_str:function(e, v) {
        var r = this.eval_object(e, v);
        r = (typeof(r) == "undefined" || r == null) ? "" : r.toString();
        return e == "0" ? v["0"] : r;
    },
    eval_format:function(e, v) {
        var m, src = e.split(/#/), r = src[0];
        for (var i = 1; i < src.length; i++) {
            if (m = src[i].match(/^{(.*)}(.*)/)) {
                r += this.eval_str(m[1], v) + m[2];
            } else {
                r += "#" + src[i];
            }
        }
        return r;
    },
    eval_bool:function(e, v) {
        return this.eval_object(e, v) ? true : false;
    },
    trim : function(v, mode) {
        if (!v || !mode) return v;
        switch (mode) {
            case 'both':
                return v.replace(/^\s*|\s*$/g, "");
            case "left":
                return v.replace(/^\s*/, "");
            case "right":
                return v.replace(/\s*$/, "");
        }
    },
    escape_text:function(s) {
        return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
    },
    escape_att:function(s) {
        return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
    },
    render_node : function(e, v, inner_trim) {
        if (e.nodeType == 3) {
            return inner_trim ? this.trim(e.data, inner_trim) : e.data;
        }
        if (e.nodeType == 1) {
            var g_att = {};
            var t_att = {};
            var t_render = null;
            var a = e.attributes;
            for (var i = 0; i < a.length; i++) {
                var an = a[i].name,av = a[i].value;
                var m;
                if (m = an.match(this.reg)) {
                    var n = m[1];
                    if (n == "eval") {
                        n = m[2].substring(1);
                        av = this.eval_str(av, v);
                    }
                    var f;
                    if (f = this.att[n]) {
                        this[f](e, t_att, g_att, v, m[2], av);
                    } else if (f = this.tag[n]) {
                        t_render = f;
                    }
                    t_att[n] = av;
                } else {
                    g_att[an] = av;
                }
            }
            if (inner_trim && !t_att["trim"]) {
                t_att["trim"] = "inner " + inner_trim;
            }
            if (t_render) {
                return this[t_render](e, t_att, g_att, v);
            }
            return this.render_element(e, t_att, g_att, v);
        }
        return "";
    },
    render_element:function(e, t_att, g_att, v) {
        var inner = "", ec = e.childNodes, trim = t_att["trim"], inner_trim;
        if (trim) {
            if (trim.match(/(^|\W)(inner)($|\W)/)) {
                inner_trim = true;
                trim = "both";
            }
            var tm = trim.match(/(^|\W)(both|left|right)($|\W)/);
            if (tm) trim = tm[2];
        }
        for (var i = 0; i < ec.length; i++) {
            inner += inner_trim ? this.trim(this.render_node(ec[i], v, inner_trim ? trim : null), trim) : this.render_node(ec[i], v, inner_trim ? trim : null);
        }
        if (trim && !inner_trim) {
            inner = this.trim(inner, trim);
        }
        if (e.tagName == this.prefix) {
            return inner;
        }
        var att = "";
        for (var an in g_att) {
            att += " " + an + '="' + this.escape_att(g_att[an]) + '"';
        }
        return inner.length ? "<" + e.tagName + att + ">" + inner + "</" + e.tagName + ">" : "<" + e.tagName + att + "/>";
    },
    render_att_att:function(e, t_att, g_att, v, ext, av) {
        if (ext) {
            g_att[ext.substring(1)] = this.eval_str(av, v);
        } else {
            var o = this.eval_object(av, v);
            g_att[o[0]] = o[1];
        }
    },
    render_att_attf:function(e, t_att, g_att, v, ext, av) {
        g_att[ext.substring(1)] = this.eval_format(av, v);
    },
    render_tag_raw:function(e, t_att, g_att, v) {
        return this.eval_str(t_att["raw"], v);
    },
    render_tag_rawf:function(e, t_att, g_att, v) {
        return this.eval_format(t_att["raw"], v);
    },
    render_tag_esc:function(e, t_att, g_att, v) {
        return this.escape_text(this.eval_str(t_att["esc"], v));
    },
    render_tag_escf:function(e, t_att, g_att, v) {
        return this.escape_text(this.eval_format(t_att["esc"], v));
    },
    render_tag_if:function(e, t_att, g_att, v) {
        return this.eval_bool(t_att["if"], v) ? this.render_element(e, t_att, g_att, v) : "";
    },
    render_tag_set:function(e, t_att, g_att, v) {
        var ev = t_att["value"];
        if (ev && ev.constructor != Function) {
            v[t_att["set"]] = this.eval_object(ev, v);
        } else {
            v[t_att["set"]] = this.render_element(e, t_att, g_att, v);
        }
        return "";
    },
    render_tag_call:function(e, t_att, g_att, v) {
        var d = v;
        if (!t_att["import"]) {
            d = {};
            for (var i in v) {
                d[i] = v[i];
            }
        }
        d["0"] = this.render_element(e, t_att, g_att, d);
        return this.render(t_att["call"], d);
    },
    render_tag_js:function(e, t_att, g_att, v) {
        var r = this.eval_str(this.render_element(e, t_att, g_att, v), v);
        return t_att["js"] != "quiet" ? r : "";
    },
    /**
     * Renders a foreach loop (@t-foreach).
     *
     * Adds the following elements to its context, where ${name}
     * is specified via @t-as:
     * * ${name} The current element itself
     * * ${name}_value Same as ${name}
     * * ${name}_index The 0-based index of the current element
     * * ${name}_first Whether the current element is the first one
     * * ${name}_parity odd|even (as strings)
     * * ${name}_all The iterated collection itself
     *
     * If the collection being iterated is an array, also adds:
     * * ${name}_last Whether the current element is the last one
     * * All members of the current object
     *
     * If the collection being iterated is an object, the value is actually the object's key
     *
     * @param e ?
     * @param t_att attributes of the element being t-foreach'd
     * @param g_att ?
     * @param old_context the context in which the foreach is evaluated
     */
    render_tag_foreach:function(e, t_att, g_att, old_context) {
        var expr = t_att["foreach"];
        var enu = this.eval_object(expr, old_context);
        var ru = [];
        if (enu) {
            var val = t_att['as'] || expr.replace(/[^a-zA-Z0-9]/g, '_');
            var context = {};
            for (var i in old_context) {
                context[i] = old_context[i];
            }
            context[val + "_all"] = enu;
            var val_value = val + "_value",
                val_index = val + "_index",
                val_first = val + "_first",
                val_last = val + "_last",
                val_parity = val + "_parity";
            var size = enu.length;
            if (size) {
                context[val + "_size"] = size;
                for (var j = 0; j < size; j++) {
                    var cur = enu[j];
                    context[val_value] = cur;
                    context[val_index] = j;
                    context[val_first] = j == 0;
                    context[val_last] = j + 1 == size;
                    context[val_parity] = (j % 2 == 1 ? 'odd' : 'even');
                    if (cur.constructor == Object) {
                        for (var k in cur) {
                            context[k] = cur[k];
                        }
                    }
                    context[val] = cur;
                    var r = this.render_element(e, t_att, g_att, context);
                    ru.push(r);
                }
            } else {
                var index = 0;
                for (cur in enu) {
                    context[val_value] = cur;
                    context[val_index] = index;
                    context[val_first] = index == 0;
                    context[val_parity] = (index % 2 == 1 ? 'odd' : 'even');
                    context[val] = cur;
                    ru.push(this.render_element(e, t_att, g_att, context));
                    index += 1;
                }
            }
            return ru.join("");
        } else {
            return "qweb: foreach " + expr + " not found.";
        }
    },
    hash:function() {
        var l = [], m;
        for (var i in this) {
            if (m = i.match(/render_tag_(.*)/)) {
                this.tag[m[1]] = i;
                l.push(m[1]);
            } else if (m = i.match(/render_att_(.*)/)) {
                this.att[m[1]] = i;
                l.push(m[1]);
            }
        }
        l.sort(function(a, b) {
            return a.length > b.length ? -1 : 1;
        });
        var s = "^" + this.prefix + "-(eval|" + l.join("|") + "|.*)(.*)$";
        this.reg = new RegExp(s);
    },
    /**
     * returns the correct XMLHttpRequest instance for the browser, or null if
     * it was not able to build any XHR instance.
     *
     * @returns XMLHttpRequest|MSXML2.XMLHTTP.3.0|null
     */
    get_xhr:function () {
        if (window.XMLHttpRequest) {
            return new window.XMLHttpRequest();
        }
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        } catch(e) {
            return null;
        }
    },
    load_xml:function(s) {
        var xml;
        if (s[0] == "<") {
            /*
             manque ca pour sarrisa
             if(window.DOMParser){
             mozilla
             if(!window.DOMParser){
             var doc = Sarissa.getDomDocument();
             doc.loadXML(sXml);
             return doc;
             };
             };
             */
        } else {
            var req = this.get_xhr();
            if (req) {
                req.open("GET", s, false);
                req.send(null);
                //if ie r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
                xml = req.responseXML;
                /*
                 TODO
                 if intsernetexploror
                 getdomimplmentation() for try catch
                 responseXML.getImplet
                 d=domimple()
                 d.preserverWhitespace=1
                 d.loadXML()

                 xml.preserverWhitespace=1
                 xml.loadXML(r.reponseText)
                 */
                return xml;
            }
        }
    },
    add_template:function(e) {
        // TODO: keep sources so we can implement reload()
        this.hash();
        if (e.constructor == String) {
            e = this.load_xml(e);
        }
        var ec = e.documentElement ? e.documentElement.childNodes
               : e.childNodes ? e.childNodes
               : [];
        for (var i = 0; i < ec.length; i++) {
            var n = ec[i];
            if (n.nodeType == 1) {
                var name = n.getAttribute(this.prefix + "-name");
                this.templates[name] = n;
            }
        }
    },
    render:function(name, v) {
        var e;
        if (e = this.templates[name]) {
            return this.render_node(e, v);
        }
        return "template " + name + " not found";
    }
};

<commit_msg>[IMP] replace \W's for word boundaries by \b (actually about word boundaries), replace String.match to literal regexes by RegExp.test (if result is used in test only) or RegExp.exec<commit_after>// vim:set noet fdm=syntax fdl=0 fdc=3 fdn=2:
//---------------------------------------------------------
// QWeb javascript
//---------------------------------------------------------

/*
	TODO

		String parsing
			if (window.DOMParser) {
				parser=new DOMParser();
				xmlDoc=parser.parseFromString(text,"text/xml");
			} else {
				xmlDoc=new ActiveXObject("Msxml2.DOMDocument.4.0");
				xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
					Which versions to try, it's confusing...
				xmlDoc.async="false";
				xmlDoc.async=false;
				xmlDoc.preserveWhiteSpace=true;
				xmlDoc.load("f.xml");
				xmlDoc.loadXML(text);  ?
			}

		Support space in IE by reparsing the responseText
			xmlhttp.responseXML.loadXML(xmlhttp.responseText); ?

		Preprocess: (nice optimization)
			preprocess by flattening all non t- element to a TEXT_NODE.
			count the number of "\n" in text nodes to give an aproximate LINE NUMBER on elements for error reporting
			if from IE HTMLDOM use if(a[i].specified) to avoid 88 empty attributes per element during the preprocess,

		implement t-trim 'left' 'right' 'both', is it needed ? inner=render_trim(l_inner.join(), t_att)

		Ruby/python: to backport from javascript to python/ruby render_node to use regexp, factorize foreach %var, t-att test for tuple(attname,value)

	DONE
		we reintroduced t-att-id, no more t-esc-id because of the new convention t-att="["id","val"]"
*/

var QWeb = {
    templates:{},
    prefix:"t",
    reg:new RegExp(),
    tag:{},
    att:{},
    eval_object:function(e, v) {
        // TODO: Currently this will also replace and, or, ... in strings. Try
        // 'hi boys and girls' != '' and 1 == 1  -- will be replaced to : 'hi boys && girls' != '' && 1 == 1
        // try to find a solution without tokenizing
        e = e.replace(/\band\b/g, " && ");
        e = e.replace(/\bor\b/g, " || ");
        e = e.replace(/\bgt\b/g, " > ");
        e = e.replace(/\bgte\b/g, " >= ");
        e = e.replace(/\blt\b/g, " < ");
        e = e.replace(/\blte\b/g, " <= ");
        if (v[e] != undefined) {
            return v[e];
        } else {
            with (v) return eval(e);
        }
    },
    eval_str:function(e, v) {
        var r = this.eval_object(e, v);
        r = (typeof(r) == "undefined" || r == null) ? "" : r.toString();
        return e == "0" ? v["0"] : r;
    },
    eval_format:function(e, v) {
        var m, src = e.split(/#/), r = src[0];
        for (var i = 1; i < src.length; i++) {
            if (m = src[i].match(/^{(.*)}(.*)/)) {
                r += this.eval_str(m[1], v) + m[2];
            } else {
                r += "#" + src[i];
            }
        }
        return r;
    },
    eval_bool:function(e, v) {
        return this.eval_object(e, v) ? true : false;
    },
    trim : function(v, mode) {
        if (!v || !mode) return v;
        switch (mode) {
            case 'both':
                return v.replace(/^\s*|\s*$/g, "");
            case "left":
                return v.replace(/^\s*/, "");
            case "right":
                return v.replace(/\s*$/, "");
        }
    },
    escape_text:function(s) {
        return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
    },
    escape_att:function(s) {
        return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
    },
    render_node : function(e, v, inner_trim) {
        if (e.nodeType == 3) {
            return inner_trim ? this.trim(e.data, inner_trim) : e.data;
        }
        if (e.nodeType == 1) {
            var g_att = {};
            var t_att = {};
            var t_render = null;
            var a = e.attributes;
            for (var i = 0; i < a.length; i++) {
                var an = a[i].name,av = a[i].value;
                var m;
                if (m = an.match(this.reg)) {
                    var n = m[1];
                    if (n == "eval") {
                        n = m[2].substring(1);
                        av = this.eval_str(av, v);
                    }
                    var f;
                    if (f = this.att[n]) {
                        this[f](e, t_att, g_att, v, m[2], av);
                    } else if (f = this.tag[n]) {
                        t_render = f;
                    }
                    t_att[n] = av;
                } else {
                    g_att[an] = av;
                }
            }
            if (inner_trim && !t_att["trim"]) {
                t_att["trim"] = "inner " + inner_trim;
            }
            if (t_render) {
                return this[t_render](e, t_att, g_att, v);
            }
            return this.render_element(e, t_att, g_att, v);
        }
        return "";
    },
    render_element:function(e, t_att, g_att, v) {
        var inner = "", ec = e.childNodes, trim = t_att["trim"], inner_trim;
        if (trim) {
            if (/\binner\b/.test(trim)) {
                inner_trim = true;
                trim = "both";
            }
            var tm = /\b(both|left|right)\b/.exec(trim);
            if (tm) trim = tm[1];
        }
        for (var i = 0; i < ec.length; i++) {
            inner += inner_trim ? this.trim(this.render_node(ec[i], v, inner_trim ? trim : null), trim) : this.render_node(ec[i], v, inner_trim ? trim : null);
        }
        if (trim && !inner_trim) {
            inner = this.trim(inner, trim);
        }
        if (e.tagName == this.prefix) {
            return inner;
        }
        var att = "";
        for (var an in g_att) {
            att += " " + an + '="' + this.escape_att(g_att[an]) + '"';
        }
        return inner.length ? "<" + e.tagName + att + ">" + inner + "</" + e.tagName + ">" : "<" + e.tagName + att + "/>";
    },
    render_att_att:function(e, t_att, g_att, v, ext, av) {
        if (ext) {
            g_att[ext.substring(1)] = this.eval_str(av, v);
        } else {
            var o = this.eval_object(av, v);
            g_att[o[0]] = o[1];
        }
    },
    render_att_attf:function(e, t_att, g_att, v, ext, av) {
        g_att[ext.substring(1)] = this.eval_format(av, v);
    },
    render_tag_raw:function(e, t_att, g_att, v) {
        return this.eval_str(t_att["raw"], v);
    },
    render_tag_rawf:function(e, t_att, g_att, v) {
        return this.eval_format(t_att["raw"], v);
    },
    render_tag_esc:function(e, t_att, g_att, v) {
        return this.escape_text(this.eval_str(t_att["esc"], v));
    },
    render_tag_escf:function(e, t_att, g_att, v) {
        return this.escape_text(this.eval_format(t_att["esc"], v));
    },
    render_tag_if:function(e, t_att, g_att, v) {
        return this.eval_bool(t_att["if"], v) ? this.render_element(e, t_att, g_att, v) : "";
    },
    render_tag_set:function(e, t_att, g_att, v) {
        var ev = t_att["value"];
        if (ev && ev.constructor != Function) {
            v[t_att["set"]] = this.eval_object(ev, v);
        } else {
            v[t_att["set"]] = this.render_element(e, t_att, g_att, v);
        }
        return "";
    },
    render_tag_call:function(e, t_att, g_att, v) {
        var d = v;
        if (!t_att["import"]) {
            d = {};
            for (var i in v) {
                d[i] = v[i];
            }
        }
        d["0"] = this.render_element(e, t_att, g_att, d);
        return this.render(t_att["call"], d);
    },
    render_tag_js:function(e, t_att, g_att, v) {
        var r = this.eval_str(this.render_element(e, t_att, g_att, v), v);
        return t_att["js"] != "quiet" ? r : "";
    },
    /**
     * Renders a foreach loop (@t-foreach).
     *
     * Adds the following elements to its context, where ${name}
     * is specified via @t-as:
     * * ${name} The current element itself
     * * ${name}_value Same as ${name}
     * * ${name}_index The 0-based index of the current element
     * * ${name}_first Whether the current element is the first one
     * * ${name}_parity odd|even (as strings)
     * * ${name}_all The iterated collection itself
     *
     * If the collection being iterated is an array, also adds:
     * * ${name}_last Whether the current element is the last one
     * * All members of the current object
     *
     * If the collection being iterated is an object, the value is actually the object's key
     *
     * @param e ?
     * @param t_att attributes of the element being t-foreach'd
     * @param g_att ?
     * @param old_context the context in which the foreach is evaluated
     */
    render_tag_foreach:function(e, t_att, g_att, old_context) {
        var expr = t_att["foreach"];
        var enu = this.eval_object(expr, old_context);
        var ru = [];
        if (enu) {
            var val = t_att['as'] || expr.replace(/[^a-zA-Z0-9]/g, '_');
            var context = {};
            for (var i in old_context) {
                context[i] = old_context[i];
            }
            context[val + "_all"] = enu;
            var val_value = val + "_value",
                val_index = val + "_index",
                val_first = val + "_first",
                val_last = val + "_last",
                val_parity = val + "_parity";
            var size = enu.length;
            if (size) {
                context[val + "_size"] = size;
                for (var j = 0; j < size; j++) {
                    var cur = enu[j];
                    context[val_value] = cur;
                    context[val_index] = j;
                    context[val_first] = j == 0;
                    context[val_last] = j + 1 == size;
                    context[val_parity] = (j % 2 == 1 ? 'odd' : 'even');
                    if (cur.constructor == Object) {
                        for (var k in cur) {
                            context[k] = cur[k];
                        }
                    }
                    context[val] = cur;
                    var r = this.render_element(e, t_att, g_att, context);
                    ru.push(r);
                }
            } else {
                var index = 0;
                for (cur in enu) {
                    context[val_value] = cur;
                    context[val_index] = index;
                    context[val_first] = index == 0;
                    context[val_parity] = (index % 2 == 1 ? 'odd' : 'even');
                    context[val] = cur;
                    ru.push(this.render_element(e, t_att, g_att, context));
                    index += 1;
                }
            }
            return ru.join("");
        } else {
            return "qweb: foreach " + expr + " not found.";
        }
    },
    hash:function() {
        var l = [], m;
        for (var i in this) {
            if (m = i.match(/render_tag_(.*)/)) {
                this.tag[m[1]] = i;
                l.push(m[1]);
            } else if (m = i.match(/render_att_(.*)/)) {
                this.att[m[1]] = i;
                l.push(m[1]);
            }
        }
        l.sort(function(a, b) {
            return a.length > b.length ? -1 : 1;
        });
        var s = "^" + this.prefix + "-(eval|" + l.join("|") + "|.*)(.*)$";
        this.reg = new RegExp(s);
    },
    /**
     * returns the correct XMLHttpRequest instance for the browser, or null if
     * it was not able to build any XHR instance.
     *
     * @returns XMLHttpRequest|MSXML2.XMLHTTP.3.0|null
     */
    get_xhr:function () {
        if (window.XMLHttpRequest) {
            return new window.XMLHttpRequest();
        }
        try {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        } catch(e) {
            return null;
        }
    },
    load_xml:function(s) {
        var xml;
        if (s[0] == "<") {
            /*
             manque ca pour sarrisa
             if(window.DOMParser){
             mozilla
             if(!window.DOMParser){
             var doc = Sarissa.getDomDocument();
             doc.loadXML(sXml);
             return doc;
             };
             };
             */
        } else {
            var req = this.get_xhr();
            if (req) {
                req.open("GET", s, false);
                req.send(null);
                //if ie r.setRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT");
                xml = req.responseXML;
                /*
                 TODO
                 if intsernetexploror
                 getdomimplmentation() for try catch
                 responseXML.getImplet
                 d=domimple()
                 d.preserverWhitespace=1
                 d.loadXML()

                 xml.preserverWhitespace=1
                 xml.loadXML(r.reponseText)
                 */
                return xml;
            }
        }
    },
    add_template:function(e) {
        // TODO: keep sources so we can implement reload()
        this.hash();
        if (e.constructor == String) {
            e = this.load_xml(e);
        }
        var ec = e.documentElement ? e.documentElement.childNodes
               : e.childNodes ? e.childNodes
               : [];
        for (var i = 0; i < ec.length; i++) {
            var n = ec[i];
            if (n.nodeType == 1) {
                var name = n.getAttribute(this.prefix + "-name");
                this.templates[name] = n;
            }
        }
    },
    render:function(name, v) {
        var e;
        if (e = this.templates[name]) {
            return this.render_node(e, v);
        }
        return "template " + name + " not found";
    }
};

<|endoftext|>


<commit_before>"use strict"

module.exports = SteamCrawler;

const Regex = SteamCrawler.Regex = require('../resources/regex');
const Currency = SteamCrawler.Currency = require('../resources/currency');
const Endpoint = SteamCrawler.Endpoint = require('../resources/endpoint');

const request = require('request-promise');

function SteamCrawler(options) {
    options = options || {};

    // Default currrency
    this.currency = options.currency || Currency.USD;
    // Use HTTP/HTTPS proxy to mask requests
    this.proxy = options.proxy || null;
    // Set timeout for requests
    this.timeout = options.timeout || 5000;
    // Set MAX_RETRIES for requests
    this.maxRetries = options.maxRetries || 3;

    /**
     * Mostly options that are necessary for SteamApis,
     * you can safely ignore these.
     */
    // Use WebProxy to proxy the requests
    this.webProxy = options.webProxy || false;
    // Use Base64 encoded strings as URL's where possible,
    // good when using webProxy - other than that pointless.
    this.base64 = options.base64 || false;
    // Base64 prefix
    this.base64Prefix = options.base64Prefix || 'base64';
}

SteamCrawler.prototype.request = function(url, callback, retries) {
    request({
        url: url,
        proxy: this.proxy,
        headers: {
            'accept-charset': 'utf-8'
        },
        timeout: this.timeout
    }).then((res) => {
        // Successful Response
        if(typeof callback === "function") return callback(null, res);
    }, (reason) => {
        // Request Failure
        if( ! retries) retries = 0;
        else retries++;
        if(retries <= this.maxRetries) {
            this.request(url, callback, retries);
        } else {
            if(typeof callback === "function") return callback(reason);
        }
    });
}

SteamCrawler.prototype.requestJson = function(url, callback, retries) {
    request({
        url: url,
        proxy: this.proxy,
        headers: {
            'accept-charset': 'utf-8'
        },
        timeout: this.timeout
    }).then((res) => {
        // Successful Response
        let error = null;
        // Try to parse response as JSON
        try {
            res = JSON.parse(res);
        } catch(e) {
            error = 'Not valid JSON.';
        }
        // Check if NULL or has error
        if(error === null && (res === null || res.success === false)) {
            error = 'Received "null" or response success was false.';
        }
        if(error) {
            if( ! retries) retries = 0;
            else retries++;
            if(retries <= this.maxRetries) {
                this.requestJson(url, callback, retries);
            }
        }
        if(typeof callback === "function") return callback(error, res);
    }, (reason) => {
        // Request Failure
        if( ! retries) retries = 0;
        else retries++;
        if(retries <= this.maxRetries) {
            this.requestJson(url, callback, retries);
        } else {
            if(typeof callback === "function") return callback(reason);
        }
    });
}

require('./crawl.js');
<commit_msg>Fix double callback issue<commit_after>"use strict"

module.exports = SteamCrawler;

const Regex = SteamCrawler.Regex = require('../resources/regex');
const Currency = SteamCrawler.Currency = require('../resources/currency');
const Endpoint = SteamCrawler.Endpoint = require('../resources/endpoint');

const request = require('request-promise');

function SteamCrawler(options) {
    options = options || {};

    // Default currrency
    this.currency = options.currency || Currency.USD;
    // Use HTTP/HTTPS proxy to mask requests
    this.proxy = options.proxy || null;
    // Set timeout for requests
    this.timeout = options.timeout || 5000;
    // Set MAX_RETRIES for requests
    this.maxRetries = options.maxRetries || 3;

    /**
     * Mostly options that are necessary for SteamApis,
     * you can safely ignore these.
     */
    // Use WebProxy to proxy the requests
    this.webProxy = options.webProxy || false;
    // Use Base64 encoded strings as URL's where possible,
    // good when using webProxy - other than that pointless.
    this.base64 = options.base64 || false;
    // Base64 prefix
    this.base64Prefix = options.base64Prefix || 'base64';
}

SteamCrawler.prototype.request = function(url, callback, retries) {
    request({
        url: url,
        proxy: this.proxy,
        headers: {
            'accept-charset': 'utf-8'
        },
        timeout: this.timeout
    }).then((res) => {
        // Successful Response
        if(typeof callback === "function") return callback(null, res);
    }, (reason) => {
        // Request Failure
        if( ! retries) retries = 0;
        else retries++;
        if(retries <= this.maxRetries) {
            this.request(url, callback, retries);
        } else {
            if(typeof callback === "function") return callback(reason);
        }
    });
}

SteamCrawler.prototype.requestJson = function(url, callback, retries) {
    request({
        url: url,
        proxy: this.proxy,
        headers: {
            'accept-charset': 'utf-8'
        },
        timeout: this.timeout
    }).then((res) => {
        // Successful Response
        let error = null;
        // Try to parse response as JSON
        try {
            res = JSON.parse(res);
        } catch(e) {
            error = 'Not valid JSON.';
        }
        // Check if NULL or has error
        if(error === null && (res === null || res.success === false)) {
            error = 'Received "null" or response success was false.';
        }
        if(error) {
            if( ! retries) retries = 0;
            else retries++;
            if(retries <= this.maxRetries) {
                this.requestJson(url, callback, retries);
            }
        } else {
            if(typeof callback === "function") return callback(error, res);
        }
    }, (reason) => {
        // Request Failure
        if( ! retries) retries = 0;
        else retries++;
        if(retries <= this.maxRetries) {
            this.requestJson(url, callback, retries);
        } else {
            if(typeof callback === "function") return callback(reason);
        }
    });
}

require('./crawl.js');
<|endoftext|>


<commit_before>//get Master firefox version
function getFirefoxMasterVersion(userAgent) {
    var version = 0;
    var ua = userAgent || navigator.userAgent;

    var matches = /Firefox\/([0-9]+).[0-9]+(?:.[0-9]+)?/.exec(
        ua
    );

    if (matches !== null && matches.length > 0) {
        version = parseInt(matches[1], 10);
    }

    return version;
}

function isFirefox(userAgent) {
    var ua = userAgent || navigator.userAgent;
    // camino UA string contains 'like Firefox'
    return (
        (/\sFirefox/).test(ua) &&
        !(/like Firefox/i).test(ua) &&
        !(/SeaMonkey/i).test(ua)
    );
}

function isAustralis() {
    // Check if the visitor is using Firefox and it's v29+
    if (isFirefox()) {
        var fx_version = getFirefoxMasterVersion();
        var minimumAustralisVersion = 29;
        return (fx_version >= minimumAustralisVersion);
    } else {
        return false;
    }
}

var _currentDataTimestamp = 0;

// TODO: make this better and less hacky
// should not be in global scope, nor should
// most of this file.
window.setInterval(function(){
    // bump forward 1 minute
    if (_currentDataTimestamp) {
        // only move forward if it's been set
        _currentDataTimestamp += 60;
    }
}, 60000);  // 60s

function getDevTimestamp() {
    // TODO REMOVE ME
    // confirmed manually that this is equal to
    // smithers.utils.get_epoch_minute()
    var unixtime = (new Date()).getTime() / 1000  // seconds since epoch
    return Math.floor(unixtime / 60) * 60  // round down to minute
}

function getJsonDataUrl() {
    var $body = $('body');
    var staticDataUrl = $body.data('staticDataUrl');

    if (!_currentDataTimestamp) {
        // NOTE: will be 0 when redis is off or returns nothing
        var latestTimestamp = $body.data('timestamp'); // an int

        if (!latestTimestamp) {
            // TODO REMOVE ME
            // guess at the latest timestamp (for dev)
            console.log('USING DEV TIMESTAMP!');
            // take off 3 extra minutes for fewer 404s
            latestTimestamp = getDevTimestamp() - 180;
        }

        // two minutes ago
        _currentDataTimestamp = latestTimestamp - 120;
    }
    console.log('Current timestamp', _currentDataTimestamp);
    var url = staticDataUrl + 'stats/' + _currentDataTimestamp + '.json';
    console.log(url);
    return url;
}

var $stats_panel_tab_title = $('.stats-panel-tab .title');
var stats_panel_opened_title = $stats_panel_tab_title.data('open-title');
var stats_panel_closed_title = $stats_panel_tab_title.text();

function updateStatsPanel() {
    if ($('body').hasClass('stats-panel-open')) {
        hideGlows();
        $stats_panel_tab_title.text(stats_panel_opened_title);
    } else {
        showGlows();
        $stats_panel_tab_title.text(stats_panel_closed_title);
    }
}

$(document).ready(function () {

    var hasMediaQueries = ('matchMedia' in window);
    var mode = 'mobile';

    function checkMode() {
        var current_mode = getMode();
        if (mode !== current_mode) {
            mode = current_mode;
            setMode(mode);
        }
    }

    function getMode() {
        if (hasMediaQueries && matchMedia('(min-width: 768px)').matches) {
            return 'desktop';
        }
        return 'mobile';
    }

    function setMode(mode) {
        if (mode === 'desktop') {
            // move choices to choice-modal
            $('.choices-wrapper').appendTo($('#choice-modal-choice-page'));

            // move links from hamburger menu to desktop UI
            $('.footer-link').appendTo($('footer ul'));
            $('.video-panel-link').prependTo($('.actions'));

            // move stats panel to desktop
            $('.stats-panel-contents')
                .append($('.stats-panel-column'))
                .append($('.stats-panel-column-right'));

            // hide the menu if it is open
            $('.mobile-menu').removeClass('open');

            showGlows();
        } else {
            hideGlows();

            // move choices to mobile
            $('.choices-wrapper').prependTo($('.choices-mobile'));

            // move links from main UI to hanburger menu
            $('.mobile-menu .dropdown-menu')
              .append($('.footer-link'))
              .prepend($('.video-panel-link'));

            // move stats content to mobile
            $('.stats-mobile-content').append($('.stats-panel-column'));
            $('.stats-panel-column-right').insertBefore($('.stats-panel-column .chart2'));
        }
    }

    if (hasMediaQueries) {
        checkMode();
        $(window).on('resize', function () {
            checkMode();
        });
    }

    var $choice_modal = $('.choice-modal');
    var $choices = $('.choices .btn');
    var events = 'transitionend';

    var events = (function() {
        var el = document.createElement('div');
        var transitions = {
            'transition': 'transitionend',
            'OTransition': 'oTransitionEnd',
            'MSTransition': 'msTransitionEnd',
            'MozTransition': 'transitionend',
            'WebkitTransition': 'webkitTransitionEnd'
        }

        for (var selector in transitions){
            if (el.style[selector] !== undefined) {
                return transitions[selector];
            }
        }

        return null;
    })();

    $choice_modal.find('.choice-body-container').on(
        events,
        function (event) {
            var $body = $(this);
            if (event.target === event.currentTarget) {
                var state = $body.data('state');
                if (state === 'opening-opacity-out') {
                    $('.choices').removeClass('in-progress');
                    $choices.each(function(i) {
                        $(this).removeClass('selected');
                    });

                    var from_height = $body.height();

                    // We need to ignore this transition for Safari. Otherwise
                    // it will animate from height 0 to from_height.
                    var transition = $body.css('transition');
                    $body
                        .css('transition', 'none')
                        .css('height', from_height);

                    $choice_modal.addClass('choice-modal-interstitial');

                    var to_height = $body.find('.modal-body').outerHeight();

                    // Hack for Safari 7. If this is not added, the CSS
                    // transitions on the $body do not run.
                    $choice_modal.find('.modal-dialog').css('transform', 'translate(0, 1px)');

                    // Yield to allow the browser to draw at from_height
                    setTimeout(function() {
                        // Re-enable transitions. Yield to allow the browser
                        // to register the transitions.
                        $body.css('transition', transition);
                        setTimeout(function() {
                            $body
                                .data('state', 'opening-height')
                                .css('height', to_height);

                            // Hack for Safari 7. If this is not added, the CSS
                            // transitions on the $body do not run.
                            $choice_modal.find('.modal-dialog').css('transform', 'translate(0, 0)');
                        }, 0);
                    }, 0);
                } else if (state === 'opening-height') {
                    $body.data('state', 'opening-opacity-in').css('opacity', 1);
                } else if (state === 'opening-opacity-in') {
                    $body.data('state', '');
                }
            }
        }
    );

    function openInterstitialModal(choice) {
        $choice_modal.find('.choice-body-container')
            .data('state', 'opening-opacity-out')
            .css('opacity', 0);

        $choices.each(function(i) {
            $choice_modal.removeClass('interstitial-modal-' + $(this).data('choice'));
        });

        if (choice) {
            $choice_modal.addClass('interstitial-modal-' + choice);
        }
    }

    function handleChoiceModalOpen() {
        var $choice_modal = $(this);

        // remove any selection
        $choices.each(function(i) {
            $choice_modal.removeClass('interstitial-modal-' + $(this).data('choice'));
        });

        // make sure we're on the first page
        $choice_modal.removeClass('choice-modal-interstitial')
        $choice_modal.find('.choice-body-container').css('height', 'auto');

        // center it vertically
        $choice_modal.css('display', 'block');
        var $dialog = $choice_modal.find('.modal-dialog');

        setTimeout(function() {
            var offset = Math.max(
                ($(window).height() - $dialog.height()) / 2,
                20
            );
            $dialog.css('margin-top', offset);
        }, 20);
    }

    $choice_modal.on('show.bs.modal', handleChoiceModalOpen);

    var hash = window.location.hash;

    if (isAustralis()) {
        // if the browser is Australis, add a class to HTML tag
        $('html').addClass('australis');
    } else {
        $('html').addClass('non-australis');
    }

    if (hash === '#choice') {
        $choice_modal.modal();
    } else if (hash === '#video') {
        var onVideoPath = true;
        $('#video-modal').modal();
        insertVideo();
    } else if (hash === '#number') {
        $('#number-modal').modal();
    } else if (/^#stats/.test(hash)) {
        // if URL starts with #stats, show the stats modal
        $('body').addClass('stats-panel-open');
        updateStatsPanel();
    } else if (getMode() === 'desktop') {
        // if there are no known URL fragments and we're on desktop,
        // then open choice modal
        $choice_modal.modal();
    }

    $( '.stats-panel-tab' ).click(function() {
        $('body').toggleClass('stats-panel-open');
        updateStatsPanel();
    });

    // turn on fading for choice modal after initial load so it fades
    // on subsequent open/closes, but not the first time
    $choice_modal.addClass('fade');

    // "Share the map" popopver
    $('.popover-markup > .trigger').popover({
        html : true,
        title: function() {
          return $(this).parent().find('.head').html();
        },
        content: function() {
          return $(this).parent().find('.content').html();
        },
        container: 'body'
    });

    // Close popover on click outside of popover
    $('body').on('click', function (e) {
        $('[data-toggle="popover"]').each(function () {
            //the 'is' for buttons that trigger popups
            //the 'has' for icons within a button that triggers a popup
            if (!$(this).is(e.target) &&
                $(this).has(e.target).length === 0 &&
                $('.popover').has(e.target).length === 0) {
                $(this).popover('hide');
            }
        });
    });

    function openShareWindow(href) {
        $('.popover-markup  .trigger').popover('hide');
        window.open(href, '_blank', "height=420,width=550");
    }

    function shareChoice(choice) {
        var url = $choice_modal.data('share-url');
        if (url) {
            $.post(url, { 'issue': choice });
        }
    }

    $('.mobile-choice-link').click(function(e) {
        e.preventDefault();
        $choices.removeClass('selected');
        $('.choices').removeClass('in-progress');
        $('body').removeClass('stats-panel-open');
        updateStatsPanel();
    });

    $choices.click(function() {
        $choices.removeClass('selected');
        $(this).addClass('selected');
        $('.choices').addClass('in-progress');

        shareChoice($(this).data('choice'));

        if (getMode() === 'mobile') {
            $('body')
                .addClass('stats-panel-open')
                .scrollTop(0);

            updateStatsPanelChoice($(this).data('choice'));
            updateStatsPanel();
        } else {
            openInterstitialModal($(this).data('choice'));
        }
    });

    $('.stats-mobile-picker').click(function(event) {
        var n = event.target;
        var pass_click = true;
        while (n !== this) {
            if (n.nodeName === 'SELECT') {
                pass_click = false;
                break;
            }
            n = n.parentNode;
        }
        if (pass_click) {
            // Using a regular $.click() doesn't work here. We use the DOM
            // Level 2 Events API instead.
            var e = document.createEvent('MouseEvents');
            e.initMouseEvent('mousedown', true, true, window);
            var $select = $(this).find('select');
            $select.get(0).dispatchEvent(e);
        }
    });

    // Open .share-window links in a new window, and close any popovers
    $(document).on('click', '.share-window', function(event) {
        event.preventDefault();

        // TODO: This is hackey, due to how coded i cant access events
        // in ga_event-tracking.js so putting here.
        var shareType = $(this).data('ga');
        var shareChoice = $(this).find('span.title').html();

        ga('send', 'event', shareType, shareChoice);

        openShareWindow(this.href);
    });

    // Insert YouTube video into #video modal
    function insertVideo() {

        $('#video-player').tubeplayer({
            width: 853, // the width of the player
            height: 480, // the height of the player
            allowFullScreen: "true", // true by default, allow user to go full screen
            initialVideo: "Xm5i5kbIXzc", // the video that is loaded into the player
            showinfo: 0,
            autoPlay: 1,
            onPlayerEnded: function(){
                proceedToChoiceModal();
            }, // after the player is stopped
        });

        function proceedToChoiceModal() {
            if (onVideoPath) {
                $('#video-modal').modal('hide');
                $('#choice-modal').modal('show');
            }
            onVideoPath = false;
        }

        $('#video-player').fitVids();

    }

    $('#video-modal').on('show.bs.modal', function (e) {
        // Insert YouTube player when video modal is opened
        insertVideo();
    });

    $('#video-modal').on('hidden.bs.modal', function (e) {
        // Stop YouTube player when video modal is closed
        $('#video-player').tubeplayer('destroy');
    });

    // Hide glows when opening modals
    $('.modal').on('show.bs.modal', function (e) {
        hideGlows();
    });

    // Show glows when closing modals
    $('.modal').on('hidden.bs.modal', function (e) {
        if (!$('body').hasClass('stats-panel-open')) {
            showGlows();
        }
    });

});
<commit_msg>Forced Youtube HTTPS: Fixed youtube content being blocked on dev and prod.<commit_after>//get Master firefox version
function getFirefoxMasterVersion(userAgent) {
    var version = 0;
    var ua = userAgent || navigator.userAgent;

    var matches = /Firefox\/([0-9]+).[0-9]+(?:.[0-9]+)?/.exec(
        ua
    );

    if (matches !== null && matches.length > 0) {
        version = parseInt(matches[1], 10);
    }

    return version;
}

function isFirefox(userAgent) {
    var ua = userAgent || navigator.userAgent;
    // camino UA string contains 'like Firefox'
    return (
        (/\sFirefox/).test(ua) &&
        !(/like Firefox/i).test(ua) &&
        !(/SeaMonkey/i).test(ua)
    );
}

function isAustralis() {
    // Check if the visitor is using Firefox and it's v29+
    if (isFirefox()) {
        var fx_version = getFirefoxMasterVersion();
        var minimumAustralisVersion = 29;
        return (fx_version >= minimumAustralisVersion);
    } else {
        return false;
    }
}

var _currentDataTimestamp = 0;

// TODO: make this better and less hacky
// should not be in global scope, nor should
// most of this file.
window.setInterval(function(){
    // bump forward 1 minute
    if (_currentDataTimestamp) {
        // only move forward if it's been set
        _currentDataTimestamp += 60;
    }
}, 60000);  // 60s

function getDevTimestamp() {
    // TODO REMOVE ME
    // confirmed manually that this is equal to
    // smithers.utils.get_epoch_minute()
    var unixtime = (new Date()).getTime() / 1000  // seconds since epoch
    return Math.floor(unixtime / 60) * 60  // round down to minute
}

function getJsonDataUrl() {
    var $body = $('body');
    var staticDataUrl = $body.data('staticDataUrl');

    if (!_currentDataTimestamp) {
        // NOTE: will be 0 when redis is off or returns nothing
        var latestTimestamp = $body.data('timestamp'); // an int

        if (!latestTimestamp) {
            // TODO REMOVE ME
            // guess at the latest timestamp (for dev)
            console.log('USING DEV TIMESTAMP!');
            // take off 3 extra minutes for fewer 404s
            latestTimestamp = getDevTimestamp() - 180;
        }

        // two minutes ago
        _currentDataTimestamp = latestTimestamp - 120;
    }
    console.log('Current timestamp', _currentDataTimestamp);
    var url = staticDataUrl + 'stats/' + _currentDataTimestamp + '.json';
    console.log(url);
    return url;
}

var $stats_panel_tab_title = $('.stats-panel-tab .title');
var stats_panel_opened_title = $stats_panel_tab_title.data('open-title');
var stats_panel_closed_title = $stats_panel_tab_title.text();

function updateStatsPanel() {
    if ($('body').hasClass('stats-panel-open')) {
        hideGlows();
        $stats_panel_tab_title.text(stats_panel_opened_title);
    } else {
        showGlows();
        $stats_panel_tab_title.text(stats_panel_closed_title);
    }
}

$(document).ready(function () {

    var hasMediaQueries = ('matchMedia' in window);
    var mode = 'mobile';

    function checkMode() {
        var current_mode = getMode();
        if (mode !== current_mode) {
            mode = current_mode;
            setMode(mode);
        }
    }

    function getMode() {
        if (hasMediaQueries && matchMedia('(min-width: 768px)').matches) {
            return 'desktop';
        }
        return 'mobile';
    }

    function setMode(mode) {
        if (mode === 'desktop') {
            // move choices to choice-modal
            $('.choices-wrapper').appendTo($('#choice-modal-choice-page'));

            // move links from hamburger menu to desktop UI
            $('.footer-link').appendTo($('footer ul'));
            $('.video-panel-link').prependTo($('.actions'));

            // move stats panel to desktop
            $('.stats-panel-contents')
                .append($('.stats-panel-column'))
                .append($('.stats-panel-column-right'));

            // hide the menu if it is open
            $('.mobile-menu').removeClass('open');

            showGlows();
        } else {
            hideGlows();

            // move choices to mobile
            $('.choices-wrapper').prependTo($('.choices-mobile'));

            // move links from main UI to hanburger menu
            $('.mobile-menu .dropdown-menu')
              .append($('.footer-link'))
              .prepend($('.video-panel-link'));

            // move stats content to mobile
            $('.stats-mobile-content').append($('.stats-panel-column'));
            $('.stats-panel-column-right').insertBefore($('.stats-panel-column .chart2'));
        }
    }

    if (hasMediaQueries) {
        checkMode();
        $(window).on('resize', function () {
            checkMode();
        });
    }

    var $choice_modal = $('.choice-modal');
    var $choices = $('.choices .btn');
    var events = 'transitionend';

    var events = (function() {
        var el = document.createElement('div');
        var transitions = {
            'transition': 'transitionend',
            'OTransition': 'oTransitionEnd',
            'MSTransition': 'msTransitionEnd',
            'MozTransition': 'transitionend',
            'WebkitTransition': 'webkitTransitionEnd'
        }

        for (var selector in transitions){
            if (el.style[selector] !== undefined) {
                return transitions[selector];
            }
        }

        return null;
    })();

    $choice_modal.find('.choice-body-container').on(
        events,
        function (event) {
            var $body = $(this);
            if (event.target === event.currentTarget) {
                var state = $body.data('state');
                if (state === 'opening-opacity-out') {
                    $('.choices').removeClass('in-progress');
                    $choices.each(function(i) {
                        $(this).removeClass('selected');
                    });

                    var from_height = $body.height();

                    // We need to ignore this transition for Safari. Otherwise
                    // it will animate from height 0 to from_height.
                    var transition = $body.css('transition');
                    $body
                        .css('transition', 'none')
                        .css('height', from_height);

                    $choice_modal.addClass('choice-modal-interstitial');

                    var to_height = $body.find('.modal-body').outerHeight();

                    // Hack for Safari 7. If this is not added, the CSS
                    // transitions on the $body do not run.
                    $choice_modal.find('.modal-dialog').css('transform', 'translate(0, 1px)');

                    // Yield to allow the browser to draw at from_height
                    setTimeout(function() {
                        // Re-enable transitions. Yield to allow the browser
                        // to register the transitions.
                        $body.css('transition', transition);
                        setTimeout(function() {
                            $body
                                .data('state', 'opening-height')
                                .css('height', to_height);

                            // Hack for Safari 7. If this is not added, the CSS
                            // transitions on the $body do not run.
                            $choice_modal.find('.modal-dialog').css('transform', 'translate(0, 0)');
                        }, 0);
                    }, 0);
                } else if (state === 'opening-height') {
                    $body.data('state', 'opening-opacity-in').css('opacity', 1);
                } else if (state === 'opening-opacity-in') {
                    $body.data('state', '');
                }
            }
        }
    );

    function openInterstitialModal(choice) {
        $choice_modal.find('.choice-body-container')
            .data('state', 'opening-opacity-out')
            .css('opacity', 0);

        $choices.each(function(i) {
            $choice_modal.removeClass('interstitial-modal-' + $(this).data('choice'));
        });

        if (choice) {
            $choice_modal.addClass('interstitial-modal-' + choice);
        }
    }

    function handleChoiceModalOpen() {
        var $choice_modal = $(this);

        // remove any selection
        $choices.each(function(i) {
            $choice_modal.removeClass('interstitial-modal-' + $(this).data('choice'));
        });

        // make sure we're on the first page
        $choice_modal.removeClass('choice-modal-interstitial')
        $choice_modal.find('.choice-body-container').css('height', 'auto');

        // center it vertically
        $choice_modal.css('display', 'block');
        var $dialog = $choice_modal.find('.modal-dialog');

        setTimeout(function() {
            var offset = Math.max(
                ($(window).height() - $dialog.height()) / 2,
                20
            );
            $dialog.css('margin-top', offset);
        }, 20);
    }

    $choice_modal.on('show.bs.modal', handleChoiceModalOpen);

    var hash = window.location.hash;

    if (isAustralis()) {
        // if the browser is Australis, add a class to HTML tag
        $('html').addClass('australis');
    } else {
        $('html').addClass('non-australis');
    }

    if (hash === '#choice') {
        $choice_modal.modal();
    } else if (hash === '#video') {
        var onVideoPath = true;
        $('#video-modal').modal();
        insertVideo();
    } else if (hash === '#number') {
        $('#number-modal').modal();
    } else if (/^#stats/.test(hash)) {
        // if URL starts with #stats, show the stats modal
        $('body').addClass('stats-panel-open');
        updateStatsPanel();
    } else if (getMode() === 'desktop') {
        // if there are no known URL fragments and we're on desktop,
        // then open choice modal
        $choice_modal.modal();
    }

    $( '.stats-panel-tab' ).click(function() {
        $('body').toggleClass('stats-panel-open');
        updateStatsPanel();
    });

    // turn on fading for choice modal after initial load so it fades
    // on subsequent open/closes, but not the first time
    $choice_modal.addClass('fade');

    // "Share the map" popopver
    $('.popover-markup > .trigger').popover({
        html : true,
        title: function() {
          return $(this).parent().find('.head').html();
        },
        content: function() {
          return $(this).parent().find('.content').html();
        },
        container: 'body'
    });

    // Close popover on click outside of popover
    $('body').on('click', function (e) {
        $('[data-toggle="popover"]').each(function () {
            //the 'is' for buttons that trigger popups
            //the 'has' for icons within a button that triggers a popup
            if (!$(this).is(e.target) &&
                $(this).has(e.target).length === 0 &&
                $('.popover').has(e.target).length === 0) {
                $(this).popover('hide');
            }
        });
    });

    function openShareWindow(href) {
        $('.popover-markup  .trigger').popover('hide');
        window.open(href, '_blank', "height=420,width=550");
    }

    function shareChoice(choice) {
        var url = $choice_modal.data('share-url');
        if (url) {
            $.post(url, { 'issue': choice });
        }
    }

    $('.mobile-choice-link').click(function(e) {
        e.preventDefault();
        $choices.removeClass('selected');
        $('.choices').removeClass('in-progress');
        $('body').removeClass('stats-panel-open');
        updateStatsPanel();
    });

    $choices.click(function() {
        $choices.removeClass('selected');
        $(this).addClass('selected');
        $('.choices').addClass('in-progress');

        shareChoice($(this).data('choice'));

        if (getMode() === 'mobile') {
            $('body')
                .addClass('stats-panel-open')
                .scrollTop(0);

            updateStatsPanelChoice($(this).data('choice'));
            updateStatsPanel();
        } else {
            openInterstitialModal($(this).data('choice'));
        }
    });

    $('.stats-mobile-picker').click(function(event) {
        var n = event.target;
        var pass_click = true;
        while (n !== this) {
            if (n.nodeName === 'SELECT') {
                pass_click = false;
                break;
            }
            n = n.parentNode;
        }
        if (pass_click) {
            // Using a regular $.click() doesn't work here. We use the DOM
            // Level 2 Events API instead.
            var e = document.createEvent('MouseEvents');
            e.initMouseEvent('mousedown', true, true, window);
            var $select = $(this).find('select');
            $select.get(0).dispatchEvent(e);
        }
    });

    // Open .share-window links in a new window, and close any popovers
    $(document).on('click', '.share-window', function(event) {
        event.preventDefault();

        // TODO: This is hackey, due to how coded i cant access events
        // in ga_event-tracking.js so putting here.
        var shareType = $(this).data('ga');
        var shareChoice = $(this).find('span.title').html();

        ga('send', 'event', shareType, shareChoice);

        openShareWindow(this.href);
    });

    // Insert YouTube video into #video modal
    function insertVideo() {

        $('#video-player').tubeplayer({
            width: 853, // the width of the player
            height: 480, // the height of the player
            allowFullScreen: "true", // true by default, allow user to go full screen
            initialVideo: "Xm5i5kbIXzc", // the video that is loaded into the player
            showinfo: 0,
            autoPlay: 1,
            protocol: 'https',
            onPlayerEnded: function(){
                proceedToChoiceModal();
            }, // after the player is stopped
        });

        function proceedToChoiceModal() {
            if (onVideoPath) {
                $('#video-modal').modal('hide');
                $('#choice-modal').modal('show');
            }
            onVideoPath = false;
        }

        $('#video-player').fitVids();

    }

    $('#video-modal').on('show.bs.modal', function (e) {
        // Insert YouTube player when video modal is opened
        insertVideo();
    });

    $('#video-modal').on('hidden.bs.modal', function (e) {
        // Stop YouTube player when video modal is closed
        $('#video-player').tubeplayer('destroy');
    });

    // Hide glows when opening modals
    $('.modal').on('show.bs.modal', function (e) {
        hideGlows();
    });

    // Show glows when closing modals
    $('.modal').on('hidden.bs.modal', function (e) {
        if (!$('body').hasClass('stats-panel-open')) {
            showGlows();
        }
    });

});
<|endoftext|>