<commit_before>"use strict";

var request = require('request');
var cheerio = require('cheerio');
var PDFParser = require('pdf2json');

module.exports = {

    intent: /.*gkk.*/i,
    location: "linz",
    getMenu: function (callback) {
        var result = {
            title: "GKK",
            url: 'http://www.caseli.at/content/download/1363/6617/file/Speiseplan_O%C3%96_GKK_Hauptstelle.pdf',
            menu: "Couldn't find the menu today :(",
            location: "https://www.google.de/maps/place/O%C3%96GKK+-+Ober%C3%B6sterreichische+Gebietskrankenkasse+Forum+Gesundheit/"
        };

        var day = new Date().getDay();
        if (day < 1 || day > 5) {
            callback(result);
            return;
        }

        var pdfParser = new PDFParser();
        pdfParser.on("pdfParser_dataError", errData => console.error(errData.parserError));
        pdfParser.on("pdfParser_dataReady", pdfData => {
            //console.log(JSON.stringify(pdfData));
            //console.log(pdfData.formImage.Pages[0].Texts);

            var text = pdfData.formImage.Pages[0].Texts.reduce(function (a, b) {
                return a + b.R.reduce(function (a, b) {
                    return a + b.T;
                }, "");
            }, "");
            text = decodeURI(text);
            //console.log(text);

            var results = text.split(/(MONTAG|DIENSTAG|MITTWOCH|DONNERSTAG|FREITAG)/)

            //console.log(results);
            /*
                 0...empty
                 1...MONTAG
                 2...<monday menu>
                 3...DIENSTAG
                 4...<tuesday menu>
                 5...WEDNESDAY
                 6...<wednesday menu>
                 7...THURSDAY
                 8...<thursday menu>
                 9...FRIDAY
                 10..<friday menu>

                 Monday  -> day==1 --> day+day==2 --> monday menu
                 Tuesday -> day==2 --> day+day==4 --> tuesday menu
                 */
            var index = day * 2;

            var menu = results[index];
            if (!menu) {
                callback(result);
                return;
            }
            menu = menu.trim();
            menu = decodeURIComponent(menu);
            menu = menu.replace(/Classic (I+)/g, "\n\n*Classic $1*\n\n");
            menu = menu.replace("GUSTO", "\n\n*Gusto*\n\n");
            menu = menu.replace("Mehlspeise", "\n\n*Mehlspeise*\n\n");
            menu = menu.replace(/^, /g, "");
            menu = menu.replace("%2C", "");
            menu = menu.substring(menu.indexOf("*") + 1);
            menu = menu.replace(/[0-9]+/g, "\n");

            if (menu.indexOf("Caseli") !== -1) {
                menu = menu.substring(0, menu.indexOf("Caseli") - 1);
            }
            menu = menu.replace("SPEISEPLAN OÖ GK", "");

            result.menu = menu;
            callback(result);
        });

        var pdfPipe = request(result.url).pipe(pdfParser);
    }
};<commit_msg>remove double newline after sub-title<commit_after>"use strict";

var request = require('request');
var cheerio = require('cheerio');
var PDFParser = require('pdf2json');

module.exports = {

    intent: /.*gkk.*/i,
    location: "linz",
    getMenu: function (callback) {
        var result = {
            title: "GKK",
            url: 'http://www.caseli.at/content/download/1363/6617/file/Speiseplan_O%C3%96_GKK_Hauptstelle.pdf',
            menu: "Couldn't find the menu today :(",
            location: "https://www.google.de/maps/place/O%C3%96GKK+-+Ober%C3%B6sterreichische+Gebietskrankenkasse+Forum+Gesundheit/"
        };

        var day = new Date().getDay();
        if (day < 1 || day > 5) {
            callback(result);
            return;
        }

        var pdfParser = new PDFParser();
        pdfParser.on("pdfParser_dataError", errData => console.error(errData.parserError));
        pdfParser.on("pdfParser_dataReady", pdfData => {
            //console.log(JSON.stringify(pdfData));
            //console.log(pdfData.formImage.Pages[0].Texts);

            var text = pdfData.formImage.Pages[0].Texts.reduce(function (a, b) {
                return a + b.R.reduce(function (a, b) {
                    return a + b.T;
                }, "");
            }, "");
            text = decodeURI(text);
            //console.log(text);

            var results = text.split(/(MONTAG|DIENSTAG|MITTWOCH|DONNERSTAG|FREITAG)/)

            //console.log(results);
            /*
                 0...empty
                 1...MONTAG
                 2...<monday menu>
                 3...DIENSTAG
                 4...<tuesday menu>
                 5...WEDNESDAY
                 6...<wednesday menu>
                 7...THURSDAY
                 8...<thursday menu>
                 9...FRIDAY
                 10..<friday menu>

                 Monday  -> day==1 --> day+day==2 --> monday menu
                 Tuesday -> day==2 --> day+day==4 --> tuesday menu
                 */
            var index = day * 2;

            var menu = results[index];
            if (!menu) {
                callback(result);
                return;
            }
            menu = menu.trim();
            menu = decodeURIComponent(menu);
            menu = menu.replace(/Classic (I+)/g, "\n\n*Classic $1*\n");
            menu = menu.replace("GUSTO", "\n\n*Gusto*\n");
            menu = menu.replace("Mehlspeise", "\n\n*Mehlspeise*\n");
            menu = menu.replace(/^, /g, "");
            menu = menu.replace("%2C", "");
            menu = menu.substring(menu.indexOf("*") + 1);
            menu = menu.replace(/[0-9]+/g, "\n");

            if (menu.indexOf("Caseli") !== -1) {
                menu = menu.substring(0, menu.indexOf("Caseli") - 1);
            }
            menu = menu.replace("SPEISEPLAN OÖ GK", "");

            result.menu = menu;
            callback(result);
        });

        var pdfPipe = request(result.url).pipe(pdfParser);
    }
};<|endoftext|>


<commit_before>const TimerMixin = require('react-timer-mixin');

let nextID = 1;

module.exports = (React, ReactNative, { constants, model, styles }) => {
  const {
    NativeModules: { UIManager },
    TouchableWithoutFeedback,
    ScrollView,
    View
  } = ReactNative;
  const AnimatedOptionsContainer = require('./makeAnimatedOptionsContainer')(React, ReactNative);

  // Calls a function once, then never again.
  const once = (fn) => {
    let called = false;
    return (...args) => {
      if (!called) {
        called = true;
        fn(...args);
      }
    };
  };

  const defaultOptionsContainerRenderer = (options) => (
    <ScrollView>
      {options}
    </ScrollView>
  );

  const makeOptions = (options, { top, right }) => {
    const { optionsContainerStyle, renderOptionsContainer = defaultOptionsContainerRenderer} = options.props;
    return (
      <AnimatedOptionsContainer style={[ styles.optionsContainer, optionsContainerStyle, { top, right }]}>
        { renderOptionsContainer(options) }
      </AnimatedOptionsContainer>
    );
  };

  const NULL_HOOKS = {
    didOpen: () => {},
    didClose: () => {}
  }

  /*
   * The MenuContext provides a tunnel for descendant menu components to access
   * top-level methods. It also allows the <MenuOptions/> element to be placed
   * properly.
   */
  const MenuContext = React.createClass({
    displayName: 'MenuContext',
    mixins: [TimerMixin],

    // Public methods
    isMenuOpen() {
      return this.state.openedMenu
    },
    openMenu(name) {
      const handle = React.findNodeHandle(this._menus[name].ref);
      UIManager.measure(handle, (x, y, w, h, px, py) => {
        this._menus[name].measurements = { x, y, w, h, px, py };

        this.setState({
          openedMenu: name,
          menuOptions: this._makeAndPositionOptions(name, this._menus[name].measurements),
          backdropWidth: this._ownMeasurements.w
        });

        this._activeMenuHooks = this._menus[name];
        this._activeMenuHooks && this._activeMenuHooks.didOpen();
      });
    },
    closeMenu() {
      this.setState({
        openedMenu: '',
        menuOptions: null
      });

      this._activeMenuHooks && this._activeMenuHooks.didClose();
      this._activeMenuHooks = NULL_HOOKS;
    },
    toggleMenu(name) {
      if (this.state.openedMenu === name) {
        this.closeMenu(name);
      } else {
        this.openMenu(name);
      }
    },

    // Private and lifecycle methods
    getInitialState() {
      return {
        openedMenu: '',
        menuOptions: null,
        optionsTop: 0,
        optionsRight: 0,
        backdropWidth: 0
      };
    },
    childContextTypes: {
      menuController: model.IMenuController
    },
    getChildContext() {
      const menuController = {
        open: this.openMenu,
        close: this.closeMenu,
        toggle: this.toggleMenu,
        registerMenu: this._registerMenu,
        unregisterMenu: this._unregisterMenu,
        registerOptionsElement: this._registerOptionsElement,
        makeName: this._makeName
      };
      return { menuController };
    },
    componentWillMount() {
      this._menus = {};
      this._options = {};
      // Only do this once on initial layout.
      this.onLayout = once(this.onLayout);
    },
    onLayout() {
      const handle = React.findNodeHandle(this.refs.Container);
      UIManager.measure(handle, (x, y, w, h, px, py) => {
        this._ownMeasurements = {x, y, w, h, px, py};
      });
    },
    _registerMenu(name, hooks) {
      if (this._menus[name]) {
        console.warn(`Menu ${name} has already been registered in this context. Please provide a different name.`);
      }
      this._menus[name] = hooks;
    },
    _unregisterMenu(name) {
      delete this._menus[name];
      delete this._options[name];
    },
    _registerOptionsElement(name, options) {
      // If options are already set and visible, skip the update.
      if (this.state.menuOptions === options) {
        return;
      }
      this._options[name] = options;
      // If the menu is already open, re-render the options.
      this.setTimeout(() => {
        if (this.state.openedMenu === name) {
          this.setState({ menuOptions: this._makeAndPositionOptions(name, this._menus[name].measurements) });
        }
      }, 16);
    },
    _makeName() {
      return `menu-${nextID++}`;
    },
    _makeAndPositionOptions(name, menuMeasurements) {
      const options = this._options[name];
      const { w: menuWidth, px: menuPX, py: menuPY } = menuMeasurements;
      const { w: ownWidth, px: ownPX, py: ownPY } = this._ownMeasurements;
      const optionsTop = menuPY - ownPY;
      const optionsRight = ownWidth + ownPX - menuPX - menuWidth;
      return makeOptions(options, { top: optionsTop, right: optionsRight });
    },

    render() {
      return (
        <View ref="Container" onLayout={this.onLayout} style={{ flex: 1 }}>
          <View style={this.props.style}>
            { this.props.children }
          </View>
          <TouchableWithoutFeedback onPress={this.closeMenu}>
            <View style={[ styles.backdrop
                          , this.state.openedMenu ? { width: this.state.backdropWidth, top: 0, bottom: 0 }: null ]}/>
          </TouchableWithoutFeedback>
          { this.state.menuOptions }
        </View>
      )
    }
  });

  return MenuContext;
};
<commit_msg>Fixed compatibility with RN 26<commit_after>const TimerMixin = require('react-timer-mixin');

