<commit_before>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        this.$el = document.getElementById(_el);
        this.html = this.$el.innerHTML;
        this.dom = {type: this.$el.nodeName, children: [], node: this.$el};

        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.dom.children);
            }
        });

        this.build = function(children) {
          var tempData = this.$data;
          for(var i = 0; i < children.length; i++) {
            var el = children[i];
            if(el.type === "#text") {
              el.val.replace(/{{(\w+)}}/gi, function(match, p1) {
                var dataToAdd = tempData[p1];
                el.node.textContent = el.val.replace(new RegExp(match, "gi"), dataToAdd);
              });
            } else {
               this.build(el.children);
            }
          }
        }

        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, child));
          }
          return recursiveChildrenArr;
        }

        this.createElement = function(type, children, val, node) {
          return {type: type, children: children, val: val, node: node};
        }

        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, node);
          this.dom = vdom;
        }

        this.seed = function() {
          this.createVirtualDOM(this.$el);
        }

        this.extractAttrs = function(node) {
          var attrs = {};
          var rawAttrs = node.attributes;
          for(var i = 0; i < rawAttrs.length; i++) {
            attrs[rawAttrs[i].name] = rawAttrs[i].value
          }

          return attrs;
        }

        this.set = function(key, val) {
          this.$data[key] = val;
          this.build(this.dom.children);
        }

        this.get = function(key) {
          return this.$data[key];
        }

        this.ajax = function(method, url, params, cb) {
          var xmlHttp = new XMLHttpRequest();
          method = method.toUpperCase();
          if(typeof params === "function") {
            cb = params;
          }
          var urlParams = "?";
          if(method === "POST") {
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            for(var param in params) {
              urlParams += param + "=" + params[param] + "&";
            }
          }
          xmlHttp.onreadystatechange = function() {
          if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
            cb(JSON.parse(xmlHttp.responseText));
          }
          xmlHttp.open(method, url, true);
          xmlHttp.send(method === "POST" ? urlParams : null);
        }

        this.method = function(method) {
          _methods[method]();
        }

        this.seed();
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<commit_msg>if text return attributes<commit_after>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        this.$el = document.getElementById(_el);
        this.html = this.$el.innerHTML;
        this.dom = {type: this.$el.nodeName, children: [], node: this.$el};

        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.dom.children);
            }
        });

        this.build = function(children) {
          var tempData = this.$data;
          for(var i = 0; i < children.length; i++) {
            var el = children[i];
            if(el.type === "#text") {
              el.val.replace(/{{(\w+)}}/gi, function(match, p1) {
                var dataToAdd = tempData[p1];
                el.node.textContent = el.val.replace(new RegExp(match, "gi"), dataToAdd);
              });
            } else {
               this.build(el.children);
            }
          }
        }

        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, child));
          }
          return recursiveChildrenArr;
        }

        this.createElement = function(type, children, val, node) {
          return {type: type, children: children, val: val, node: node};
        }

        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, node);
          this.dom = vdom;
        }

        this.seed = function() {
          this.createVirtualDOM(this.$el);
        }

        this.extractAttrs = function(node) {
          var attrs = {};
          if(node.nodeName === "#text") return attrs;
          var rawAttrs = node.attributes;
          for(var i = 0; i < rawAttrs.length; i++) {
            attrs[rawAttrs[i].name] = rawAttrs[i].value
          }

          return attrs;
        }

        this.set = function(key, val) {
          this.$data[key] = val;
          this.build(this.dom.children);
        }

        this.get = function(key) {
          return this.$data[key];
        }

        this.ajax = function(method, url, params, cb) {
          var xmlHttp = new XMLHttpRequest();
          method = method.toUpperCase();
          if(typeof params === "function") {
            cb = params;
          }
          var urlParams = "?";
          if(method === "POST") {
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            for(var param in params) {
              urlParams += param + "=" + params[param] + "&";
            }
          }
          xmlHttp.onreadystatechange = function() {
          if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
            cb(JSON.parse(xmlHttp.responseText));
          }
          xmlHttp.open(method, url, true);
          xmlHttp.send(method === "POST" ? urlParams : null);
        }

        this.method = function(method) {
          _methods[method]();
        }

        this.seed();
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<|endoftext|>


<commit_before>/**
 * Created by 清月_荷雾 on 14-2-16.
 * @author 清月_荷雾(441984145@qq.com)
 *        赤菁风铃(liuxuanzy@qq.com)
 * 核心工具类（所有相关插件会注入到此文件中）
 */