let nextID = 1;

module.exports = (React, ReactNative, { constants, model, styles }) => {
  const {
    NativeModules: { UIManager },
    TouchableWithoutFeedback,
    ScrollView,
    View
  } = ReactNative;
  const AnimatedOptionsContainer = require('./makeAnimatedOptionsContainer')(React, ReactNative);

  // Calls a function once, then never again.
  const once = (fn) => {
    let called = false;
    return (...args) => {
      if (!called) {
        called = true;
        fn(...args);
      }
    };
  };

  const defaultOptionsContainerRenderer = (options) => (
    <ScrollView>
      {options}
    </ScrollView>
  );

  const makeOptions = (options, { top, right }) => {
    const { optionsContainerStyle, renderOptionsContainer = defaultOptionsContainerRenderer} = options.props;
    return (
      <AnimatedOptionsContainer style={[ styles.optionsContainer, optionsContainerStyle, { top, right }]}>
        { renderOptionsContainer(options) }
      </AnimatedOptionsContainer>
    );
  };

  const NULL_HOOKS = {
    didOpen: () => {},
    didClose: () => {}
  }

  /*
   * The MenuContext provides a tunnel for descendant menu components to access
   * top-level methods. It also allows the <MenuOptions/> element to be placed
   * properly.
   */
  const MenuContext = React.createClass({
    displayName: 'MenuContext',
    mixins: [TimerMixin],

    // Public methods
    isMenuOpen() {
      return this.state.openedMenu
    },
    openMenu(name) {
      const handle = ReactNative.findNodeHandle(this._menus[name].ref);
      UIManager.measure(handle, (x, y, w, h, px, py) => {
        this._menus[name].measurements = { x, y, w, h, px, py };

        this.setState({
          openedMenu: name,
          menuOptions: this._makeAndPositionOptions(name, this._menus[name].measurements),
          backdropWidth: this._ownMeasurements.w
        });

        this._activeMenuHooks = this._menus[name];
        this._activeMenuHooks && this._activeMenuHooks.didOpen();
      });
    },
    closeMenu() {
      this.setState({
        openedMenu: '',
        menuOptions: null
      });

      this._activeMenuHooks && this._activeMenuHooks.didClose();
      this._activeMenuHooks = NULL_HOOKS;
    },
    toggleMenu(name) {
      if (this.state.openedMenu === name) {
        this.closeMenu(name);
      } else {
        this.openMenu(name);
      }
    },

    // Private and lifecycle methods
    getInitialState() {
      return {
        openedMenu: '',
        menuOptions: null,
        optionsTop: 0,
        optionsRight: 0,
        backdropWidth: 0
      };
    },
    childContextTypes: {
      menuController: model.IMenuController
    },
    getChildContext() {
      const menuController = {
        open: this.openMenu,
        close: this.closeMenu,
        toggle: this.toggleMenu,
        registerMenu: this._registerMenu,
        unregisterMenu: this._unregisterMenu,
        registerOptionsElement: this._registerOptionsElement,
        makeName: this._makeName
      };
      return { menuController };
    },
    componentWillMount() {
      this._menus = {};
      this._options = {};
      // Only do this once on initial layout.
      this.onLayout = once(this.onLayout);
    },
    onLayout() {
      const handle = ReactNative.findNodeHandle(this.refs.Container);
      UIManager.measure(handle, (x, y, w, h, px, py) => {
        this._ownMeasurements = {x, y, w, h, px, py};
      });
    },
    _registerMenu(name, hooks) {
      if (this._menus[name]) {
        console.warn(`Menu ${name} has already been registered in this context. Please provide a different name.`);
      }
      this._menus[name] = hooks;
    },
    _unregisterMenu(name) {
      delete this._menus[name];
      delete this._options[name];
    },
    _registerOptionsElement(name, options) {
      // If options are already set and visible, skip the update.
      if (this.state.menuOptions === options) {
        return;
      }
      this._options[name] = options;
      // If the menu is already open, re-render the options.
      this.setTimeout(() => {
        if (this.state.openedMenu === name) {
          this.setState({ menuOptions: this._makeAndPositionOptions(name, this._menus[name].measurements) });
        }
      }, 16);
    },
    _makeName() {
      return `menu-${nextID++}`;
    },
    _makeAndPositionOptions(name, menuMeasurements) {
      const options = this._options[name];
      const { w: menuWidth, px: menuPX, py: menuPY } = menuMeasurements;
      const { w: ownWidth, px: ownPX, py: ownPY } = this._ownMeasurements;
      const optionsTop = menuPY - ownPY;
      const optionsRight = ownWidth + ownPX - menuPX - menuWidth;
      return makeOptions(options, { top: optionsTop, right: optionsRight });
    },

    render() {
      return (
        <View ref="Container" onLayout={this.onLayout} style={{ flex: 1 }}>
          <View style={this.props.style}>
            { this.props.children }
          </View>
          <TouchableWithoutFeedback onPress={this.closeMenu}>
            <View style={[ styles.backdrop
                          , this.state.openedMenu ? { width: this.state.backdropWidth, top: 0, bottom: 0 }: null ]}/>
          </TouchableWithoutFeedback>
          { this.state.menuOptions }
        </View>
      )
    }
  });

  return MenuContext;
};
<|endoftext|>


<commit_before>var Bacon = require("baconjs");
var _ = require("lodash");
var request = require("request");

var Logger = require("../logger.js");
var conf = require("./configuration.js");

var Notification = module.exports;

var makeJsonRequest = function(api, verb, url, queryParams, body) {
  //var completeUrl = conf.API_HOST + url
  var completeUrl = 'https://ccapi-preprod.cleverapps.io/v2' + url
  Logger.debug(verb + ' ' + completeUrl);
  var options = {
    method: verb,
    agent: new (require("https").Agent)({ keepAlive: true }),
    url: completeUrl,
    headers: {
      authorization: api.session.getAuthorization(verb, completeUrl, queryParams),
      "Accept": "application/json"
    }
  };

  if(body) options.json = body;
  var s_res = Bacon.fromNodeCallback(request, options);

  return s_res.flatMapLatest(function(res) {
    if(typeof res.body === "object") return res.body;

    var jsonBody = _.attempt(JSON.parse, res.body);
    if(!_.isError(jsonBody) && _.isArray(jsonBody)) {
      return jsonBody;
    } else {
      if(!_.isError(jsonBody) && jsonBody["type"] === "error") {
        return new Bacon.Error(jsonBody);
      } else {
        return new Bacon.Error("Received invalid JSON: " + res.body);
      }
    }
  });
}

Notification.list = function(api, owner_id, entity_id) {
  Logger.debug("Fetching notifications for " + owner_id);
  var s_res = makeJsonRequest(api, 'GET', '/notifications/webhooks/' + owner_id, {});
  return s_res.map(function(hooks) {
    return hooks.filter(function(hook) {
      var emptyScope = !hook.scope || hook.scope.length == 0;
      return !entity_id || emptyScope || hook.scope.indexOf(entity_id) >= 0;
    });
  });
};

Notification.add = function(api, owner_id, name, urls, scope, events) {
  Logger.debug("Registering notification for " + owner_id);

  var body = { name: name, urls: urls, scope: [], events: [] };

  if(scope) body.scope = scope;
  if(events) body.events = events;

  var s_res = makeJsonRequest(api, 'POST', '/notifications/webhooks/' + owner_id, {}, body);
  return s_res;
};

Notification.remove = function(api, owner_id, notif_id) {
  Logger.debug("Removing notification " + notif_id + " for " + owner_id);

  var s_res = makeJsonRequest(api, 'DELETE', '/notifications/webhooks/' + owner_id + '/' + notif_id, {});
  return s_res;
};

<commit_msg>Properly handle DELETE notifications response<commit_after>var Bacon = require("baconjs");
var _ = require("lodash");
var request = require("request");

var Logger = require("../logger.js");
var conf = require("./configuration.js");

var Notification = module.exports;

var makeJsonRequest = function(api, verb, url, queryParams, body) {
  //var completeUrl = conf.API_HOST + url
  var completeUrl = 'https://ccapi-preprod.cleverapps.io/v2' + url
  Logger.debug(verb + ' ' + completeUrl);
  var options = {
    method: verb,
    agent: new (require("https").Agent)({ keepAlive: true }),
    url: completeUrl,
    headers: {
      authorization: api.session.getAuthorization(verb, completeUrl, queryParams),
      "Accept": "application/json"
    }
  };

  if(body) options.json = body;
  var s_res = Bacon.fromNodeCallback(request, options);

  return s_res.flatMapLatest(function(res) {
    if(typeof res.body === "object") return res.body;

    var jsonBody = _.attempt(JSON.parse, res.body);
    if(!_.isError(jsonBody) && _.isArray(jsonBody)) {
      return jsonBody;
    } else {
      if(!_.isError(jsonBody) && jsonBody["type"] === "error") {
        return new Bacon.Error(jsonBody);
      } else {
        return new Bacon.Error("Received invalid JSON: " + res.body);
      }
    }
  });
}

Notification.list = function(api, owner_id, entity_id) {
  Logger.debug("Fetching notifications for " + owner_id);
  var s_res = makeJsonRequest(api, 'GET', '/notifications/webhooks/' + owner_id, {});
  return s_res.map(function(hooks) {
    return hooks.filter(function(hook) {
      var emptyScope = !hook.scope || hook.scope.length == 0;
      return !entity_id || emptyScope || hook.scope.indexOf(entity_id) >= 0;
    });
  });
};

Notification.add = function(api, owner_id, name, urls, scope, events) {
  Logger.debug("Registering notification for " + owner_id);

  var body = { name: name, urls: urls, scope: [], events: [] };

  if(scope) body.scope = scope;
  if(events) body.events = events;

  var s_res = makeJsonRequest(api, 'POST', '/notifications/webhooks/' + owner_id, {}, body);
  return s_res;
};

Notification.remove = function(api, owner_id, notif_id) {
  Logger.debug("Removing notification " + notif_id + " for " + owner_id);

  var s_res = makeJsonRequest(api, 'DELETE', '/notifications/webhooks/' + owner_id + '/' + notif_id, {});
  return s_res.flatMapError(function(error) {
    if(error === 'Received invalid JSON: ') {
      return null;
    } else {
      return new Bacon.Error(error);
    }
  });
};

<|endoftext|>


<commit_before>/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var mysql = require("mysql-libmysqlclient");
var Config = require("./config.js");
var Logger = require("./logger.js");
var Rank = require("./rank.js");
var Media = require("./media.js").Media;
//var Server = require("./server.js");

var initialized = false;

exports.getConnection = function() {
    var db = mysql.createConnectionSync();
    db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
                   Config.MYSQL_PASSWORD, Config.MYSQL_DB);
    if(!db.connectedSync()) {
        Logger.errlog.log("database.getConnection: DB connection failed");
        return false;
    }
    return db;
}

function sqlEscape(data) {
    if(data == null || data == undefined)
        return "NULL";
    else if(typeof data == "number")
        return data;
    else if(typeof data == "object")
        return "(object)";
    else if(typeof data == "string") {
        return data.replace("'", "\\'");
    }
}

exports.init = function() {
    if(initialized)
        return;

    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.init: DB conection failed");
        return;
    }
    var query = "CREATE TABLE IF NOT EXISTS `channels` \
                    (`id` INT NOT NULL AUTO_INCREMENT, \
                     `name` VARCHAR(255) NOT NULL, \
                     PRIMARY KEY (`id`)) \
                     ENGINE = MyISAM;";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: channel table init failed!");
        return false;
    }

    var query = "CREATE TABLE IF NOT EXISTS `registrations` \
                    (`id` INT NOT NULL AUTO_INCREMENT, \
                     `uname` VARCHAR(20) NOT NULL, \
                     `pw` VARCHAR(64) NOT NULL, \
                     `global_rank` INT NOT NULL, \
                     PRIMARY KEY (`id`)) \
                     ENGINE = MyISAM;";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: registration table init failed!");
        return false;
    }

    var query = "CREATE TABLE IF NOT EXISTS `global_bans` \
                    (`ip` VARCHAR( 15 ) NOT NULL ,\
                    `note` VARCHAR( 255 ) NOT NULL ,\
                    PRIMARY KEY (`ip`)\
                    ) ENGINE = MYISAM";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: global ban table init failed!");
        return false;
    }

    initialized = true;
    db.closeSync();
    return true;
}

var gbanTime = 0;
var gbans = {};
exports.checkGlobalBan = function(ip) {
    // Check database at most once per 5 minutes
    if(new Date().getTime() > gbanTime + 300000) {
        exports.refreshGlobalBans();
    }
    var parts = ip.split(".");
    var slash16 = parts[0] + "." + parts[1];
    var slash24 = slash16 + "." + parts[2];
    return (ip in gbans || slash16 in gbans || slash24 in gbans);
}

exports.refreshGlobalBans = function() {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    // Check if channel exists
    var query = "SELECT * FROM global_bans WHERE 1";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("loadGlobalBans: query failed");
        return false;
    }
    var rows = results.fetchAllSync();
    gbans = {};
    for(var i = 0; i < rows.length; i++) {
        gbans[rows[i].ip] = rows[i].note;
    }
    db.closeSync();
    gbanTime = new Date().getTime();
    return gbans;
}

exports.addGlobalBan = function(ip, reason) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    var query = "INSERT INTO global_bans VALUES ('{1}', '{2}')"
        .replace("{1}", sqlEscape(ip))
        .replace("{2}", sqlEscape(reason));
    var result = db.querySync(query);
    db.closeSync();
    return result;
}

exports.liftGlobalBan = function(ip) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    var query = "DELETE FROM global_bans WHERE ip='{}'"
        .replace("{}", sqlEscape(ip))
    var result = db.querySync(query);
    db.closeSync();
    return result;
}

exports.loadChannel = function(chan) {
    var db = exports.getConnection();
    if(!db) {
        chan.logger.log("!!! Could not connect to database");
        return;
    }
    // Check if channel exists
    var query = "SELECT * FROM channels WHERE name='{}'"
        .replace("{}", chan.name);
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: Channel query failed");
        return;
    }
    var rows = results.fetchAllSync();
    if(rows.length == 0) {
        Logger.syslog.log("Channel " + chan.name + " is unregistered.");
        return;
    }
    else if(rows[0].name != chan.name) {
        chan.name = rows[0].name;
    }
    chan.registered = true;

    // Load library
    var query = "SELECT * FROM chan_{}_library"
        .replace("{}", sqlEscape(chan.name));
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: failed to load library for " + chan.name);
        return;
    }
    var rows = results.fetchAllSync();
    for(var i = 0; i < rows.length; i++) {
        chan.library[rows[i].id] = new Media(rows[i].id, rows[i].title, rows[i].seconds, rows[i].type);
    }

    // Load bans
    var query = "SELECT * FROM chan_{}_bans"
        .replace("{}", sqlEscape(chan.name));
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: failed to load banlist for " + chan.name);
        return;
    }
    var rows = results.fetchAllSync();
    for(var i = 0; i < rows.length; i++) {
        chan.ipbans[rows[i].ip] = [rows[i].name, rows[i].banner];
    }

    chan.logger.log("*** Loaded channel from database");
    Logger.syslog.log("Loaded channel " + chan.name + " from database");
    db.closeSync();

}

exports.registerChannel = function(chan) {
    var db = exports.getConnection();
    if(!db) {
        chan.logger.log("!!! Could not connect to database");
        return false;
    }
    // Create library table
    var query= "CREATE TABLE `chan_{}_library` \
                    (`id` VARCHAR(255) NOT NULL, \
                    `title` VARCHAR(255) NOT NULL, \
                    `seconds` INT NOT NULL, \
                    `playtime` VARCHAR(8) NOT NULL, \
                    `type` VARCHAR(2) NOT NULL, \
                    PRIMARY KEY (`id`)) \
                    ENGINE = MyISAM;"
        .replace("{}", chan.name);
    var results = db.querySync(query);

    // Create rank table
    var query = "CREATE TABLE  `chan_{}_ranks` (\
                    `name` VARCHAR( 32 ) NOT NULL ,\
                    `rank` INT NOT NULL ,\
                    UNIQUE (\
                    `name`\
                    )\
                    ) ENGINE = MYISAM"
        .replace("{}", chan.name);
    results = db.querySync(query) || results;

    // Create ban table
    var query = "CREATE TABLE  `chan_{}_bans` (\
                    `ip` VARCHAR( 15 ) NOT NULL ,\
                    `name` VARCHAR( 32 ) NOT NULL ,\
                    `banner` VARCHAR( 32 ) NOT NULL ,\
                    PRIMARY KEY (\
                    `ip`\
                    )\
                    ) ENGINE = MYISAM"
        .replace("{}", chan.name);
    results = db.querySync(query) || results;

    // Insert into global channel table
    var query = "INSERT INTO channels (`id`, `name`) VALUES (NULL, '{}')"
        .replace("{}", sqlEscape(chan.name));
    results = db.querySync(query) || results;
    db.closeSync();
    return results;
}

exports.lookupChannelRank = function(channame, username) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.lookupChannelRank: DB connection failed");
        return Rank.Guest;
    }
    var query = "SELECT * FROM chan_{1}_ranks WHERE name='{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(username));
    var results = db.querySync(query);
    if(!results) {
        return Rank.Guest;
    }
    var rows = results.fetchAllSync();
    if(rows.length == 0) {
        return Rank.Guest;
    }

    db.closeSync();
    return rows[0].rank;
}

exports.saveChannelRank = function(channame, user) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.saveChannelRank: DB connection failed");
        return false;
    }
    var query = "UPDATE chan_{1}_ranks SET rank='{2}' WHERE name='{3}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(user.rank))
        .replace("{3}", sqlEscape(user.name));
    var results = db.querySync(query);
    // Gonna have to insert a new one, bugger
    if(!results.fetchAllSync) {
        var query = "INSERT INTO chan_{1}_ranks (`name`, `rank`) VALUES ('{2}', '{3}')"
            .replace("{1}", sqlEscape(channame))
            .replace("{2}", sqlEscape(user.name))
            .replace("{3}", sqlEscape(user.rank));
        results = db.querySync(query);
    }
    db.closeSync();
    return results;
}