var core = {},//核心类
    util = core.util = require("./util/util"),
    dom = core.dom = require("./util/dom"),
    ui = core.ui = {},//页面框架对象
    widget = core.widget = {},//页面公用组件
    view = core.view = {},//所有视图
    transforms = {//页面切换样式（默认提供了fade、pop、horizontal、vertical四种样式，可自定义）
        fade: {
            href: "fade-in",
            back: "fade-out"
        },
        pop: {
            href: "pop-in",
            back: "pop-out"
        },
        horizontal: {
            href: "horizontal-in",
            back: "horizontal-out"
        },
        vertical: {
            href: "vertical-in",
            back: "vertical-out"
        },
        none: {
            href: "",
            back: ""
        }
    },
    transformString = "",
    root = core.root = dom.parse('

')[0],
    prevent = false,
    zindex = 10001,
    backStack = [],//回退栈
    current = null; //当前页面
/**
 * 重新生成transformString
 * @private
 */
function _fixTransformString() {
    var arr = [], i;
    for (i in transforms) {
        if (transforms.hasOwnProperty(i)) {
            arr.push(transforms[i].href);
            arr.push(transforms[i].back);
        }
    }
    transformString = arr.join(" ");
}

/**
 * 注册相关插件
 * @param type 注册类型（请注意规避plugin、ui、view、widget、root、register、href、back、config）另外请确保plugin与util不会重复，否则会发生覆盖
 * @param obj 注册的对象
 */
function register(type, obj) {
    if (type === "ui") {//注入UI相关工具
        util.merge(ui, obj);
    } else if (type === "widget") {//注入公用组件
        widget[obj] = arguments[2];
    } else if (type === "view") {//注入视图
        view[obj] = arguments[2];
    } else if (type === "transform") {//注入切换样式
        transforms[obj] = arguments[2];
        _fixTransformString();
    } else {//注册其它使用工具或者插件，请注意是系统保留控件，请不要使用这几个名字
        //排除所有与系统函数重名的插件，加快效率
        if (type in core) {
            return;
        }
        core[type] = obj;
    }
}
core.register = register;

/**
 * 导航页面到name
 * @param name 页面的名称，为back时回退页面
 * @param [data] 导航数据
 * @param [style] {string} 页面的切换类型
 * @param [transform] {string} 页面切换的样式
 */
function href(name, data, style, transform) {
    if (prevent) {
        return;
    }
    if (name === "back") {//回退快捷方式
        back(data, style, transform);
        return;
    }
    try {
        var createFunc = view[name],
            i = 0;
        prevent = true;
        createFunc(core, data, function (item) {
            dom.style(item._dom, "z-index", zindex++);
            style = item._style = style || "none";//页面样式

            function done() {
                var arr, j;
                item.emit("load");
                //从dom树上摘除当前页
                if (style !== "frame" && current) {
                    current.detach();
                }
                if (style === "switch" && current && current.style === "switch") {
                    current.destroy(true);
                    current = null;
                } else if (style === "switch") {
                    for (i = backStack.length - 1; i >= 0; i++) {
                        if (backStack[i].style === "switch") {
                            arr = backStack.splice(i, backStack.length - i);
                            for (j = 0; j < arr.length; j++) {
                                arr[j].destroy(true);
                            }
                            current.destroy(true);
                            current = null;
                            break;
                        }
                    }
                }
                if (current) {
                    backStack.push(current);//缓存当前页
                }
                current = item;//重新指定当前页
                //检查回退标识并释放资源
                if (style === "none") {
                    for (i = 0; i < backStack.length; i++) {
                        backStack[i].destroy(true);
                    }
                    backStack = [];
                }
                prevent = false;
            }

            dom.removeClass(item._dom, transformString);
            transform = item._transform = transform || "none";
            if (transform !== "none") {
                dom.addClass(item._dom, transforms[transform].href);
                setTimeout(done, 400);
                item.attach(root);
            } else {
                item.attach(root);
                done();
            }
        });
    } catch (e) {
        prevent = false;
        window.console.log("创建页面时出错：" + e.message);
    }
}
core.href = href;

/**
 * 回退页面
 * @param data 导航数据
 * @param [style] {string} 页面的切换类型
 * @param [transform] {string} 页面切换的样式
 */
function back(data, style, transform) {
    if (backStack.length === 0 || !current || current.style === "none") {//已经回退完毕
        return false;
    }
    var item = current;
    style = style || item._style || "none";//页面样式
    current = backStack.pop();//获取上一页面
    dom.removeClass(current._dom, transformString);
    if (style !== "frame") {
        current.attach(root);//恢复当前页
    }
    current.emit("back", data);//触发回退页面的back事件

    function done() {
        item.emit("unload");
        item.destroy(true);//销毁页面元素，并清理元素内部的事件，释放内存
    }

    dom.removeClass(item._dom, transformString);
    transform = transform || item._transform || "none";

    if (transform !== "none") {
        dom.addClass(item._dom, transforms[transform].back);
        setTimeout(done, 400);
    } else {
        done();
    }
    return true;
}
core.back = back;

//init
core.plugin = require("./plugin/plugin");
_fixTransformString();
document.body.appendChild(root);
module.exports = core;
<commit_msg>change version to 0.2.0, add more tools in dom.js and util.js;change the way of page change<commit_after>/**
 * Created by 清月_荷雾 on 14-2-16.
 * @author 清月_荷雾(441984145@qq.com)
 *        赤菁风铃(liuxuanzy@qq.com)
 * 核心工具类（所有相关插件会注入到此文件中）
 */

var core = {},//核心类
    util = core.util = require("./util/util"),
    dom = core.dom = require("./util/dom"),
    ui = core.ui = {},//页面框架对象
    widget = core.widget = {},//页面公用组件
    view = core.view = {},//所有视图
    transforms = {//页面切换样式（默认提供了fade、pop、horizontal、vertical四种样式，可自定义）
        fade: {
            href: "fade-in",
            back: "fade-out"
        },
        pop: {
            href: "pop-in",
            back: "pop-out"
        },
        horizontal: {
            href: "horizontal-in",
            back: "horizontal-out"
        },
        vertical: {
            href: "vertical-in",
            back: "vertical-out"
        },
        none: {
            href: "",
            back: ""
        }
    },
    transformString = "",
    root = core.root = dom.parse('

')[0],
    prevent = false,
    zindex = 10001,
    backStack = [],//回退栈
    current = null; //当前页面
/**
 * 重新生成transformString
 * @private
 */
function _fixTransformString() {
    var arr = [], i;
    for (i in transforms) {
        if (transforms.hasOwnProperty(i)) {
            arr.push(transforms[i].href);
            arr.push(transforms[i].back);
        }
    }
    transformString = arr.join(" ");
}

/**
 * 注册相关插件
 * @param type 注册类型（请注意规避plugin、ui、view、widget、root、register、href、back、config）另外请确保plugin与util不会重复，否则会发生覆盖
 * @param obj 注册的对象
 */
function register(type, obj) {
    if (type === "ui") {//注入UI相关工具
        util.merge(ui, obj);
    } else if (type === "widget") {//注入公用组件
        widget[obj] = arguments[2];
    } else if (type === "view") {//注入视图
        view[obj] = arguments[2];
    } else if (type === "transform") {//注入切换样式
        transforms[obj] = arguments[2];
        _fixTransformString();
    } else {//注册其它使用工具或者插件，请注意是系统保留控件，请不要使用这几个名字
        //排除所有与系统函数重名的插件，加快效率
        if (type in core) {
            return;
        }
        core[type] = obj;
    }
}
core.register = register;

/**
 * 导航页面到name
 * @param name 页面的名称，为back时回退页面
 * @param [data] 导航数据
 * @param [style] {string} 页面的切换类型
 * @param [transform] {string} 页面切换的样式
 */
function href(name, data, style, transform) {
    if (prevent) {
        return;
    }
    if (name === "back") {//回退快捷方式
        back(data, style, transform);
        return;
    }
    try {
        var createFunc = view[name],
            i = 0;
        prevent = true;
        createFunc(core, data, function (item) {
            dom.style(item._dom, "z-index", zindex++);
            style = item._style = style || "none";//页面样式

            function done() {
                var arr, j;
                item.emit("load");
                //从dom树上摘除当前页
                if (style !== "frame" && current) {
                    current.detach();
                }
                if (style === "switch" && current && current.style === "switch") {
                    current.destroy(true);
                    current = null;
                } else if (style === "switch") {
                    for (i = backStack.length - 1; i >= 0; i++) {
                        if (backStack[i].style === "switch") {
                            arr = backStack.splice(i, backStack.length - i);
                            for (j = 0; j < arr.length; j++) {
                                arr[j].destroy(true);
                            }
                            current.destroy(true);
                            current = null;
                            break;
                        }
                    }
                }
                if (current) {
                    backStack.push(current);//缓存当前页
                }
                current = item;//重新指定当前页
                //检查回退标识并释放资源
                if (style === "none") {
                    for (i = 0; i < backStack.length; i++) {
                        backStack[i].destroy(true);
                    }
                    backStack = [];
                }
                prevent = false;
            }

            dom.removeClass(item._dom, transformString);
            transform = item._transform = transform || "none";
            if (transform !== "none") {
                dom.addClass(item._dom, transforms[transform].href);
                setTimeout(done, 400);
                item.attach(root);
            } else {
                item.attach(root);
                done();
            }
        });
    } catch (e) {
        prevent = false;
        window.console.log("创建页面时出错：" + e.message);
    }
}
core.href = href;

/**
 * 回退页面
 * @param data 导航数据
 * @param [style] {string} 页面的切换类型
 * @param [transform] {string} 页面切换的样式
 */
function back(data, style, transform) {
    if (backStack.length === 0 || !current || current.style === "none") {//已经回退完毕
        return false;
    }
    var item = current;
    style = style || item._style || "none";//页面样式
    current = backStack.pop();//获取上一页面
    dom.removeClass(current._dom, transformString);
    if (style !== "frame") {
        current.attach(root);//恢复当前页
    }
    current.emit("back", data);//触发回退页面的back事件

    function done() {
        item.emit("unload");
        item.destroy(true);//销毁页面元素，并清理元素内部的事件，释放内存
    }

    dom.removeClass(item._dom, transformString);
    transform = transform || item._transform || "none";

    if (transform !== "none") {
        dom.addClass(item._dom, transforms[transform].back);
        setTimeout(done, 250);
    } else {
        done();
    }
    return true;
}
core.back = back;

//init
core.plugin = require("./plugin/plugin");
_fixTransformString();
document.body.appendChild(root);
module.exports = core;
<|endoftext|>


<commit_before>// Arbitrary numbers chosen to ensure Long Portrait mode fits into Chrome on Android.
// Firefox uses less vertical space for the URL bar so Long Portrait will be available in both.
// 270 is six times 45, as board is twelve squares and logo is two.
// 73 is the remaining 3.24444 squares that can fit into the Chrome window.
// Scaled up until all values are integers for ease of reading.
const THRESHOLD_LongPortraitFitToWidth = 270/(45+270+73);
const THRESHOLD_LongPortraitFitToHeight = (45+270)/(45+270+73);
const THRESHOLD_ShortPortraitFitToWidth = (45+270)/(270+73);
const THRESHOLD_ShortPortraitFitToHeight = 1;
const THRESHOLD_ShortLandscapeFitToWidth = (270+73)/(45+270);
const THRESHOLD_ShortLandscapeFitToHeight = (45+270+73)/(45+270);
const THRESHOLD_LongLandscapeFitToWidth = (45+270+73)/270;

function reorganise() {
  ratio = window.innerWidth / window.innerHeight;
  if (ratio < THRESHOLD_LongPortraitFitToWidth) {arrange_LongPortraitFitToWidth();
  } else if (ratio < THRESHOLD_LongPortraitFitToHeight) {arrange_LongPortraitFitToHeight();
  } else if (ratio < THRESHOLD_ShortPortraitFitToWidth) {arrange_ShortPortraitFitToWidth();
  } else if (ratio < THRESHOLD_ShortPortraitFitToHeight) {arrange_ShortPortraitFitToHeight();
  } else if (ratio < THRESHOLD_ShortLandscapeFitToWidth) {arrange_ShortLandscapeFitToWidth();
  } else if (ratio < THRESHOLD_ShortLandscapeFitToHeight) {arrange_ShortLandscapeFitToHeight();
  } else if (ratio < THRESHOLD_LongLandscapeFitToWidth) {arrange_LongLandscapeFitToWidth();
  } else {arrange_LongLandscapeFitToHeight();
  }
}

function arrange_LongPortraitFitToWidth() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerWidth;
  document.getElementById('div-title-north').style.width = w.toString().concat('px');
  document.getElementById('div-title-north').style.height = (w/6).toString().concat('px');
  document.getElementById('div-board-area').style.width = w.toString().concat('px');
  document.getElementById('div-board-area').style.height = w.toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*7/6).toString().concat('px');

  document.getElementById('board-canvas').style.width = w.toString().concat('px');
  document.getElementById('board-canvas').style.height = w.toString().concat('px');

  document.getElementById('div-logo-north').style.width = (w*4/6).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (w/6).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (w/6).toString().concat('px');
}