exports.cacheMedia = function(channame, media) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.cacheMedia: DB connection failed");
        return false;
    }
    var query = "INSERT INTO chan_{1}_library VALUES ('{2}', '{3}', {4}, '{5}', '{6}')"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(media.id))
        .replace("{3}", sqlEscape(media.title))
        .replace("{4}", sqlEscape(media.seconds))
        .replace("{5}", sqlEscape(media.duration))
        .replace("{6}", sqlEscape(media.type));
    var results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.uncacheMedia = function(channame, id) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.uncacheMedia: DB connection failed");
        return false;
    }
    var query = "DELETE FROM chan_{1}_library WHERE id='{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(id))
    var results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.addChannelBan = function(channame, actor, receiver) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("exports.addChannelBan: DB connection failed");
        return false;
    }
    var query = "INSERT INTO chan_{1}_bans (`ip`, `name`, `banner`) VALUES ('{2}', '{3}', '{4}')"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(receiver.ip))
        .replace("{3}", sqlEscape(receiver.name))
        .replace("{4}", sqlEscape(actor.name));
    results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.removeChannelBan = function(channame, ip) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("exports.removeChannelBan: DB connection failed");
        return false;
    }
    var query = "DELETE FROM chan_{1}_bans WHERE `ip` = '{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(ip));
    results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.getChannelRanks = function(channame) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }

    var query = "SELECT * FROM chan_{}_ranks WHERE 1"
        .replace("{}", sqlEscape(channame));

    var results = db.querySync(query);
    if(results) {
        var rows = results.fetchAllSync();
        db.closeSync();
        return rows;
    }
    else {
        return [];
    }
}
<commit_msg>Fix new registrations table generation<commit_after>/*
The MIT License (MIT)
Copyright (c) 2013 Calvin Montgomery

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var mysql = require("mysql-libmysqlclient");
var Config = require("./config.js");
var Logger = require("./logger.js");
var Rank = require("./rank.js");
var Media = require("./media.js").Media;
//var Server = require("./server.js");

var initialized = false;

exports.getConnection = function() {
    var db = mysql.createConnectionSync();
    db.connectSync(Config.MYSQL_SERVER, Config.MYSQL_USER,
                   Config.MYSQL_PASSWORD, Config.MYSQL_DB);
    if(!db.connectedSync()) {
        Logger.errlog.log("database.getConnection: DB connection failed");
        return false;
    }
    return db;
}

function sqlEscape(data) {
    if(data == null || data == undefined)
        return "NULL";
    else if(typeof data == "number")
        return data;
    else if(typeof data == "object")
        return "(object)";
    else if(typeof data == "string") {
        return data.replace("'", "\\'");
    }
}

exports.init = function() {
    if(initialized)
        return;

    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.init: DB conection failed");
        return;
    }
    var query = "CREATE TABLE IF NOT EXISTS `channels` \
                    (`id` INT NOT NULL AUTO_INCREMENT, \
                     `name` VARCHAR(255) NOT NULL, \
                     PRIMARY KEY (`id`)) \
                     ENGINE = MyISAM;";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: channel table init failed!");
        return false;
    }

    var query = "CREATE TABLE IF NOT EXISTS `registrations` \
                    (`id` INT NOT NULL AUTO_INCREMENT, \
                     `uname` VARCHAR(20) NOT NULL, \
                     `pw` VARCHAR(64) NOT NULL, \
                     `global_rank` INT NOT NULL, \
                     `session_hash` VARCHAR(64) NOT NULL, \
                     `expire` BIGINT NOT NULL, \
                     PRIMARY KEY (`id`)) \
                     ENGINE = MyISAM;";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: registration table init failed!");
        return false;
    }

    var query = "CREATE TABLE IF NOT EXISTS `global_bans` \
                    (`ip` VARCHAR( 15 ) NOT NULL ,\
                    `note` VARCHAR( 255 ) NOT NULL ,\
                    PRIMARY KEY (`ip`)\
                    ) ENGINE = MYISAM";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("database.init: global ban table init failed!");
        return false;
    }

    initialized = true;
    db.closeSync();
    return true;
}

var gbanTime = 0;
var gbans = {};
exports.checkGlobalBan = function(ip) {
    // Check database at most once per 5 minutes
    if(new Date().getTime() > gbanTime + 300000) {
        exports.refreshGlobalBans();
    }
    var parts = ip.split(".");
    var slash16 = parts[0] + "." + parts[1];
    var slash24 = slash16 + "." + parts[2];
    return (ip in gbans || slash16 in gbans || slash24 in gbans);
}

exports.refreshGlobalBans = function() {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    // Check if channel exists
    var query = "SELECT * FROM global_bans WHERE 1";
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("loadGlobalBans: query failed");
        return false;
    }
    var rows = results.fetchAllSync();
    gbans = {};
    for(var i = 0; i < rows.length; i++) {
        gbans[rows[i].ip] = rows[i].note;
    }
    db.closeSync();
    gbanTime = new Date().getTime();
    return gbans;
}

exports.addGlobalBan = function(ip, reason) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    var query = "INSERT INTO global_bans VALUES ('{1}', '{2}')"
        .replace("{1}", sqlEscape(ip))
        .replace("{2}", sqlEscape(reason));
    var result = db.querySync(query);
    db.closeSync();
    return result;
}

exports.liftGlobalBan = function(ip) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }
    var query = "DELETE FROM global_bans WHERE ip='{}'"
        .replace("{}", sqlEscape(ip))
    var result = db.querySync(query);
    db.closeSync();
    return result;
}

exports.loadChannel = function(chan) {
    var db = exports.getConnection();
    if(!db) {
        chan.logger.log("!!! Could not connect to database");
        return;
    }
    // Check if channel exists
    var query = "SELECT * FROM channels WHERE name='{}'"
        .replace("{}", chan.name);
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: Channel query failed");
        return;
    }
    var rows = results.fetchAllSync();
    if(rows.length == 0) {
        Logger.syslog.log("Channel " + chan.name + " is unregistered.");
        return;
    }
    else if(rows[0].name != chan.name) {
        chan.name = rows[0].name;
    }
    chan.registered = true;

    // Load library
    var query = "SELECT * FROM chan_{}_library"
        .replace("{}", sqlEscape(chan.name));
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: failed to load library for " + chan.name);
        return;
    }
    var rows = results.fetchAllSync();
    for(var i = 0; i < rows.length; i++) {
        chan.library[rows[i].id] = new Media(rows[i].id, rows[i].title, rows[i].seconds, rows[i].type);
    }

    // Load bans
    var query = "SELECT * FROM chan_{}_bans"
        .replace("{}", sqlEscape(chan.name));
    var results = db.querySync(query);
    if(!results) {
        Logger.errlog.log("Channel.loadMysql: failed to load banlist for " + chan.name);
        return;
    }
    var rows = results.fetchAllSync();
    for(var i = 0; i < rows.length; i++) {
        chan.ipbans[rows[i].ip] = [rows[i].name, rows[i].banner];
    }

    chan.logger.log("*** Loaded channel from database");
    Logger.syslog.log("Loaded channel " + chan.name + " from database");
    db.closeSync();

}

exports.registerChannel = function(chan) {
    var db = exports.getConnection();
    if(!db) {
        chan.logger.log("!!! Could not connect to database");
        return false;
    }
    // Create library table
    var query= "CREATE TABLE `chan_{}_library` \
                    (`id` VARCHAR(255) NOT NULL, \
                    `title` VARCHAR(255) NOT NULL, \
                    `seconds` INT NOT NULL, \
                    `playtime` VARCHAR(8) NOT NULL, \
                    `type` VARCHAR(2) NOT NULL, \
                    PRIMARY KEY (`id`)) \
                    ENGINE = MyISAM;"
        .replace("{}", chan.name);
    var results = db.querySync(query);

    // Create rank table
    var query = "CREATE TABLE  `chan_{}_ranks` (\
                    `name` VARCHAR( 32 ) NOT NULL ,\
                    `rank` INT NOT NULL ,\
                    UNIQUE (\
                    `name`\
                    )\
                    ) ENGINE = MYISAM"
        .replace("{}", chan.name);
    results = db.querySync(query) || results;

    // Create ban table
    var query = "CREATE TABLE  `chan_{}_bans` (\
                    `ip` VARCHAR( 15 ) NOT NULL ,\
                    `name` VARCHAR( 32 ) NOT NULL ,\
                    `banner` VARCHAR( 32 ) NOT NULL ,\
                    PRIMARY KEY (\
                    `ip`\
                    )\
                    ) ENGINE = MYISAM"
        .replace("{}", chan.name);
    results = db.querySync(query) || results;

    // Insert into global channel table
    var query = "INSERT INTO channels (`id`, `name`) VALUES (NULL, '{}')"
        .replace("{}", sqlEscape(chan.name));
    results = db.querySync(query) || results;
    db.closeSync();
    return results;
}

exports.lookupChannelRank = function(channame, username) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.lookupChannelRank: DB connection failed");
        return Rank.Guest;
    }
    var query = "SELECT * FROM chan_{1}_ranks WHERE name='{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(username));
    var results = db.querySync(query);
    if(!results) {
        return Rank.Guest;
    }
    var rows = results.fetchAllSync();
    if(rows.length == 0) {
        return Rank.Guest;
    }

    db.closeSync();
    return rows[0].rank;
}

exports.saveChannelRank = function(channame, user) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.saveChannelRank: DB connection failed");
        return false;
    }
    var query = "UPDATE chan_{1}_ranks SET rank='{2}' WHERE name='{3}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(user.rank))
        .replace("{3}", sqlEscape(user.name));
    var results = db.querySync(query);
    // Gonna have to insert a new one, bugger
    if(!results.fetchAllSync) {
        var query = "INSERT INTO chan_{1}_ranks (`name`, `rank`) VALUES ('{2}', '{3}')"
            .replace("{1}", sqlEscape(channame))
            .replace("{2}", sqlEscape(user.name))
            .replace("{3}", sqlEscape(user.rank));
        results = db.querySync(query);
    }
    db.closeSync();
    return results;
}

exports.cacheMedia = function(channame, media) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.cacheMedia: DB connection failed");
        return false;
    }
    var query = "INSERT INTO chan_{1}_library VALUES ('{2}', '{3}', {4}, '{5}', '{6}')"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(media.id))
        .replace("{3}", sqlEscape(media.title))
        .replace("{4}", sqlEscape(media.seconds))
        .replace("{5}", sqlEscape(media.duration))
        .replace("{6}", sqlEscape(media.type));
    var results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.uncacheMedia = function(channame, id) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("database.uncacheMedia: DB connection failed");
        return false;
    }
    var query = "DELETE FROM chan_{1}_library WHERE id='{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(id))
    var results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.addChannelBan = function(channame, actor, receiver) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("exports.addChannelBan: DB connection failed");
        return false;
    }
    var query = "INSERT INTO chan_{1}_bans (`ip`, `name`, `banner`) VALUES ('{2}', '{3}', '{4}')"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(receiver.ip))
        .replace("{3}", sqlEscape(receiver.name))
        .replace("{4}", sqlEscape(actor.name));
    results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.removeChannelBan = function(channame, ip) {
    var db = exports.getConnection();
    if(!db) {
        Logger.errlog.log("exports.removeChannelBan: DB connection failed");
        return false;
    }
    var query = "DELETE FROM chan_{1}_bans WHERE `ip` = '{2}'"
        .replace("{1}", sqlEscape(channame))
        .replace("{2}", sqlEscape(ip));
    results = db.querySync(query);
    db.closeSync();
    return results;
}

exports.getChannelRanks = function(channame) {
    var db = exports.getConnection();
    if(!db) {
        return false;
    }

    var query = "SELECT * FROM chan_{}_ranks WHERE 1"
        .replace("{}", sqlEscape(channame));

    var results = db.querySync(query);
    if(results) {
        var rows = results.fetchAllSync();
        db.closeSync();
        return rows;
    }
    else {
        return [];
    }
}
<|endoftext|>


<commit_before>'use strict';

var ZOOM = 1;
var OFFSET_PX = 1;
var MIN_WIDTH = 12;
var MIN_HEIGHT = 8;

function animationDuration(distance) {
	return Math.pow(inRange(distance, 32, 1024), 1/3) * 50;
}

function easing(x) {
	return 1 - (1 - Math.pow(x, 1/4));
}

function opacityEasing(x) {
	var Q = 4;
	if (x < 1/Q) {
		var y = Q * (-Q*x*x + 2*x);
	} else {
		y = Math.pow(x - 1 - 1/Q, 2);
	}
	return y * 0.4 + 0.2;
}

var svg = null;
var polygon = null;
function initialize() {
	var root = createPolygon();
	svg = root.svg;
	polygon = root.polygon;
	document.body.appendChild(svg);
}

function scrollOffset() {
	var win = document.defaultView;
	var docElem = document.documentElement;
	var body = document.body;
	var top = win.pageYOffset || docElem.scrollTop  || body.scrollTop;
	var left = win.pageXOffset || docElem.scrollLeft || body.scrollLeft;
	return {
		top: top,
		left: left
	};
}

function offsetOf(elem) {
	var rect = elem.getBoundingClientRect();
	var docElem = document.documentElement;
	var body = document.body;

	var scroll = scrollOffset();

	var clientTop  = docElem.clientTop  || body.clientTop  || 0,
		clientLeft = docElem.clientLeft || body.clientLeft || 0,
		top  = rect.top  + scroll.top  - clientTop,
		left = rect.left + scroll.left - clientLeft;

	return {top: top, left: left};
}

var prevFocused = null;
var isFirstFocus = true;
var keyDownTime = 0;

document.documentElement.addEventListener('keydown', function(event) {
	var code = event.which;
	// Show animation only upon Tab or Arrow keys press.
	if (code === 9 || (code > 36 && code < 41)) {
		keyDownTime = Date.now();
	}
}, false);

var prev = null;
var current = null;
var animationId = 0;

document.documentElement.addEventListener('focus', function(event) {
	var target = event.target;
	if (target.id === 'focus-snail') {
		return;
	}
	var offset = offsetOf(target);
	prev = current;
	current = {
		left: offset.left - OFFSET_PX,
		top: offset.top - OFFSET_PX,
		width: Math.max(MIN_WIDTH, target.offsetWidth) + 2*OFFSET_PX,
		height: Math.max(MIN_HEIGHT, target.offsetHeight) + 2*OFFSET_PX
	};

	if (isFirstFocus) {
		isFirstFocus = false;
		return;
	}

	if (Date.now() - keyDownTime > 42) {
		return;
	}

	if (!svg) {
		initialize();
	}

	onEnd();

	prevFocused = target;

	var left = 0;
	var prevLeft = 0;
	var top = 0;
	var prevTop = 0;
	var isFirstCall = true;

	var distance = euclideanDistance(prev, current);
	var duration = animationDuration(distance);

	animate(function(step) {
		if (isFirstCall) {
			setup();
			isFirstCall = false;
		}

		var e = easing(step);
		tick(
			between(prevLeft + (prev.width - ZOOM * prev.width)  / 2, left, e),
			between(prevTop + (prev.height - ZOOM * prev.height) / 2, top, e),
			between(ZOOM * prev.width,  current.width,  e),
			between(ZOOM * prev.height, current.height, e)
		);

		if (step < 1) {
			var opacity = inRange(opacityEasing(step), 0, 1);
			polygon.style.opacity = opacity;
		} else {
			polygon.style.opacity = '';
			onEnd();
		}
	}, duration);

	function tick(_left, _top, _width, _height) {
		var list = getPointsList({
			top: _top,
			right: _left + _width,
			bottom: _top + _height,
			left: _left
		}, {
			top: top,
			right: left + current.width,
			bottom: top + current.height,
			left: left
		});
		enclose(list, polygon);
	}

	function setup() {
		var scroll = scrollOffset();
		svg.style.left = scroll.left + 'px';
		svg.style.top = scroll.top + 'px';
		svg.setAttribute('width', window.innerWidth);
		svg.setAttribute('height', window.innerHeight);
		svg.classList.add('focus-snail_visible');
		left = current.left - scroll.left;
		prevLeft = prev.left - scroll.left;
		top = current.top - scroll.top;
		prevTop = prev.top - scroll.top;
	}

}, true);

function onEnd() {
	if (animationId) {
		cancelAnimationFrame(animationId);
		animationId = 0;
	}
	svg.classList.remove('focus-snail_visible');
	prevFocused = null;
}

function euclideanDistance(a ,b) {
	var dx = a.left - b.left;
	var dy = a.top - b.top;
	return Math.sqrt(dx*dx + dy*dy);
}

function inRange(value, from, to) {
	if (value <= from) {
		return from;
	}
	if (value >= to) {
		return to;
	}
	return value;
}

function between(from, to, value) {
	if (value <= 0) {
		return from;
	}
	if (value >= 1) {
		return to;
	}
	return from * (1 - value) + to * value;
}

function animate(onStep, duration) {
	var start = Date.now();
	(function loop() {
		animationId = requestAnimationFrame(function() {
			var diff = Date.now() - start;
			var x = diff / duration;
			onStep(x);
			if (diff < duration) {
				loop();
			}
		});
	})();
}

function getPointsList(a, b) {
	var x = 0;

	if (a.top < b.top)
		x = 1;

	if (a.right > b.right)
		x += 2;

	if (a.bottom > b.bottom)
		x += 4;

	if (a.left < b.left)
		x += 8;

	var dict = [
		[],
		[0, 1],
		[1, 2],
		[0, 1, 2],
		[2, 3],
		[0, 1], // FIXME: do two polygons
		[1, 2, 3],
		[0, 1, 2, 3],
		[3, 0],
		[3, 0, 1],
		[3, 0], // FIXME: do two polygons
		[3, 0, 1, 2],
		[2, 3, 0],
		[2, 3, 0, 1],
		[1, 2, 3, 0],
		[0, 1, 2, 3, 0]
	];

	var points = rectPoints(a).concat(rectPoints(b));
	var list = [];
	var indexes = dict[x];
	for (var i = 0; i < indexes.length; i++) {
		list.push(points[indexes[i]]);
	}
	while (i--) {
		list.push(points[indexes[i] + 4]);
	}
	return list;
}

function createPolygon() {
	var dict = htmlFragment('<svg id="focus-snail_svg" width="1000" height="800" xmlns:xlink="http://www.w3.org/1999/xlink">\
		<polygon id="focus-snail_polygon"/>\
	</svg>', 'focus-snail_');

	var svg = dict.svg;
	var polygon = dict.polygon;

	var points = [];
	for (var i = 4; i > 0; i--) {
		var point = svg.createSVGPoint();
		points.push(point);
		polygon.points.appendItem(point);
	}
	return dict;
}

function enclose(list, polygon) {
	polygon.points.clear();
	for (var i = 0; i < list.length; i++) {
		var p = list[i];
		addPoint(polygon, p);
	}
}

function addPoint(polygon, point) {
	var pt = svg.createSVGPoint();
	pt.x = point.x;
	pt.y = point.y;
	polygon.points.appendItem(pt);
}

function rectPoints(rect) {
	return [
		{
			x: rect.left,
			y: rect.top
		},
		{
			x: rect.right,
			y: rect.top
		},
		{
			x: rect.right,
			y: rect.bottom
		},
		{
			x: rect.left,
			y: rect.bottom
		}
	]
}

function htmlFragment(content, prefix) {
	var dummy = document.createElement('div');
	dummy.innerHTML = content;
	var all = dummy.querySelectorAll('*');
	var result = {};
	for (var i = 0; i < all.length; i++) {
		var element = all[i];
		var id = element.id;
		if (prefix) {
			id = id.slice(prefix.length);
		}
		result[id] = element;
	}
	return result;
}
<commit_msg>A strawman for gradient and code polish.<commit_after>'use strict';

var ZOOM = 1;
var OFFSET_PX = 1;
var MIN_WIDTH = 12;
var MIN_HEIGHT = 8;

function animationDuration(distance) {
	return Math.pow(inRange(distance, 32, 1024), 1/3) * 50;
}

function easing(x) {
	return 1 - (1 - Math.pow(x, 1/4));
}

function opacityEasing(x) {
	var Q = 4;
	if (x < 1/Q) {
		var y = Q * (-Q*x*x + 2*x);
	} else {
		y = Math.pow(x - 1 - 1/Q, 2);
	}
	return y * 0.4 + 0.2;
}

var keyDownTime = 0;

document.documentElement.addEventListener('keydown', function(event) {
	var code = event.which;
	// Show animation only upon Tab or Arrow keys press.
	if (code === 9 || (code > 36 && code < 41)) {
		keyDownTime = Date.now();
	}
}, false);

var prevFocused = null;
var isFirstFocus = true;
var prev = null;
var current = null;
var animationId = 0;

document.documentElement.addEventListener('focus', function(event) {
	var target = event.target;
	if (target.id === 'focus-snail') {
		return;
	}
	var offset = offsetOf(target);
	prev = current;
	current = {
		left: offset.left - OFFSET_PX,
		top: offset.top - OFFSET_PX,
		width: Math.max(MIN_WIDTH, target.offsetWidth) + 2*OFFSET_PX,
		height: Math.max(MIN_HEIGHT, target.offsetHeight) + 2*OFFSET_PX
	};

	if (isFirstFocus) {
		isFirstFocus = false;
		return;
	}

	if (Date.now() - keyDownTime > 42) {
		return;
	}

	if (!svg) {
		initialize();
	}

	onEnd();

	prevFocused = target;

	var left = 0;
	var prevLeft = 0;
	var top = 0;
	var prevTop = 0;
	var isFirstCall = true;

	var distance = euclideanDistance(prev, current);
	var duration = animationDuration(distance);

	animate(function(step) {
		if (isFirstCall) {
			setup();
			isFirstCall = false;
		}

		var e = easing(step);
		tick(
			between(prevLeft + (prev.width - ZOOM * prev.width)  / 2, left, e),
			between(prevTop + (prev.height - ZOOM * prev.height) / 2, top, e),
			between(ZOOM * prev.width,  current.width,  e),
			between(ZOOM * prev.height, current.height, e)
		);

		if (step < 1) {
			var opacity = inRange(opacityEasing(step), 0, 1);
			polygon.style.opacity = opacity;
		} else {
			polygon.style.opacity = '';
			onEnd();
		}
	}, duration);

	function tick(_left, _top, _width, _height) {
		var list = getPointsList({
			top: _top,
			right: _left + _width,
			bottom: _top + _height,
			left: _left
		}, {
			top: top,
			right: left + current.width,
			bottom: top + current.height,
			left: left
		});
		enclose(list, polygon);
	}

	function setup() {
		var scroll = scrollOffset();
		svg.style.left = scroll.left + 'px';
		svg.style.top = scroll.top + 'px';
		svg.setAttribute('width', window.innerWidth);
		svg.setAttribute('height', window.innerHeight);
		svg.classList.add('focus-snail_visible');
		left = current.left - scroll.left;
		prevLeft = prev.left - scroll.left;
		top = current.top - scroll.top;
		prevTop = prev.top - scroll.top;
	}

}, true);

var svg = null;
var polygon = null;
var start = null;
var end = null;

function initialize() {
	var dict = htmlFragment('<svg id="focus-snail_svg" width="1000" height="800" xmlns:xlink="http://www.w3.org/1999/xlink">\
		<linearGradient id="focus-snail_gradient" x1="0" x2="0" y1="0" y2="2">\
			<stop id="focus-snail_start" offset="0%" stop-color="rgb(91, 157, 217)" stop-opacity="0"/>\
			<stop id="focus-snail_end" offset="100%" stop-color="rgb(91, 157, 217)" stop-opacity="1"/>\
		</linearGradient>\
		<polygon id="focus-snail_polygon"/>\
	</svg>', 'focus-snail_');
	svg = dict.svg;
	polygon = dict.polygon;
	start = dict.start;
	end = dict.end;
	document.body.appendChild(svg);
}

function onEnd() {
	if (animationId) {
		cancelAnimationFrame(animationId);
		animationId = 0;
	}
	svg.classList.remove('focus-snail_visible');
	prevFocused = null;
}

function euclideanDistance(a ,b) {
	var dx = a.left - b.left;
	var dy = a.top - b.top;
	return Math.sqrt(dx*dx + dy*dy);
}

function inRange(value, from, to) {
	if (value <= from) {
		return from;
	}
	if (value >= to) {
		return to;
	}
	return value;
}

function between(from, to, value) {
	if (value <= 0) {
		return from;
	}
	if (value >= 1) {
		return to;
	}
	return from * (1 - value) + to * value;
}

function animate(onStep, duration) {
	var start = Date.now();
	(function loop() {
		animationId = requestAnimationFrame(function() {
			var diff = Date.now() - start;
			var x = diff / duration;
			onStep(x);
			if (diff < duration) {
				loop();
			}
		});
	})();
}

function getPointsList(a, b) {
	var x = 0;

	if (a.top < b.top)
		x = 1;

	if (a.right > b.right)
		x += 2;

	if (a.bottom > b.bottom)
		x += 4;

	if (a.left < b.left)
		x += 8;

	var dict = [
		[],
		[0, 1],
		[1, 2],
		[0, 1, 2],
		[2, 3],
		[0, 1], // FIXME: do two polygons
		[1, 2, 3],
		[0, 1, 2, 3],
		[3, 0],
		[3, 0, 1],
		[3, 0], // FIXME: do two polygons
		[3, 0, 1, 2],
		[2, 3, 0],
		[2, 3, 0, 1],
		[1, 2, 3, 0],
		[0, 1, 2, 3, 0]
	];

	var points = rectPoints(a).concat(rectPoints(b));
	var list = [];
	var indexes = dict[x];
	for (var i = 0; i < indexes.length; i++) {
		list.push(points[indexes[i]]);
	}
	while (i--) {
		list.push(points[indexes[i] + 4]);
	}
	return list;
}

function enclose(list, polygon) {
	polygon.points.clear();
	for (var i = 0; i < list.length; i++) {
		var p = list[i];
		addPoint(polygon, p);
	}
}

function addPoint(polygon, point) {
	var pt = svg.createSVGPoint();
	pt.x = point.x;
	pt.y = point.y;
	polygon.points.appendItem(pt);
}

function rectPoints(rect) {
	return [
		{
			x: rect.left,
			y: rect.top
		},
		{
			x: rect.right,
			y: rect.top
		},
		{
			x: rect.right,
			y: rect.bottom
		},
		{
			x: rect.left,
			y: rect.bottom
		}
	]
}

function htmlFragment(content, prefix) {
	var dummy = document.createElement('div');
	dummy.innerHTML = content;
	var all = dummy.querySelectorAll('*');
	var result = {};
	for (var i = 0; i < all.length; i++) {
		var element = all[i];
		var id = element.id;
		if (prefix) {
			id = id.slice(prefix.length);
		}
		result[id] = element;
	}
	return result;
}

function scrollOffset() {
	var win = document.defaultView;
	var docElem = document.documentElement;
	var body = document.body;
	var top = win.pageYOffset || docElem.scrollTop  || body.scrollTop;
	var left = win.pageXOffset || docElem.scrollLeft || body.scrollLeft;
	return {
		top: top,
		left: left
	};
}

function offsetOf(elem) {
	var rect = elem.getBoundingClientRect();
	var docElem = document.documentElement;
	var body = document.body;

	var scroll = scrollOffset();

	var clientTop  = docElem.clientTop  || body.clientTop  || 0,
			clientLeft = docElem.clientLeft || body.clientLeft || 0,
			top  = rect.top  + scroll.top  - clientTop,
			left = rect.left + scroll.left - clientLeft;

	return {top: top, left: left};
}
<|endoftext|>


<commit_before>/**
 * base class for primitive dynform fields
 *
 * @param {Object} field_descriptor field specific part of the form schema
 * @param {Object} field_data initial value for the field
 * @constructor
 * @class schnipp.dynforms.primitive_field
 * @extends schnipp.dynforms.abstract_field
 * @module schnipp.dynforms
 **/
schnipp.dynforms.primitive_field = function(field_descriptor, field_data) {
    var self = schnipp.dynforms.abstract_field(field_descriptor, field_data)
    self.dom.input = null /* must be set in subclass */

    /**
     * set data of the field
     * used internally by set_data to actually set the field
     * override this instead of set_data to change how the value is set
     * while retaining the event firing implementation
     * @param {Mixed} field data - format depends on the field type
     * @method _set
     **/
    self._set = function(value) {
        self.dom.input.val(value)
    }

    /**
     * get current field data
     * @return {Mixed} field data - the format of the data depends on the
     * field type
     * @method get_data
     **/
    self.get_data = function() {
        return self.dom.input.val()
    }

    /**
     * render the input portion of the field
     * -must be implemented by subclass-
     *
     * @return {Object} jquery nodelist containing input portion of rendered field view
     * @method render_input
     **/
    self.render_input = function() {
        return self.dom.input
    }

    /**
     * initialize the field - must be called after the field has been rendered
     * and placed into the DOM of the page.
     * @method initialize
     **/
    self.initialize = function() {
        self.dom.input.change(function() {
            self.events.fire('changed', {
                src: self,
                value: self.get_data()
            })
        })
    }

    return self
}
<commit_msg>changed change event from "changed" to "change" as it is defined in the parent implementation => abstractfield<commit_after>/**
 * base class for primitive dynform fields
 *
 * @param {Object} field_descriptor field specific part of the form schema
 * @param {Object} field_data initial value for the field
 * @constructor
 * @class schnipp.dynforms.primitive_field
 * @extends schnipp.dynforms.abstract_field
 * @module schnipp.dynforms
 **/