function arrange_LongPortraitFitToHeight() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerHeight * THRESHOLD_LongPortraitFitToWidth;
  document.getElementById('div-title-north').style.width = w.toString().concat('px');
  document.getElementById('div-title-north').style.height = (w/6).toString().concat('px');
  document.getElementById('div-board-area').style.width = w.toString().concat('px');
  document.getElementById('div-board-area').style.height = w.toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*7/6).toString().concat('px');

  document.getElementById('board-canvas').style.width = w.toString().concat('px');
  document.getElementById('board-canvas').style.height = w.toString().concat('px');

  document.getElementById('div-logo-north').style.width = (w*4/6).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (w/6).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (w/6).toString().concat('px');
}

function arrange_ShortPortraitFitToWidth() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerWidth;
  document.getElementById('div-title-west').style.width = (w/7).toString().concat('px');
  document.getElementById('div-title-west').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (w*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (w*6/7).toString().concat('px');

  document.getElementById('div-logo-west').style.height = (w*4/7).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (w/7).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (w/7).toString().concat('px');
}

function arrange_ShortPortraitFitToHeight() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerHeight * THRESHOLD_ShortPortraitFitToWidth;
  document.getElementById('div-title-west').style.width = (w/7).toString().concat('px');
  document.getElementById('div-title-west').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (w*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (w*6/7).toString().concat('px');

  document.getElementById('div-logo-west').style.height = (w*4/7).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (w/7).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (w/7).toString().concat('px');
}

function arrange_ShortLandscapeFitToWidth() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h = window.innerWidth * THRESHOLD_ShortPortraitFitToWidth;
  document.getElementById('div-title-north').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-title-north').style.height = (h/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.height = (h*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (h*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (h*6/7).toString().concat('px');

  document.getElementById('div-logo-north').style.width = (h*4/7).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (h/7).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (h/7).toString().concat('px');
}

function arrange_ShortLandscapeFitToHeight() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h = window.innerHeight;
  document.getElementById('div-title-north').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-title-north').style.height = (h/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.height = (h*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (h*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (h*6/7).toString().concat('px');

  document.getElementById('div-logo-north').style.width = (h*4/7).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (h/7).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (h/7).toString().concat('px');
}

function arrange_LongLandscapeFitToWidth() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h  = window.innerWidth * THRESHOLD_LongPortraitFitToWidth;
  document.getElementById('div-title-west').style.width = (h/6).toString().concat('px');
  document.getElementById('div-title-west').style.height = h.toString().concat('px');
  document.getElementById('div-board-area').style.width = h.toString().concat('px');
  document.getElementById('div-board-area').style.height = h.toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*7/6).toString().concat('px');
  document.getElementById('div-info-east').style.height = h.toString().concat('px');

  document.getElementById('board-canvas').style.width = h.toString().concat('px');
  document.getElementById('board-canvas').style.height = h.toString().concat('px');

  document.getElementById('div-logo-west').style.height = (h*4/6).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (h/6).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (h/6).toString().concat('px');
}

function arrange_LongLandscapeFitToHeight() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h  = window.innerHeight;
  document.getElementById('div-title-west').style.width = (h/6).toString().concat('px');
  document.getElementById('div-title-west').style.height = h.toString().concat('px');
  document.getElementById('div-board-area').style.width = h.toString().concat('px');
  document.getElementById('div-board-area').style.height = h.toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*7/6).toString().concat('px');
  document.getElementById('div-info-east').style.height = h.toString().concat('px');

  document.getElementById('board-canvas').style.width = h.toString().concat('px');
  document.getElementById('board-canvas').style.height = h.toString().concat('px');

  document.getElementById('div-logo-west').style.height = (h*4/6).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (h/6).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (h/6).toString().concat('px');
}
function toggle-full-screen() {
  if (!document.fullscreenElement) {document.body.requestFullscreen} else {document.exitFullscreen()}

}
window.onresize = reorganise;
window.onload = reorganise;
<commit_msg>Missing semicolons and readability<commit_after>// Arbitrary numbers chosen to ensure Long Portrait mode fits into Chrome on Android.
// Firefox uses less vertical space for the URL bar so Long Portrait will be available in both.
// 270 is six times 45, as board is twelve squares and logo is two.
// 73 is the remaining 3.24444 squares that can fit into the Chrome window.
// Scaled up until all values are integers for ease of reading.
const THRESHOLD_LongPortraitFitToWidth = 270/(45+270+73);
const THRESHOLD_LongPortraitFitToHeight = (45+270)/(45+270+73);
const THRESHOLD_ShortPortraitFitToWidth = (45+270)/(270+73);
const THRESHOLD_ShortPortraitFitToHeight = 1;
const THRESHOLD_ShortLandscapeFitToWidth = (270+73)/(45+270);
const THRESHOLD_ShortLandscapeFitToHeight = (45+270+73)/(45+270);
const THRESHOLD_LongLandscapeFitToWidth = (45+270+73)/270;

function reorganise() {
  ratio = window.innerWidth / window.innerHeight;
  if (ratio < THRESHOLD_LongPortraitFitToWidth) {arrange_LongPortraitFitToWidth();
  } else if (ratio < THRESHOLD_LongPortraitFitToHeight) {arrange_LongPortraitFitToHeight();
  } else if (ratio < THRESHOLD_ShortPortraitFitToWidth) {arrange_ShortPortraitFitToWidth();
  } else if (ratio < THRESHOLD_ShortPortraitFitToHeight) {arrange_ShortPortraitFitToHeight();
  } else if (ratio < THRESHOLD_ShortLandscapeFitToWidth) {arrange_ShortLandscapeFitToWidth();
  } else if (ratio < THRESHOLD_ShortLandscapeFitToHeight) {arrange_ShortLandscapeFitToHeight();
  } else if (ratio < THRESHOLD_LongLandscapeFitToWidth) {arrange_LongLandscapeFitToWidth();
  } else {arrange_LongLandscapeFitToHeight();
  }
}

function arrange_LongPortraitFitToWidth() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerWidth;
  document.getElementById('div-title-north').style.width = w.toString().concat('px');
  document.getElementById('div-title-north').style.height = (w/6).toString().concat('px');
  document.getElementById('div-board-area').style.width = w.toString().concat('px');
  document.getElementById('div-board-area').style.height = w.toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*7/6).toString().concat('px');

  document.getElementById('board-canvas').style.width = w.toString().concat('px');
  document.getElementById('board-canvas').style.height = w.toString().concat('px');

  document.getElementById('div-logo-north').style.width = (w*4/6).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (w/6).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (w/6).toString().concat('px');
}

function arrange_LongPortraitFitToHeight() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerHeight * THRESHOLD_LongPortraitFitToWidth;
  document.getElementById('div-title-north').style.width = w.toString().concat('px');
  document.getElementById('div-title-north').style.height = (w/6).toString().concat('px');
  document.getElementById('div-board-area').style.width = w.toString().concat('px');
  document.getElementById('div-board-area').style.height = w.toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*7/6).toString().concat('px');

  document.getElementById('board-canvas').style.width = w.toString().concat('px');
  document.getElementById('board-canvas').style.height = w.toString().concat('px');

  document.getElementById('div-logo-north').style.width = (w*4/6).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (w/6).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (w/6).toString().concat('px');
}

function arrange_ShortPortraitFitToWidth() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerWidth;
  document.getElementById('div-title-west').style.width = (w/7).toString().concat('px');
  document.getElementById('div-title-west').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (w*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (w*6/7).toString().concat('px');

  document.getElementById('div-logo-west').style.height = (w*4/7).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (w/7).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (w/7).toString().concat('px');
}

function arrange_ShortPortraitFitToHeight() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-east').style.display = 'none';
  document.getElementById('div-info-south').style.display = 'inline';

  var w = window.innerHeight * THRESHOLD_ShortPortraitFitToWidth;
  document.getElementById('div-title-west').style.width = (w/7).toString().concat('px');
  document.getElementById('div-title-west').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (w*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (w*6/7).toString().concat('px');
  document.getElementById('div-info-south').style.width = w.toString().concat('px');
  document.getElementById('div-info-south').style.height = (window.innerHeight-w*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (w*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (w*6/7).toString().concat('px');

  document.getElementById('div-logo-west').style.height = (w*4/7).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (w/7).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (w/7).toString().concat('px');
}

function arrange_ShortLandscapeFitToWidth() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h = window.innerWidth * THRESHOLD_ShortPortraitFitToWidth;
  document.getElementById('div-title-north').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-title-north').style.height = (h/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.height = (h*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (h*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (h*6/7).toString().concat('px');

  document.getElementById('div-logo-north').style.width = (h*4/7).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (h/7).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (h/7).toString().concat('px');
}

function arrange_ShortLandscapeFitToHeight() {
  document.getElementById('div-title-west').style.display = 'none';
  document.getElementById('div-title-north').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h = window.innerHeight;
  document.getElementById('div-title-north').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-title-north').style.height = (h/7).toString().concat('px');
  document.getElementById('div-board-area').style.width = (h*6/7).toString().concat('px');
  document.getElementById('div-board-area').style.height = (h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*6/7).toString().concat('px');
  document.getElementById('div-info-east').style.height = (h*6/7).toString().concat('px');

  document.getElementById('board-canvas').style.width = (h*6/7).toString().concat('px');
  document.getElementById('board-canvas').style.height = (h*6/7).toString().concat('px');

  document.getElementById('div-logo-north').style.width = (h*4/7).toString().concat('px');
  document.getElementById('button-new-game-north').style.width = (h/7).toString().concat('px');
  document.getElementById('button-full-screen-north').style.width = (h/7).toString().concat('px');
}

function arrange_LongLandscapeFitToWidth() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h  = window.innerWidth * THRESHOLD_LongPortraitFitToWidth;
  document.getElementById('div-title-west').style.width = (h/6).toString().concat('px');
  document.getElementById('div-title-west').style.height = h.toString().concat('px');
  document.getElementById('div-board-area').style.width = h.toString().concat('px');
  document.getElementById('div-board-area').style.height = h.toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*7/6).toString().concat('px');
  document.getElementById('div-info-east').style.height = h.toString().concat('px');

  document.getElementById('board-canvas').style.width = h.toString().concat('px');
  document.getElementById('board-canvas').style.height = h.toString().concat('px');

  document.getElementById('div-logo-west').style.height = (h*4/6).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (h/6).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (h/6).toString().concat('px');
}

function arrange_LongLandscapeFitToHeight() {
  document.getElementById('div-title-north').style.display = 'none';
  document.getElementById('div-title-west').style.display = 'flex';
  document.getElementById('div-info-south').style.display = 'none';
  document.getElementById('div-info-east').style.display = 'inline';

  var h  = window.innerHeight;
  document.getElementById('div-title-west').style.width = (h/6).toString().concat('px');
  document.getElementById('div-title-west').style.height = h.toString().concat('px');
  document.getElementById('div-board-area').style.width = h.toString().concat('px');
  document.getElementById('div-board-area').style.height = h.toString().concat('px');
  document.getElementById('div-info-east').style.width = (window.innerWidth-h*7/6).toString().concat('px');
  document.getElementById('div-info-east').style.height = h.toString().concat('px');

  document.getElementById('board-canvas').style.width = h.toString().concat('px');
  document.getElementById('board-canvas').style.height = h.toString().concat('px');

  document.getElementById('div-logo-west').style.height = (h*4/6).toString().concat('px');
  document.getElementById('button-new-game-west').style.height = (h/6).toString().concat('px');
  document.getElementById('button-full-screen-west').style.height = (h/6).toString().concat('px');
}

function toggle-full-screen() {
  if (!document.fullscreenElement) {
    document.body.requestFullscreen;
  } else {
    document.exitFullscreen();
  }
}

window.onresize = reorganise;
window.onload = reorganise;
<|endoftext|>


<commit_before>(function( jQuery ) {

var rbrace = /^(?:\{.*\}|\[.*\])$/,
	rmultiDash = /([A-Z])/g;

jQuery.extend({
	cache: {},

	// Please use with caution
	uuid: 0,

	// Unique for each copy of jQuery on the page
	// Non-digits removed to match rinlinejQuery
	expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		// Ban all objects except for Flash (which handle expandos)
		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
		"applet": true
	},

	hasData: function( elem ) {
		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
		return !!elem && !isEmptyDataObject( elem );
	},

	data: function( elem, name, data, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var privateCache, thisCache, ret,
			internalKey = jQuery.expando,
			getByName = typeof name === "string",

			// We have to handle DOM nodes and JS objects differently because IE6-7
			// can't GC object references properly across the DOM-JS boundary
			isNode = elem.nodeType,

			// Only DOM nodes need the global jQuery cache; JS object data is
			// attached directly to the object so GC can occur automatically
			cache = isNode ? jQuery.cache : elem,

			// Only defining an ID for JS objects if its cache already exists allows
			// the code to shortcut on the same path as a DOM node with no cache
			id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando,
			isEvents = name === "events";

		// Avoid doing any more work than we need to when trying to get data on an
		// object that has no data at all
		if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
			return;
		}

		if ( !id ) {
			// Only DOM nodes need a new unique ID for each element since their data
			// ends up in the global cache
			if ( isNode ) {
				elem[ jQuery.expando ] = id = ++jQuery.uuid;
			} else {
				id = jQuery.expando;
			}
		}

		if ( !cache[ id ] ) {
			cache[ id ] = {};

			// Avoids exposing jQuery metadata on plain JS objects when the object
			// is serialized using JSON.stringify
			if ( !isNode ) {
				cache[ id ].toJSON = jQuery.noop;
			}
		}

		// An object can be passed to jQuery.data instead of a key/value pair; this gets
		// shallow copied over onto the existing cache
		if ( typeof name === "object" || typeof name === "function" ) {
			if ( pvt ) {
				cache[ id ] = jQuery.extend( cache[ id ], name );
			} else {
				cache[ id ].data = jQuery.extend( cache[ id ].data, name );
			}
		}

		privateCache = thisCache = cache[ id ];

		// jQuery data() is stored in a separate object inside the object's internal data
		// cache in order to avoid key collisions between internal data and user-defined
		// data.
		if ( !pvt ) {
			if ( !thisCache.data ) {
				thisCache.data = {};
			}

			thisCache = thisCache.data;
		}

		if ( data !== undefined ) {
			thisCache[ jQuery.camelCase( name ) ] = data;
		}

		// Users should not attempt to inspect the internal events object using jQuery.data,
		// it is undocumented and subject to change. But does anyone listen? No.
		if ( isEvents && !thisCache[ name ] ) {
			return privateCache.events;
		}

		// Check for both converted-to-camel and non-converted data property names
		// If a data property was specified
		if ( getByName ) {

			// First Try to find as-is property data
			ret = thisCache[ name ];

			// Test for null|undefined property data
			if ( ret == null ) {

				// Try to find the camelCased property
				ret = thisCache[ jQuery.camelCase( name ) ];
			}
		} else {
			ret = thisCache;
		}

		return ret;
	},

	removeData: function( elem, name, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var thisCache, i, l,

			// Reference to internal data cache key
			internalKey = jQuery.expando,

			isNode = elem.nodeType,

			// See jQuery.data for more information
			cache = isNode ? jQuery.cache : elem,

			// See jQuery.data for more information
			id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

		// If there is already no cache entry for this object, there is no
		// purpose in continuing
		if ( !cache[ id ] ) {
			return;
		}

		if ( name ) {

			thisCache = pvt ? cache[ id ] : cache[ id ].data;

			if ( thisCache ) {

				// Support space separated names
				if ( jQuery.isArray( name ) ) {
					name = name;
				} else if ( name in thisCache ) {
					name = [ name ];
				} else {

					// split the camel cased version by spaces
					name = jQuery.camelCase( name );
					if ( name in thisCache ) {
						name = [ name ];
					} else {
						name = name.split( " " );
					}
				}

				for ( i = 0, l = name.length; i < l; i++ ) {
					delete thisCache[ name[i] ];
				}

				// If there is no data left in the cache, we want to continue
				// and let the cache object itself get destroyed
				if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
					return;
				}
			}
		}

		// See jQuery.data for more information
		if ( !pvt ) {
			delete cache[ id ].data;

			// Don't destroy the parent cache unless the internal data object
			// had been the only thing left in it
			if ( !isEmptyDataObject(cache[ id ]) ) {
				return;
			}
		}

		// Browsers that fail expando deletion also refuse to delete expandos on
		// the window, but it will allow it on all other JS objects; other browsers
		// don't care
		// Ensure that `cache` is not a window object #10080
		if ( jQuery.support.deleteExpando || !cache.setInterval ) {
			delete cache[ id ];
		} else {
			cache[ id ] = null;
		}

		// We destroyed the cache and need to eliminate the expando on the node to avoid
		// false lookups in the cache for entries that no longer exist
		if ( isNode ) {
			// IE does not allow us to delete expando properties from nodes,
			// nor does it have a removeAttribute function on Document nodes;
			// we must handle all of these cases
			if ( jQuery.support.deleteExpando ) {
				delete elem[ jQuery.expando ];
			} else if ( elem.removeAttribute ) {
				elem.removeAttribute( jQuery.expando );
			} else {
				elem[ jQuery.expando ] = null;
			}
		}
	},

	// For internal use only.
	_data: function( elem, name, data ) {
		return jQuery.data( elem, name, data, true );
	},

	// A method for determining if a DOM node can handle the data expando
	acceptData: function( elem ) {
		if ( elem.nodeName ) {
			var match = jQuery.noData[ elem.nodeName.toLowerCase() ];

			if ( match ) {
				return !(match === true || elem.getAttribute("classid") !== match);
			}
		}

		return true;
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		var parts, attr, name,
			data = null;

		if ( typeof key === "undefined" ) {
			if ( this.length ) {
				data = jQuery.data( this[0] );

				if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
					attr = this[0].attributes;
					for ( var i = 0, l = attr.length; i < l; i++ ) {
						name = attr[i].name;

						if ( name.indexOf( "data-" ) === 0 ) {
							name = jQuery.camelCase( name.substring(5) );

							dataAttr( this[0], name, data[ name ] );
						}
					}
					jQuery._data( this[0], "parsedAttrs", true );
				}
			}

			return data;

		} else if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			// Try to fetch any internally stored data first
			if ( data === undefined && this.length ) {
				data = jQuery.data( this[0], key );
				data = dataAttr( this[0], key, data );
			}

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;

		} else {
			return this.each(function() {
				var $this = jQuery( this ),
					args = [ parts[0], value ];

				$this.triggerHandler( "setData" + parts[1] + "!", args );
				jQuery.data( this, key, value );
				$this.triggerHandler( "changeData" + parts[1] + "!", args );
			});
		}
	},

	removeData: function( key ) {
		return this.each(function() {
			jQuery.removeData( this, key );
		});
	}
});