schnipp.dynforms.primitive_field = function(field_descriptor, field_data) {
    var self = schnipp.dynforms.abstract_field(field_descriptor, field_data)
    self.dom.input = null /* must be set in subclass */

    /**
     * set data of the field
     * used internally by set_data to actually set the field
     * override this instead of set_data to change how the value is set
     * while retaining the event firing implementation
     * @param {Mixed} field data - format depends on the field type
     * @method _set
     **/
    self._set = function(value) {
        self.dom.input.val(value)
    }

    /**
     * get current field data
     * @return {Mixed} field data - the format of the data depends on the
     * field type
     * @method get_data
     **/
    self.get_data = function() {
        return self.dom.input.val()
    }

    /**
     * render the input portion of the field
     * -must be implemented by subclass-
     *
     * @return {Object} jquery nodelist containing input portion of rendered field view
     * @method render_input
     **/
    self.render_input = function() {
        return self.dom.input
    }

    /**
     * initialize the field - must be called after the field has been rendered
     * and placed into the DOM of the page.
     * @method initialize
     **/
    self.initialize = function() {
        self.dom.input.change(function() {
            self.events.fire('change', {
                src: self,
                value: self.get_data()
            })
        })
    }

    return self
}
<|endoftext|>


<commit_before>/*
 * stylestats
 * https://github.com/t32k/stylestats
 *
 * Copyright (c) 2014
 * Licensed under the MIT license.
 */

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var glob = require('glob');
var async = require('async');
var request = require('request');
var gzipSize = require('gzip-size');
var cssParse = require('css-parse');

// Utility functions.
function _isFile() {
    try {
        var file = path.join.apply(path, arguments);
        return fs.existsSync(file) && fs.statSync(file).isFile();
    } catch (e) {
        return false;
    }
}

function _isDirectory() {
    try {
        var dir = path.join.apply(path, arguments);
        return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
    } catch (e) {
        return false;
    }
}

// Constructor function.
function StyleStats(args, config) {

    var that = this;
    var URL = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;

    args = Array.isArray(args) ? args : [args];

    this.urls = [];
    this.files = [];

    args.forEach(function(arg) {
        if (_isFile(arg) && path.extname === '.css') {
            files.push(arg);
        } else if (_isDirectory(arg)) {
            fs.readdirSync(arg).filter(function(file) {
                return (path.extname(file) === '.css');
            }).forEach(function(file) {
                that.files.push(file);
            });
        } else if (URL.test(arg) && path.extname(arg) === '.css') {
            that.urls.push(arg);
        } else {
            glob.sync(arg).filter(function(file) {
                return (path.extname(file) === '.css');
            }).forEach(function(file) {
                that.files.push(file);
            });
        }
    });

    this.stylesheets = this.files.length + this.urls.length;
    this.cssString = '';
    this.cssArray = [];

    var defaultOptions = require('./defaultOptions');
    var customOptions = {};
    if (config && _isFile(config)) {
        var configString = fs.readFileSync(config, {
            encoding: "utf-8"
        });
        try {
            customOptions = JSON.parse(configString);
        } catch (e) {
            throw e;
        }
    } else if (_.isObject(config)) {
        customOptions = config;
    }
    this.options = _.extend(defaultOptions, customOptions);
}

StyleStats.prototype.getGzippedSize = function() {
    return gzipSize.sync(this.cssString);
};

StyleStats.prototype.getPureRules = function() {
    return _.filter(this.rules, function(rule) {
        return rule.type === 'rule' || rule.type === 'media';
    });
};

StyleStats.prototype.getSelectorCount = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            count += rule.selectors.length;
        }
    });
    return count;
};

StyleStats.prototype.getUniqueFont = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.indexOf('font-size') > -1) {
                        array.push(declaration.value.replace(/\!important/, '').trim());
                    }
                }
            });
        }
    });
    return _.uniq(array);
};

StyleStats.prototype.getUniqueColor = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.indexOf('color') > -1) {
                        var color = declaration.value.replace(/\!important/, '');
                        color = color.toUpperCase().trim();
                        array.push(color);
                    }
                }
            });
        }
    });
    array = _.without(array, 'TRANSPARENT')
    return _.uniq(array);
};

StyleStats.prototype.getDeclarationCount = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            array.push({
                selector: rule.selectors,
                count: rule.declarations.length
            });
        }
    });
    return array;
};

StyleStats.prototype.getIdentifierCount = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                var trimedSelector = selector.replace(/\s?([\>|\+|\~])\s?/g, '$1');
                trimedSelector = trimedSelector.replace(/\s+/g, ' ');
                var identifiers = trimedSelector.split(/\s|\>|\+|\~/).length;
                array.push({
                    selector: selector,
                    count: identifiers
                });
            });
        }
    });
    return array;
};

StyleStats.prototype.getIdSelectors = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.indexOf('#') > -1) {
                    count += 1;
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getUniversalSelector = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.indexOf('*') > -1) {
                    count += 1;
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getUnqualifiedAttributeSelector = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.trim().match(/\[.+\]$/g)) {
                    count += 1;
                }
            });
        }
    });
    return count;
};
StyleStats.prototype.getImportantKeywords = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.value !== undefined) {
                    if (declaration.value.indexOf('!important') > -1) {
                        count += 1;
                    }
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getFloatProperty = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.indexOf('float') > -1) {
                        count += 1;
                    }
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getMediaQueries = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (rule.type.indexOf('media') > -1) {
            count += 1;
        }
    });
    return count;
};