function dataAttr( elem, key, data ) {
	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {

		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();

		data = elem.getAttribute( name );

		if ( typeof data === "string" ) {
			try {
				data = data === "true" ? true :
				data === "false" ? false :
				data === "null" ? null :
				jQuery.isNumeric( data ) ? parseFloat( data ) :
					rbrace.test( data ) ? jQuery.parseJSON( data ) :
					data;
			} catch( e ) {}

			// Make sure we set the data so it isn't changed later
			jQuery.data( elem, key, data );

		} else {
			data = undefined;
		}
	}

	return data;
}

// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
	for ( var name in obj ) {

		// if the public data object is empty, the private is still empty
		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
			continue;
		}
		if ( name !== "toJSON" ) {
			return false;
		}
	}

	return true;
}

})( jQuery );
<commit_msg>User internalKey instead of jQuery.expando. Fixes #10675<commit_after>(function( jQuery ) {

var rbrace = /^(?:\{.*\}|\[.*\])$/,
	rmultiDash = /([A-Z])/g;

jQuery.extend({
	cache: {},

	// Please use with caution
	uuid: 0,

	// Unique for each copy of jQuery on the page
	// Non-digits removed to match rinlinejQuery
	expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		// Ban all objects except for Flash (which handle expandos)
		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
		"applet": true
	},

	hasData: function( elem ) {
		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
		return !!elem && !isEmptyDataObject( elem );
	},

	data: function( elem, name, data, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var privateCache, thisCache, ret,
			internalKey = jQuery.expando,
			getByName = typeof name === "string",

			// We have to handle DOM nodes and JS objects differently because IE6-7
			// can't GC object references properly across the DOM-JS boundary
			isNode = elem.nodeType,

			// Only DOM nodes need the global jQuery cache; JS object data is
			// attached directly to the object so GC can occur automatically
			cache = isNode ? jQuery.cache : elem,

			// Only defining an ID for JS objects if its cache already exists allows
			// the code to shortcut on the same path as a DOM node with no cache
			id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
			isEvents = name === "events";

		// Avoid doing any more work than we need to when trying to get data on an
		// object that has no data at all
		if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
			return;
		}

		if ( !id ) {
			// Only DOM nodes need a new unique ID for each element since their data
			// ends up in the global cache
			if ( isNode ) {
				elem[ internalKey ] = id = ++jQuery.uuid;
			} else {
				id = internalKey;
			}
		}

		if ( !cache[ id ] ) {
			cache[ id ] = {};

			// Avoids exposing jQuery metadata on plain JS objects when the object
			// is serialized using JSON.stringify
			if ( !isNode ) {
				cache[ id ].toJSON = jQuery.noop;
			}
		}

		// An object can be passed to jQuery.data instead of a key/value pair; this gets
		// shallow copied over onto the existing cache
		if ( typeof name === "object" || typeof name === "function" ) {
			if ( pvt ) {
				cache[ id ] = jQuery.extend( cache[ id ], name );
			} else {
				cache[ id ].data = jQuery.extend( cache[ id ].data, name );
			}
		}

		privateCache = thisCache = cache[ id ];

		// jQuery data() is stored in a separate object inside the object's internal data
		// cache in order to avoid key collisions between internal data and user-defined
		// data.
		if ( !pvt ) {
			if ( !thisCache.data ) {
				thisCache.data = {};
			}

			thisCache = thisCache.data;
		}

		if ( data !== undefined ) {
			thisCache[ jQuery.camelCase( name ) ] = data;
		}

		// Users should not attempt to inspect the internal events object using jQuery.data,
		// it is undocumented and subject to change. But does anyone listen? No.
		if ( isEvents && !thisCache[ name ] ) {
			return privateCache.events;
		}

		// Check for both converted-to-camel and non-converted data property names
		// If a data property was specified
		if ( getByName ) {

			// First Try to find as-is property data
			ret = thisCache[ name ];

			// Test for null|undefined property data
			if ( ret == null ) {

				// Try to find the camelCased property
				ret = thisCache[ jQuery.camelCase( name ) ];
			}
		} else {
			ret = thisCache;
		}

		return ret;
	},

	removeData: function( elem, name, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var thisCache, i, l,

			// Reference to internal data cache key
			internalKey = jQuery.expando,

			isNode = elem.nodeType,

			// See jQuery.data for more information
			cache = isNode ? jQuery.cache : elem,

			// See jQuery.data for more information
			id = isNode ? elem[ internalKey ] : internalKey;

		// If there is already no cache entry for this object, there is no
		// purpose in continuing
		if ( !cache[ id ] ) {
			return;
		}

		if ( name ) {

			thisCache = pvt ? cache[ id ] : cache[ id ].data;

			if ( thisCache ) {

				// Support space separated names
				if ( jQuery.isArray( name ) ) {
					name = name;
				} else if ( name in thisCache ) {
					name = [ name ];
				} else {

					// split the camel cased version by spaces
					name = jQuery.camelCase( name );
					if ( name in thisCache ) {
						name = [ name ];
					} else {
						name = name.split( " " );
					}
				}

				for ( i = 0, l = name.length; i < l; i++ ) {
					delete thisCache[ name[i] ];
				}

				// If there is no data left in the cache, we want to continue
				// and let the cache object itself get destroyed
				if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
					return;
				}
			}
		}

		// See jQuery.data for more information
		if ( !pvt ) {
			delete cache[ id ].data;

			// Don't destroy the parent cache unless the internal data object
			// had been the only thing left in it
			if ( !isEmptyDataObject(cache[ id ]) ) {
				return;
			}
		}

		// Browsers that fail expando deletion also refuse to delete expandos on
		// the window, but it will allow it on all other JS objects; other browsers
		// don't care
		// Ensure that `cache` is not a window object #10080
		if ( jQuery.support.deleteExpando || !cache.setInterval ) {
			delete cache[ id ];
		} else {
			cache[ id ] = null;
		}

		// We destroyed the cache and need to eliminate the expando on the node to avoid
		// false lookups in the cache for entries that no longer exist
		if ( isNode ) {
			// IE does not allow us to delete expando properties from nodes,
			// nor does it have a removeAttribute function on Document nodes;
			// we must handle all of these cases
			if ( jQuery.support.deleteExpando ) {
				delete elem[ internalKey ];
			} else if ( elem.removeAttribute ) {
				elem.removeAttribute( internalKey );
			} else {
				elem[ internalKey ] = null;
			}
		}
	},

	// For internal use only.
	_data: function( elem, name, data ) {
		return jQuery.data( elem, name, data, true );
	},

	// A method for determining if a DOM node can handle the data expando
	acceptData: function( elem ) {
		if ( elem.nodeName ) {
			var match = jQuery.noData[ elem.nodeName.toLowerCase() ];

			if ( match ) {
				return !(match === true || elem.getAttribute("classid") !== match);
			}
		}

		return true;
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		var parts, attr, name,
			data = null;

		if ( typeof key === "undefined" ) {
			if ( this.length ) {
				data = jQuery.data( this[0] );

				if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) {
					attr = this[0].attributes;
					for ( var i = 0, l = attr.length; i < l; i++ ) {
						name = attr[i].name;

						if ( name.indexOf( "data-" ) === 0 ) {
							name = jQuery.camelCase( name.substring(5) );

							dataAttr( this[0], name, data[ name ] );
						}
					}
					jQuery._data( this[0], "parsedAttrs", true );
				}
			}

			return data;

		} else if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			// Try to fetch any internally stored data first
			if ( data === undefined && this.length ) {
				data = jQuery.data( this[0], key );
				data = dataAttr( this[0], key, data );
			}

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;

		} else {
			return this.each(function() {
				var $this = jQuery( this ),
					args = [ parts[0], value ];

				$this.triggerHandler( "setData" + parts[1] + "!", args );
				jQuery.data( this, key, value );
				$this.triggerHandler( "changeData" + parts[1] + "!", args );
			});
		}
	},

	removeData: function( key ) {
		return this.each(function() {
			jQuery.removeData( this, key );
		});
	}
});

function dataAttr( elem, key, data ) {
	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {

		var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();

		data = elem.getAttribute( name );

		if ( typeof data === "string" ) {
			try {
				data = data === "true" ? true :
				data === "false" ? false :
				data === "null" ? null :
				jQuery.isNumeric( data ) ? parseFloat( data ) :
					rbrace.test( data ) ? jQuery.parseJSON( data ) :
					data;
			} catch( e ) {}

			// Make sure we set the data so it isn't changed later
			jQuery.data( elem, key, data );

		} else {
			data = undefined;
		}
	}

	return data;
}

// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
	for ( var name in obj ) {

		// if the public data object is empty, the private is still empty
		if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
			continue;
		}
		if ( name !== "toJSON" ) {
			return false;
		}
	}

	return true;
}

})( jQuery );
<|endoftext|>


<commit_before>(function( jQuery ) {

var rbrace = /^(?:\{.*\}|\[.*\])$/;

jQuery.extend({
	cache: {},

	// Please use with caution
	uuid: 0,

	// Unique for each copy of jQuery on the page
	// Non-digits removed to match rinlinejQuery
	expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		// Ban all objects except for Flash (which handle expandos)
		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
		"applet": true
	},

	hasData: function( elem ) {
		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];

		return !!elem && !jQuery.isEmptyObject(elem);
	},

	data: function( elem, name, data, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,

			// We have to handle DOM nodes and JS objects differently because IE6-7
			// can't GC object references properly across the DOM-JS boundary
			isNode = elem.nodeType,

			// Only DOM nodes need the global jQuery cache; JS object data is
			// attached directly to the object so GC can occur automatically
			cache = isNode ? jQuery.cache : elem,

			// Only defining an ID for JS objects if its cache already exists allows
			// the code to shortcut on the same path as a DOM node with no cache
			id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;

		// Avoid doing any more work than we need to when trying to get data on an
		// object that has no data at all
		if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
			return;
		}

		if ( !id ) {
			// Only DOM nodes need a new unique ID for each element since their data
			// ends up in the global cache
			if ( isNode ) {
				elem[ jQuery.expando ] = id = ++jQuery.uuid;
			} else {
				id = jQuery.expando;
			}
		}

		if ( !cache[ id ] ) {
			cache[ id ] = {};
		}

		// An object can be passed to jQuery.data instead of a key/value pair; this gets
		// shallow copied over onto the existing cache
		if ( typeof name === "object" ) {
			if ( pvt ) {
				cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
			} else {
				cache[ id ] = jQuery.extend(cache[ id ], name);
			}
		}

		thisCache = cache[ id ];

		// Internal jQuery data is stored in a separate object inside the object's data
		// cache in order to avoid key collisions between internal data and user-defined
		// data
		if ( pvt ) {
			if ( !thisCache[ internalKey ] ) {
				thisCache[ internalKey ] = {};
			}

			thisCache = thisCache[ internalKey ];
		}

		if ( data !== undefined ) {
			thisCache[ name ] = data;
		}

		return getByName ? thisCache[ name ] : thisCache;
	},

	removeData: function( elem, name, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var internalKey = jQuery.expando, isNode = elem.nodeType,

			// See jQuery.data for more information
			cache = isNode ? jQuery.cache : elem,

			// See jQuery.data for more information
			id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

		// If there is already no cache entry for this object, there is no
		// purpose in continuing
		if ( !cache[ id ] ) {
			return;
		}

		if ( name ) {
			var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];

			if ( thisCache ) {
				delete thisCache[ name ];

				// If there is no data left in the cache, we want to continue
				// and let the cache object itself get destroyed
				if ( !jQuery.isEmptyObject(thisCache) ) {
					return;
				}
			}
		}

		// See jQuery.data for more information
		if ( pvt ) {
			delete cache[ id ][ internalKey ];

			// Don't destroy the parent cache unless the internal data object
			// had been the only thing left in it
			if ( !jQuery.isEmptyObject(cache[ id ]) ) {
				return;
			}
		}

		var internalCache = cache[ id ][ internalKey ];

		// Browsers that fail expando deletion also refuse to delete expandos on
		// the window, but it will allow it on all other JS objects; other browsers
		// don't care
		if ( jQuery.support.deleteExpando || cache != window ) {
			delete cache[ id ];
		} else {
			cache[ id ] = null;
		}

		// We destroyed the entire user cache at once because it's faster than
		// iterating through each key, but we need to continue to persist internal
		// data if it existed
		if ( internalCache ) {
			cache[ id ] = {};
			cache[ id ][ internalKey ] = internalCache;

		// Otherwise, we need to eliminate the expando on the node to avoid
		// false lookups in the cache for entries that no longer exist
		} else if ( isNode ) {
			// IE does not allow us to delete expando properties from nodes,
			// nor does it have a removeAttribute function on Document nodes;
			// we must handle all of these cases
			if ( jQuery.support.deleteExpando ) {
				delete elem[ jQuery.expando ];
			} else if ( elem.removeAttribute ) {
				elem.removeAttribute( jQuery.expando );
			} else {
				elem[ jQuery.expando ] = null;
			}
		}
	},

	// For internal use only.
	_data: function( elem, name, data ) {
		return jQuery.data( elem, name, data, true );
	},

	// A method for determining if a DOM node can handle the data expando
	acceptData: function( elem ) {
		if ( elem.nodeName ) {
			var match = jQuery.noData[ elem.nodeName.toLowerCase() ];

			if ( match ) {
				return !(match === true || elem.getAttribute("classid") !== match);
			}
		}

		return true;
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		var data = null;

		if ( typeof key === "undefined" ) {
			if ( this.length ) {
				data = jQuery.data( this[0] );

				if ( this[0].nodeType === 1 ) {
					var attr = this[0].attributes, name;
					for ( var i = 0, l = attr.length; i < l; i++ ) {
						name = attr[i].name;

						if ( name.indexOf( "data-" ) === 0 ) {
							name = name.substr( 5 );
							dataAttr( this[0], name, data[ name ] );
						}
					}
				}
			}

			return data;

		} else if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			// Try to fetch any internally stored data first
			if ( data === undefined && this.length ) {
				data = jQuery.data( this[0], key );
				data = dataAttr( this[0], key, data );
			}

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;

		} else {
			return this.each(function() {
				var $this = jQuery( this ),
					args = [ parts[0], value ];

				$this.triggerHandler( "setData" + parts[1] + "!", args );
				jQuery.data( this, key, value );
				$this.triggerHandler( "changeData" + parts[1] + "!", args );
			});
		}
	},

	removeData: function( key ) {
		return this.each(function() {
			jQuery.removeData( this, key );
		});
	}
});