StyleStats.prototype.getProperty = function() {
    var propMap = {};
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (propMap[declaration.property]) {
                    propMap[declaration.property] += 1;
                } else {
                    propMap[declaration.property] = 1;
                }
            });
        }
    });
    return propMap;
};

// Return object parsed result.
StyleStats.prototype.parse = function(callback) {

    // for callback
    var that = this;
    this.parseCallback = _.isFunction(callback) ? callback : function() {};

    this.files.forEach(function(file) {
        that.cssArray.push(fs.readFileSync(file, {
            encoding: "utf-8"
        }));
    });

    async.eachSeries(this.urls, function iterator(url, next) {
        request.get(url, function(error, response, body) {
            if (error) {
                throw error;
            }
            if (response.statusCode !== 200) {
                throw new Error('Status code is ' + response.statusCode);
            }
            that.cssArray.push(body);
            next();
        });
    }, function() {
        that.cssString = that.cssArray.join('');
        that.cssSize = Buffer.byteLength(that.cssString, 'utf8');
        that.rules = cssParse(that.cssString).stylesheet.rules;

        var stats = {};
        var options = that.options;

        // Sort font size.
        var uniqueFont = that.getUniqueFont();
        var uniqueFontSize = _.sortBy(uniqueFont.slice(), function(item) {
            return item.replace(/[^0-9\.]/g, '') - 0;
        });

        // Sort color.
        var uniqueColor = _.sortBy(that.getUniqueColor());

        // Sort property count.
        var declarationCount = that.getDeclarationCount();
        declarationCount.sort(function(a, b) {
            return b.count - a.count;
        });

        var identifierCount = that.getIdentifierCount();
        identifierCount.sort(function(a, b) {
            return b.count - a.count;
        });

        if (options.stylesheets) {
            stats.stylesheets = that.stylesheets;
        }
        if (options.size) {
            stats.size = that.cssSize;
        }
        if (options.gzippedSize) {
            stats.gzippedSize = that.getGzippedSize();
        }
        if (options.rules) {
            stats.rules = that.getPureRules().length;
        }
        if (options.selectors) {
            stats.selectors = that.getSelectorCount();
        }
        if (options.rules && options.selectors && options.simplicity) {
            stats.simplicity = stats.rules / stats.selectors;
        }

        var mostIdentifer = identifierCount.shift();
        if (mostIdentifer && options.mostIdentifers) {
            stats.mostIdentifers = mostIdentifer.count;
        }
        if (mostIdentifer && options.mostIdentifersSelector) {
            stats.mostIdentifersSelector = mostIdentifer.selector;
        }

        var lowestDefinition = declarationCount.shift();
        if (lowestDefinition && options.lowestCohesion) {
            stats.lowestCohesion = lowestDefinition.count;
        }
        if (lowestDefinition && options.lowestCohesionSelector) {
            stats.lowestCohesionSelector = lowestDefinition.selector;
        }
        if (options.totalUniqueFontSizes) {
            stats.totalUniqueFontSizes = uniqueFont.length;
        }
        if (options.uniqueFontSize) {
            stats.uniqueFontSize = uniqueFontSize;
        }
        if (options.totalUniqueColors) {
            stats.totalUniqueColors = uniqueColor.length;
        }
        if (options.uniqueColor) {
            stats.uniqueColor = uniqueColor;
        }
        if (options.idSelectors) {
            stats.idSelectors = that.getIdSelectors();
        }
        if (options.universalSelectors) {
            stats.universalSelectors = that.getUniversalSelector();
        }
        if (options.unqualifiedAttributeSelectors) {
            stats.unqualifiedAttributeSelectors = that.getUnqualifiedAttributeSelector();
        }
        if (options.importantKeywords) {
            stats.importantKeywords = that.getImportantKeywords();
        }
        if (options.floatProperties) {
            stats.floatProperties = that.getFloatProperty();
        }
        if (options.mediaQueries) {
            stats.mediaQueries = that.getMediaQueries();
        }
        if (options.propertiesCount) {
            var properties = that.getProperty();
            var propertiesCount = [];
            Object.keys(properties).forEach(function(key) {
                propertiesCount.push({
                    property: key,
                    count: properties[key]
                });
            });

            // sort by property count
            propertiesCount.sort(function(a, b) {
                return b.count - a.count;
            });

            stats.propertiesCount = propertiesCount.slice(0, options.propertiesCount);
        }

        that.parseCallback(stats);
    });
};

module.exports = StyleStats;<commit_msg>Fix unique colors metrics<commit_after>/*
 * stylestats
 * https://github.com/t32k/stylestats
 *
 * Copyright (c) 2014
 * Licensed under the MIT license.
 */

'use strict';

var _ = require('underscore');
var fs = require('fs');
var path = require('path');
var glob = require('glob');
var async = require('async');
var request = require('request');
var gzipSize = require('gzip-size');
var cssParse = require('css-parse');

// Utility functions.
function _isFile() {
    try {
        var file = path.join.apply(path, arguments);
        return fs.existsSync(file) && fs.statSync(file).isFile();
    } catch (e) {
        return false;
    }
}

function _isDirectory() {
    try {
        var dir = path.join.apply(path, arguments);
        return fs.existsSync(dir) && fs.statSync(dir).isDirectory();
    } catch (e) {
        return false;
    }
}

// Constructor function.
function StyleStats(args, config) {

    var that = this;
    var URL = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/;

    args = Array.isArray(args) ? args : [args];

    this.urls = [];
    this.files = [];

    args.forEach(function(arg) {
        if (_isFile(arg) && path.extname === '.css') {
            files.push(arg);
        } else if (_isDirectory(arg)) {
            fs.readdirSync(arg).filter(function(file) {
                return (path.extname(file) === '.css');
            }).forEach(function(file) {
                that.files.push(file);
            });
        } else if (URL.test(arg) && path.extname(arg) === '.css') {
            that.urls.push(arg);
        } else {
            glob.sync(arg).filter(function(file) {
                return (path.extname(file) === '.css');
            }).forEach(function(file) {
                that.files.push(file);
            });
        }
    });

    this.stylesheets = this.files.length + this.urls.length;
    this.cssString = '';
    this.cssArray = [];

    var defaultOptions = require('./defaultOptions');
    var customOptions = {};
    if (config && _isFile(config)) {
        var configString = fs.readFileSync(config, {
            encoding: "utf-8"
        });
        try {
            customOptions = JSON.parse(configString);
        } catch (e) {
            throw e;
        }
    } else if (_.isObject(config)) {
        customOptions = config;
    }
    this.options = _.extend(defaultOptions, customOptions);
}

StyleStats.prototype.getGzippedSize = function() {
    return gzipSize.sync(this.cssString);
};

StyleStats.prototype.getPureRules = function() {
    return _.filter(this.rules, function(rule) {
        return rule.type === 'rule' || rule.type === 'media';
    });
};

StyleStats.prototype.getSelectorCount = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            count += rule.selectors.length;
        }
    });
    return count;
};

StyleStats.prototype.getUniqueFont = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.indexOf('font-size') > -1) {
                        array.push(declaration.value.replace(/\!important/, '').trim());
                    }
                }
            });
        }
    });
    return _.uniq(array);
};

StyleStats.prototype.getUniqueColor = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.match(/^color$/)) {
                        var color = declaration.value.replace(/\!important/, '');
                        color = color.toUpperCase().trim();
                        array.push(color);
                    }
                }
            });
        }
    });
    array = _.without(array, 'TRANSPARENT')
    return _.uniq(array);
};

StyleStats.prototype.getDeclarationCount = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            array.push({
                selector: rule.selectors,
                count: rule.declarations.length
            });
        }
    });
    return array;
};

StyleStats.prototype.getIdentifierCount = function() {
    var array = [];
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                var trimedSelector = selector.replace(/\s?([\>|\+|\~])\s?/g, '$1');
                trimedSelector = trimedSelector.replace(/\s+/g, ' ');
                var identifiers = trimedSelector.split(/\s|\>|\+|\~/).length;
                array.push({
                    selector: selector,
                    count: identifiers
                });
            });
        }
    });
    return array;
};

StyleStats.prototype.getIdSelectors = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.indexOf('#') > -1) {
                    count += 1;
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getUniversalSelector = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.indexOf('*') > -1) {
                    count += 1;
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getUnqualifiedAttributeSelector = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.selectors)) {
            rule.selectors.forEach(function(selector) {
                if (selector.trim().match(/\[.+\]$/g)) {
                    count += 1;
                }
            });
        }
    });
    return count;
};
StyleStats.prototype.getImportantKeywords = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.value !== undefined) {
                    if (declaration.value.indexOf('!important') > -1) {
                        count += 1;
                    }
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getFloatProperty = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (declaration.property !== undefined) {
                    if (declaration.property.indexOf('float') > -1) {
                        count += 1;
                    }
                }
            });
        }
    });
    return count;
};

StyleStats.prototype.getMediaQueries = function() {
    var count = 0;
    this.rules.forEach(function(rule) {
        if (rule.type.indexOf('media') > -1) {
            count += 1;
        }
    });
    return count;
};

StyleStats.prototype.getProperty = function() {
    var propMap = {};
    this.rules.forEach(function(rule) {
        if (Array.isArray(rule.declarations)) {
            rule.declarations.forEach(function(declaration) {
                if (propMap[declaration.property]) {
                    propMap[declaration.property] += 1;
                } else {
                    propMap[declaration.property] = 1;
                }
            });
        }
    });
    return propMap;
};