function dataAttr( elem, key, data ) {
	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {
		data = elem.getAttribute( "data-" + key );

		if ( typeof data === "string" ) {
			try {
				data = data === "true" ? true :
				data === "false" ? false :
				data === "null" ? null :
				!jQuery.isNaN( data ) ? parseFloat( data ) :
					rbrace.test( data ) ? jQuery.parseJSON( data ) :
					data;
			} catch( e ) {}

			// Make sure we set the data so it isn't changed later
			jQuery.data( elem, key, data );

		} else {
			data = undefined;
		}
	}

	return data;
}

})( jQuery );
<commit_msg>Introduce a temporary hack to allow jQuery.fn.data("events") to continue to work. This will be going away in 1.6. More information will be available in the 1.5 release notes.<commit_after>(function( jQuery ) {

var rbrace = /^(?:\{.*\}|\[.*\])$/;

jQuery.extend({
	cache: {},

	// Please use with caution
	uuid: 0,

	// Unique for each copy of jQuery on the page
	// Non-digits removed to match rinlinejQuery
	expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		// Ban all objects except for Flash (which handle expandos)
		"object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
		"applet": true
	},

	hasData: function( elem ) {
		elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];

		return !!elem && !jQuery.isEmptyObject(elem);
	},

	data: function( elem, name, data, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,

			// We have to handle DOM nodes and JS objects differently because IE6-7
			// can't GC object references properly across the DOM-JS boundary
			isNode = elem.nodeType,

			// Only DOM nodes need the global jQuery cache; JS object data is
			// attached directly to the object so GC can occur automatically
			cache = isNode ? jQuery.cache : elem,

			// Only defining an ID for JS objects if its cache already exists allows
			// the code to shortcut on the same path as a DOM node with no cache
			id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;

		// Avoid doing any more work than we need to when trying to get data on an
		// object that has no data at all
		if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
			return;
		}

		if ( !id ) {
			// Only DOM nodes need a new unique ID for each element since their data
			// ends up in the global cache
			if ( isNode ) {
				elem[ jQuery.expando ] = id = ++jQuery.uuid;
			} else {
				id = jQuery.expando;
			}
		}

		if ( !cache[ id ] ) {
			cache[ id ] = {};
		}

		// An object can be passed to jQuery.data instead of a key/value pair; this gets
		// shallow copied over onto the existing cache
		if ( typeof name === "object" ) {
			if ( pvt ) {
				cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
			} else {
				cache[ id ] = jQuery.extend(cache[ id ], name);
			}
		}

		thisCache = cache[ id ];

		// Internal jQuery data is stored in a separate object inside the object's data
		// cache in order to avoid key collisions between internal data and user-defined
		// data
		if ( pvt ) {
			if ( !thisCache[ internalKey ] ) {
				thisCache[ internalKey ] = {};
			}

			thisCache = thisCache[ internalKey ];
		}

		if ( data !== undefined ) {
			thisCache[ name ] = data;
		}

		// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
		// not attempt to inspect the internal events object using jQuery.data, as this
		// internal data object is undocumented and subject to change.
		if ( name === "events" && !thisCache[name] ) {
			return thisCache[ internalKey ] && thisCache[ internalKey ].events;
		}

		return getByName ? thisCache[ name ] : thisCache;
	},

	removeData: function( elem, name, pvt /* Internal Use Only */ ) {
		if ( !jQuery.acceptData( elem ) ) {
			return;
		}

		var internalKey = jQuery.expando, isNode = elem.nodeType,

			// See jQuery.data for more information
			cache = isNode ? jQuery.cache : elem,

			// See jQuery.data for more information
			id = isNode ? elem[ jQuery.expando ] : jQuery.expando;

		// If there is already no cache entry for this object, there is no
		// purpose in continuing
		if ( !cache[ id ] ) {
			return;
		}

		if ( name ) {
			var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];

			if ( thisCache ) {
				delete thisCache[ name ];

				// If there is no data left in the cache, we want to continue
				// and let the cache object itself get destroyed
				if ( !jQuery.isEmptyObject(thisCache) ) {
					return;
				}
			}
		}

		// See jQuery.data for more information
		if ( pvt ) {
			delete cache[ id ][ internalKey ];

			// Don't destroy the parent cache unless the internal data object
			// had been the only thing left in it
			if ( !jQuery.isEmptyObject(cache[ id ]) ) {
				return;
			}
		}

		var internalCache = cache[ id ][ internalKey ];

		// Browsers that fail expando deletion also refuse to delete expandos on
		// the window, but it will allow it on all other JS objects; other browsers
		// don't care
		if ( jQuery.support.deleteExpando || cache != window ) {
			delete cache[ id ];
		} else {
			cache[ id ] = null;
		}

		// We destroyed the entire user cache at once because it's faster than
		// iterating through each key, but we need to continue to persist internal
		// data if it existed
		if ( internalCache ) {
			cache[ id ] = {};
			cache[ id ][ internalKey ] = internalCache;

		// Otherwise, we need to eliminate the expando on the node to avoid
		// false lookups in the cache for entries that no longer exist
		} else if ( isNode ) {
			// IE does not allow us to delete expando properties from nodes,
			// nor does it have a removeAttribute function on Document nodes;
			// we must handle all of these cases
			if ( jQuery.support.deleteExpando ) {
				delete elem[ jQuery.expando ];
			} else if ( elem.removeAttribute ) {
				elem.removeAttribute( jQuery.expando );
			} else {
				elem[ jQuery.expando ] = null;
			}
		}
	},

	// For internal use only.
	_data: function( elem, name, data ) {
		return jQuery.data( elem, name, data, true );
	},

	// A method for determining if a DOM node can handle the data expando
	acceptData: function( elem ) {
		if ( elem.nodeName ) {
			var match = jQuery.noData[ elem.nodeName.toLowerCase() ];

			if ( match ) {
				return !(match === true || elem.getAttribute("classid") !== match);
			}
		}

		return true;
	}
});

jQuery.fn.extend({
	data: function( key, value ) {
		var data = null;

		if ( typeof key === "undefined" ) {
			if ( this.length ) {
				data = jQuery.data( this[0] );

				if ( this[0].nodeType === 1 ) {
					var attr = this[0].attributes, name;
					for ( var i = 0, l = attr.length; i < l; i++ ) {
						name = attr[i].name;

						if ( name.indexOf( "data-" ) === 0 ) {
							name = name.substr( 5 );
							dataAttr( this[0], name, data[ name ] );
						}
					}
				}
			}

			return data;

		} else if ( typeof key === "object" ) {
			return this.each(function() {
				jQuery.data( this, key );
			});
		}

		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";

		if ( value === undefined ) {
			data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

			// Try to fetch any internally stored data first
			if ( data === undefined && this.length ) {
				data = jQuery.data( this[0], key );
				data = dataAttr( this[0], key, data );
			}

			return data === undefined && parts[1] ?
				this.data( parts[0] ) :
				data;

		} else {
			return this.each(function() {
				var $this = jQuery( this ),
					args = [ parts[0], value ];

				$this.triggerHandler( "setData" + parts[1] + "!", args );
				jQuery.data( this, key, value );
				$this.triggerHandler( "changeData" + parts[1] + "!", args );
			});
		}
	},

	removeData: function( key ) {
		return this.each(function() {
			jQuery.removeData( this, key );
		});
	}
});

function dataAttr( elem, key, data ) {
	// If nothing was found internally, try to fetch any
	// data from the HTML5 data-* attribute
	if ( data === undefined && elem.nodeType === 1 ) {
		data = elem.getAttribute( "data-" + key );

		if ( typeof data === "string" ) {
			try {
				data = data === "true" ? true :
				data === "false" ? false :
				data === "null" ? null :
				!jQuery.isNaN( data ) ? parseFloat( data ) :
					rbrace.test( data ) ? jQuery.parseJSON( data ) :
					data;
			} catch( e ) {}

			// Make sure we set the data so it isn't changed later
			jQuery.data( elem, key, data );

		} else {
			data = undefined;
		}
	}

	return data;
}

})( jQuery );
<|endoftext|>


<commit_before>import fetch from 'isomorphic-fetch'
import queue from 'async/queue'
import values from 'lodash/values'

import rlrankapi, { TRACKER } from '../lib/rocket-league-rank-api'

/* eslint-disable no-console */

if (!process.env.ROCKETLEAGUE_TRACKER_NETWORK_API_KEY || !process.env.RLTRACKER_PRO_API_KEY) {
  console.log('Please set the ROCKETLEAGUE_TRACKER_NETWORK_API_KEY and RLTRACKER_PRO_API_KEY env')
  process.exit(-1)
}

const PRIORITY_UNIT = 15 // minutes difference between priorities

const PULL_DELAY = 60 // seconds to wait before pulling players after a full update is done

const LOCAL_API = 'http://127.0.0.1:8080/api/v1'