// Return object parsed result.
StyleStats.prototype.parse = function(callback) {

    // for callback
    var that = this;
    this.parseCallback = _.isFunction(callback) ? callback : function() {};

    this.files.forEach(function(file) {
        that.cssArray.push(fs.readFileSync(file, {
            encoding: "utf-8"
        }));
    });

    async.eachSeries(this.urls, function iterator(url, next) {
        request.get(url, function(error, response, body) {
            if (error) {
                throw error;
            }
            if (response.statusCode !== 200) {
                throw new Error('Status code is ' + response.statusCode);
            }
            that.cssArray.push(body);
            next();
        });
    }, function() {
        that.cssString = that.cssArray.join('');
        that.cssSize = Buffer.byteLength(that.cssString, 'utf8');
        that.rules = cssParse(that.cssString).stylesheet.rules;

        var stats = {};
        var options = that.options;

        // Sort font size.
        var uniqueFont = that.getUniqueFont();
        var uniqueFontSize = _.sortBy(uniqueFont.slice(), function(item) {
            return item.replace(/[^0-9\.]/g, '') - 0;
        });

        // Sort color.
        var uniqueColor = _.sortBy(that.getUniqueColor());

        // Sort property count.
        var declarationCount = that.getDeclarationCount();
        declarationCount.sort(function(a, b) {
            return b.count - a.count;
        });

        var identifierCount = that.getIdentifierCount();
        identifierCount.sort(function(a, b) {
            return b.count - a.count;
        });

        if (options.stylesheets) {
            stats.stylesheets = that.stylesheets;
        }
        if (options.size) {
            stats.size = that.cssSize;
        }
        if (options.gzippedSize) {
            stats.gzippedSize = that.getGzippedSize();
        }
        if (options.rules) {
            stats.rules = that.getPureRules().length;
        }
        if (options.selectors) {
            stats.selectors = that.getSelectorCount();
        }
        if (options.rules && options.selectors && options.simplicity) {
            stats.simplicity = stats.rules / stats.selectors;
        }

        var mostIdentifer = identifierCount.shift();
        if (mostIdentifer && options.mostIdentifers) {
            stats.mostIdentifers = mostIdentifer.count;
        }
        if (mostIdentifer && options.mostIdentifersSelector) {
            stats.mostIdentifersSelector = mostIdentifer.selector;
        }

        var lowestDefinition = declarationCount.shift();
        if (lowestDefinition && options.lowestCohesion) {
            stats.lowestCohesion = lowestDefinition.count;
        }
        if (lowestDefinition && options.lowestCohesionSelector) {
            stats.lowestCohesionSelector = lowestDefinition.selector;
        }
        if (options.totalUniqueFontSizes) {
            stats.totalUniqueFontSizes = uniqueFont.length;
        }
        if (options.uniqueFontSize) {
            stats.uniqueFontSize = uniqueFontSize;
        }
        if (options.totalUniqueColors) {
            stats.totalUniqueColors = uniqueColor.length;
        }
        if (options.uniqueColor) {
            stats.uniqueColor = uniqueColor;
        }
        if (options.idSelectors) {
            stats.idSelectors = that.getIdSelectors();
        }
        if (options.universalSelectors) {
            stats.universalSelectors = that.getUniversalSelector();
        }
        if (options.unqualifiedAttributeSelectors) {
            stats.unqualifiedAttributeSelectors = that.getUnqualifiedAttributeSelector();
        }
        if (options.importantKeywords) {
            stats.importantKeywords = that.getImportantKeywords();
        }
        if (options.floatProperties) {
            stats.floatProperties = that.getFloatProperty();
        }
        if (options.mediaQueries) {
            stats.mediaQueries = that.getMediaQueries();
        }
        if (options.propertiesCount) {
            var properties = that.getProperty();
            var propertiesCount = [];
            Object.keys(properties).forEach(function(key) {
                propertiesCount.push({
                    property: key,
                    count: properties[key]
                });
            });

            // sort by property count
            propertiesCount.sort(function(a, b) {
                return b.count - a.count;
            });

            stats.propertiesCount = propertiesCount.slice(0, options.propertiesCount);
        }

        that.parseCallback(stats);
    });
};

module.exports = StyleStats;<|endoftext|>


<commit_before>const root = __dirname;

module.exports = function(config) {
	config.set({
		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: root,

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['mocha', 'chai'],

		// list of files / patterns to load in the browser
		files: [
			'./**/*.js'
		],

		// list of files to exclude
		exclude: [
		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			'./**/*.js': ['webpack']
		},
		webpackMiddleware: {
			stats: 'errors-only'
		},
		webpack: require('./webpack.config.js'),

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['mocha'],

		// web server port
		port: 9876,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_INFO,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: true,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['Chrome'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: false,

		// Concurrency level
		// how many browser should be started simultaneous
		concurrency: Infinity
	})
};
<commit_msg>Update karma reporting<commit_after>const root = __dirname;

module.exports = function(config) {
	config.set({
		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: root,

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['mocha', 'chai'],

		// list of files / patterns to load in the browser
		files: [
			'./**/*.js'
		],

		// list of files to exclude
		exclude: [
		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			'./**/*.js': ['webpack']
		},
		webpackMiddleware: {
			stats: 'errors-only'
		},
		webpack: require('./webpack.config.js'),

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['mocha'],
		mochaReporter: {
			showDiff: true
		},

		// web server port
		port: 9876,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_ERROR,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: true,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['Chrome'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: false,

		// Concurrency level
		// how many browser should be started simultaneous
		concurrency: Infinity
	})
};
<|endoftext|>


<commit_before>var OrchestraBORouter = Backbone.Router.extend({

//========[ROUTES LIST]===============================//

  routes: {
    'node/show/:nodeId': 'showNode',
    'template/show/:templateId': 'showTemplate',
    'contents/list/:contentTypeId': 'listContents',
    'websites/list': 'listSites',
    'content-types/list': 'listContentTypes',
    'translation': 'listTranslations',
    '': 'showHome'
  },

  initialize: function() {
  },

//========[ACTIONS LIST]==============================//

  showHome: function() {
    drawBreadCrumb();
  },

  showNode: function(nodeId) {
  this.initDisplayRouteChanges();
    showNode($("#nav-node-" + nodeId).data("url"));
  },

  showTemplate: function(templateId) {
    this.initDisplayRouteChanges();
    showTemplate($("#nav-template-" + templateId).data("url"));
  },

  listContents: function(contentTypeId) {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-contents-" + contentTypeId));
  },

  listSites: function() {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-websites"));
  },

  listContentTypes: function() {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-contentTypes"));
  },

  listTranslations: function() {
    drawBreadCrumb();
    return new TranslationView(
      {url : $("#nav-translation").data("url")}
    );
  },

//========[INTERNAL FUNCTIONS]========================//

  initDisplayRouteChanges: function() {
    var url = '#' + Backbone.history.fragment;
    $('nav li.active').removeClass("active");
    $('nav li:has(a[href="' + url + '"])').addClass("active");

    var title = ($('nav a[href="' + url + '"]').attr('title'))
    document.title = (title || document.title);

    drawBreadCrumb();
    displayLoader();
  },

});

var appRouter = new OrchestraBORouter();

Backbone.history.start();
<commit_msg>Fix modal functionnalities<commit_after>var OrchestraBORouter = Backbone.Router.extend({

//========[ROUTES LIST]===============================//

  routes: {
    'node/show/:nodeId': 'showNode',
    'template/show/:templateId': 'showTemplate',
    'contents/list/:contentTypeId': 'listContents',
    'websites/list': 'listSites',
    'content-types/list': 'listContentTypes',
    'translation': 'listTranslations',
    '': 'showHome'
  },

  initialize: function() {
  },

//========[ACTIONS LIST]==============================//

  showHome: function() {
    drawBreadCrumb();
  },

  showNode: function(nodeId) {
    this.initDisplayRouteChanges();
    showNode($("#nav-node-" + nodeId).data("url"));
  },

  createNode: function(parentNodeId) {
    var parentNode = $("#nav-createNode-" + parentNodeId);
    this.showNodeForm(parentNode);
  },

  showNodeForm: function(parentNode) {
    $('.modal-title').text(parentNode.text());
    $.ajax({
      url: parentNode.data('url'),
      method: 'GET',
      success: function(response) {
        $('#OrchestraBOModal').modal('show');
        var view;
        return view = new adminFormView({
          html: response
        });
      }
    });
  },

  showTemplate: function(templateId) {
    this.initDisplayRouteChanges();
    showTemplate($("#nav-template-" + templateId).data("url"));
  },

  listContents: function(contentTypeId) {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-contents-" + contentTypeId));
  },

  listSites: function() {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-websites"));
  },

  listContentTypes: function() {
    this.initDisplayRouteChanges();
    tableViewLoad($("#nav-contentTypes"));
  },

  createTemplate: function() {
    var templateRoot = $("#nav-createTemplate");
    this.showNodeForm(templateRoot);
  },

  listTranslations: function() {
    drawBreadCrumb();
    return new TranslationView(
      {url : $("#nav-translation").data("url")}
    );
  },

//========[INTERNAL FUNCTIONS]========================//

  initDisplayRouteChanges: function() {
    var url = '#' + Backbone.history.fragment;
    $('nav li.active').removeClass("active");
    $('nav li:has(a[href="' + url + '"])').addClass("active");

    var title = ($('nav a[href="' + url + '"]').attr('title'))
    document.title = (title || document.title);

    drawBreadCrumb();
    displayLoader();
  },

});

var appRouter = new OrchestraBORouter();

Backbone.history.start();
<|endoftext|>


<commit_before>const Sequelize = require('sequelize')

const init = () => {
  return new Promise ((resolve) => {
    const sequelize = new Sequelize('status', 'server', '!$%openvpn1', {
      dialect: 'sqlite',
      storage: './data.sqlite',
      logging: false,
      pool: {max: 5, min: 0, idle: 10000}
    })

    const Log = sequelize.define('Log', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      timestamp : {type: Sequelize.INTEGER},
      server    : {type: Sequelize.INTEGER},
      node      : {type: Sequelize.STRING},
      event     : {type: Sequelize.STRING}
    },{
      freezeTableName: true,
      timestamps: false,
      tableName: 'Log'
    })

    module.exports.Log = Log
    module.exports.state = () => sequelize.query('select p1.* from log as p1 inner join (select max(timestamp) as timestamp, node from log group by node) as p2 on p1.node = p2.node and p1.timestamp = p2.timestamp where p1.event = \'connect\'', {model:Log})

    Log.sync().then(resolve)
  })
}

module.exports = {
  init: init,
  op: Sequelize.Op
}
<commit_msg>Simplified database interface and added columns<commit_after>const Sequelize = require('sequelize')

const sequelize = new Sequelize('status', 'server', '!$%openvpn1', {
  dialect: 'sqlite',
  storage: './data.sqlite',
  logging: false,
  pool: {max: 5, min: 0, idle: 10000}
})

const Log = sequelize.define('Log', {
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  timestamp : {type: Sequelize.INTEGER},
  server    : {type: Sequelize.INTEGER},
  pub       : {type: Sequelize.STRING},
  vpn       : {type: Sequelize.STRING},
  node      : {type: Sequelize.STRING},
  event     : {type: Sequelize.STRING}
},{
  freezeTableName: true,
  timestamps: false,
  tableName: 'Log'
})

module.exports = {
  init: () => Log.sync(),
  Log: Log,
  state: () => sequelize.query('select p1.* from log as p1 inner join (select max(timestamp) as timestamp, node from log group by node) as p2 on p1.node = p2.node and p1.timestamp = p2.timestamp where p1.event = \'connect\'', {model:Log}),
  op: Sequelize.Op
}
<|endoftext|>