const TRACKER_NAME = []
TRACKER_NAME[TRACKER.RLTRACKER_PRO] = 'rltracker.pro'
TRACKER_NAME[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 'Rocket League Tracker Network'

const RATE = []
RATE[TRACKER.RLTRACKER_PRO] = 9.8
RATE[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 1.8

const lastRequest = []
lastRequest[TRACKER.RLTRACKER_PRO] = 0
lastRequest[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 0

let isUp = []
isUp[TRACKER.RLTRACKER_PRO] = true
isUp[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = true

const API_KEY = []
API_KEY[TRACKER.RLTRACKER_PRO] = process.env.RLTRACKER_PRO_API_KEY
API_KEY[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = process.env.ROCKETLEAGUE_TRACKER_NETWORK_API_KEY

const getAdjustedTime = (time, priority) =>
  time + (priority * PRIORITY_UNIT * 60 * 1000)

// should update every PRIORITY * 60 min
const shouldUpdate = (player) => {
  const timeDelta = Date.now() - (new Date(player.last_update)).getTime()
  const minTimeToUpdate = getAdjustedTime(0, player.priority)
  const justCreated = player.last_update === player.created_at
  return (timeDelta > minTimeToUpdate && player.priority > 0) || justCreated
}

const timeToWaitBeforeNextRequest = (rate, lastRequestTime) => {
  const time = ((60 * 1000) / rate) - (Date.now() - lastRequestTime)
  return time < 0 ? 0 : time
}

const pullPlayers = () => {
  console.log(`Starting full update at ${new Date().toISOString()}...`)

  console.log('Pulling players...')

  // set trackers as up
  isUp = isUp.map(() => true)

  fetch(`${LOCAL_API}/player`)
    .then((response) => {
      response.json()
        .then((jsonData) => {
          console.log('Pulled players')

          const playersToUpdate = jsonData.data.players.filter((p) => shouldUpdate(p))

          // sort by update time
          playersToUpdate.sort((a, b) =>
            getAdjustedTime((new Date(a.last_update).getTime()), a.priority) -
            getAdjustedTime((new Date(b.last_update)).getTime(), b.priority))

          console.log('Players to update:')
          console.log(playersToUpdate.map((p) => p.name))

          q.push(playersToUpdate, (err) => { if (err) console.log(err) })
        })
    })
    .catch((err) => console.log(`Error fetching players ${err}`))
}

const updatePlayer = (player, tracker) => new Promise((resolve, reject) => {
  rlrankapi.getPlayerInformation(player.platform, player.id, API_KEY[tracker], tracker)
      .then((stats) => {
        fetch(`${LOCAL_API}/player/${player.platform}/${player.id}/update`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(stats),
        })
          .then((res) => {
            if (res.status === 200) {
              res.json()
                .then((jsonData) => {
                  if (jsonData.data.updated) {
                    resolve(`Updated player: ${player.name}`)
                  } else {
                    resolve(`No updated needed for player: ${player.name}`)
                  }
                })
            } else {
              reject(`Error status from local api: ${res.status}`)
            }
          })
          .catch((err) =>
            reject(`Error updating ${player.name} with local api: ${err}`)
          )
      })
      .catch((err) =>
        reject(`Error updating ${player.name} with external api: ${err}`)
      )
})

let q = queue((player, callback) => {
  const filteredTrackers = values(TRACKER).filter((t) =>
    isUp[t] && API_KEY[t] && // remove if tracker is marked as down or if we don't have the key
    (player.platform !== 2 || (player.platform === 2 && t !== TRACKER.RLTRACKER_PRO)) // RL Tracker can't handle xbox
  )

  if (filteredTrackers.length === 0) {
    callback(`No trackers to use for updating ${player.name}`)
    return
  }

  const timesToWait = filteredTrackers.map((trackerId) => ({
    trackerId,
    timeToWait: timeToWaitBeforeNextRequest(RATE[trackerId], lastRequest[trackerId]),
  }))

  // sort by timeToWaitBeforeNextRequest
  timesToWait.sort((a, b) => a.timeToWait - b.timeToWait)

  const trackerId = timesToWait[0].trackerId
  const trackerTimeToWait = timesToWait[0].timeToWait
  const trackerName = TRACKER_NAME[trackerId]

  console.log(`Waiting ${trackerTimeToWait} ms for next request using ${trackerName}...`)
  setTimeout(() => {
    lastRequest[trackerId] = Date.now()
    console.log(`Starting update for player: ${player.name} using ${trackerName}`)
    updatePlayer(player, trackerId, trackerTimeToWait)
      .then((result) => {
        console.log(result)
        callback()
      })
      .catch((err) => {
        console.log(`Marking ${trackerName} as down`)
        isUp[trackerId] = false
        callback(err)
      })
  }, trackerTimeToWait)
}, 1)

q.drain = () => {
  console.log(`Finished full update. Pulling players again in ${PULL_DELAY} seconds`)
  setTimeout(pullPlayers, PULL_DELAY * 1000)
}

pullPlayers()
<commit_msg>Refactor update.js to remove flying V and add more comments<commit_after>import fetch from 'isomorphic-fetch'
import queue from 'async/queue'
import values from 'lodash/values'

import rlrankapi, { TRACKER } from '../lib/rocket-league-rank-api'

/* eslint-disable no-console */

if (!process.env.ROCKETLEAGUE_TRACKER_NETWORK_API_KEY || !process.env.RLTRACKER_PRO_API_KEY) {
  console.log('Please set the ROCKETLEAGUE_TRACKER_NETWORK_API_KEY and RLTRACKER_PRO_API_KEY env')
  process.exit(-1)
}

// Settings

const PRIORITY_UNIT = 15 // minutes difference between priorities

const PULL_DELAY = 60 // seconds to wait before pulling players after a full update is done

const LOCAL_API = 'http://127.0.0.1:8080/api/v1'

const TRACKER_NAME = []
TRACKER_NAME[TRACKER.RLTRACKER_PRO] = 'rltracker.pro'
TRACKER_NAME[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 'Rocket League Tracker Network'

const RATE = []
RATE[TRACKER.RLTRACKER_PRO] = 9.8
RATE[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 1.8

const lastRequest = []
lastRequest[TRACKER.RLTRACKER_PRO] = 0
lastRequest[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = 0

let isUp = []
isUp[TRACKER.RLTRACKER_PRO] = true
isUp[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = true

const API_KEY = []
API_KEY[TRACKER.RLTRACKER_PRO] = process.env.RLTRACKER_PRO_API_KEY
API_KEY[TRACKER.ROCKETLEAGUE_TRACKER_NETWORK] = process.env.ROCKETLEAGUE_TRACKER_NETWORK_API_KEY

// Helper functions

// Get time increased based on player priority
const getAdjustedTime = (time, priority) =>
  time + (priority * PRIORITY_UNIT * 60 * 1000)

const shouldUpdate = (player) => {
  // Time between now and last update in ms
  const timeDelta = Date.now() - (new Date(player.last_update)).getTime()
  // Time between updates based on player priority
  const timeBetweenUpdates = getAdjustedTime(0, player.priority)
  const justCreated = player.last_update === player.created_at
  // Return true if it's time to update or if it's a new player
  return (timeDelta > timeBetweenUpdates && player.priority > 0) || justCreated
}

// Time to wait before next request to tracker
const timeToWaitBeforeNextRequest = (rate, lastRequestTime) => {
  //           rate in ms              ms passed since last request
  const time = ((60 * 1000) / rate) - (Date.now() - lastRequestTime)
  return time < 0 ? 0 : time
}

// function to be called at the start and when the queue depletes
const pullPlayers = () => {
  console.log(`Starting full update at ${new Date().toISOString()}...`)

  console.log('Pulling players...')

  // set trackers as up
  isUp = isUp.map(() => true)

  fetch(`${LOCAL_API}/player`)
    .then((response) => response.json())
    .catch((err) => console.log(`Error fetching players ${err}`))
    .then((jsonData) => {
      console.log('Pulled players')
      const playersToUpdate = jsonData.data.players.filter(shouldUpdate)

      // sort by update time
      playersToUpdate.sort((a, b) =>
        getAdjustedTime((new Date(a.last_update).getTime()), a.priority) -
        getAdjustedTime((new Date(b.last_update)).getTime(), b.priority))

      console.log('Players to update:')
      console.log(playersToUpdate.map((p) => p.name))

      q.push(playersToUpdate, (err) => { if (err) console.log(err) })
    })
    .catch((err) => console.log(`Error fetching players ${err}`))
}

// pulls player info from tracker and updates through the api
const updatePlayer = (player, tracker) =>
  rlrankapi.getPlayerInformation(player.platform, player.id, API_KEY[tracker], tracker)
    .then((stats) =>
      fetch(`${LOCAL_API}/player/${player.platform}/${player.id}/update`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(stats),
      })
    )
    .catch((err) => {
      throw new Error(`Error updating ${player.name} with external api: ${err}`)
    })
    .then((res) => {
      if (res.status === 200) {
        return res.json()
      }
      throw new Error(`Error status from local api: ${res.status}`)
    })
    .catch((err) => {
      throw new Error(`Error updating ${player.name} with local api: ${err}`)
    })
    .then((jsonData) => {
      if (jsonData.data.updated) {
        return `Updated player: ${player.name}`
      }
      return `No updated needed for player: ${player.name}`
    })

// queue definition
let q = queue((player, callback) => {
  const filteredTrackers = values(TRACKER).filter((t) =>
    isUp[t] && API_KEY[t] && // remove if tracker is marked as down or if we don't have the key
    (player.platform !== 2 || (player.platform === 2 && t !== TRACKER.RLTRACKER_PRO)) // RL Tracker can't handle xbox
  )

  if (filteredTrackers.length === 0) {
    callback(`No trackers to use for updating ${player.name}`)
    return
  }

  const timesToWait = filteredTrackers.map((trackerId) => ({
    trackerId,
    timeToWait: timeToWaitBeforeNextRequest(RATE[trackerId], lastRequest[trackerId]),
  }))

  // sort by timeToWaitBeforeNextRequest
  timesToWait.sort((a, b) => a.timeToWait - b.timeToWait)

  const trackerId = timesToWait[0].trackerId
  const trackerTimeToWait = timesToWait[0].timeToWait
  const trackerName = TRACKER_NAME[trackerId]

  console.log(`Waiting ${trackerTimeToWait} ms for next request using ${trackerName}...`)
  setTimeout(() => {
    lastRequest[trackerId] = Date.now()
    console.log(`Starting update for player: ${player.name} using ${trackerName}`)
    updatePlayer(player, trackerId, trackerTimeToWait)
      .then((result) => {
        console.log(result)
        callback()
      })
      .catch((err) => {
        console.log(`Marking ${trackerName} as down`)
        isUp[trackerId] = false
        callback(err)
      })
  }, trackerTimeToWait)
}, 1)

// pullPlayers again when queue finishes
q.drain = () => {
  console.log(`Finished full update. Pulling players again in ${PULL_DELAY} seconds`)
  setTimeout(pullPlayers, PULL_DELAY * 1000)
}

// pullPlayers on script start
pullPlayers()
<|endoftext|>