<commit_before>/* SauceNao query Handler
 *
 *
 * Contributed by Capuccino
 */

const got = require('got');
const urlRegex = str => /(http(s)?:\/\/)?(www\.)?[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi.test(str);

/**
 * Query Handler for SauceNao-related requests.
 */

class SauceHandler {
    /**
     * @param {String} key your API Key for Saucenao
     * @param {Number} outputType type of output you want the API to show. Default is value 2 (JSON Response)
     * @param {Number} numRes amount of responses you want returned from the API. Default is 5 Responses.
     * @see {link} https://saucenao.com/user.php?page=search-api
     */
    constructor({key, numRes, outputType}) {
        if (!key) throw new TypeError('NO API Key provided!');
        this.key = key,
        this.outputType = outputType || 2,
        this.numRes = numRes || 5;
    }
    /**
     * Gets the source and outputs it in your preffered output type
     * @param {String} path filepath for the image you want to get the source from (Not Implemented yet).
     * @param {String} link web address for the source, must be a valid HTTP/HTTPS address.
     * @returns {Promise} and JSON output.
     * @example client.getSauce(path/link).then(res => { console.log(res) });
     */
    getSauce(path, link) {
        return new Promise((resolve, reject) => {
            if (!path === typeof path) {
                throw new TypeError('path is not string');
            } else if (path) {
                /** @todo Finalize this. We need to input file as a stream then convert to MIMEType */
                throw new Error('This is not implemented yet!');
                /* got(`http://saucenao.com/search.php?output_type=${this.outputType}&numres=${this.numRes}&api_key=${this.key}`, {}).then(res => {
                 });*/
            } else if (link) {
                if (!urlRegex) {
                    throw new Error('Link is not valid HTTP/HTTPS Address.');
                } else {
                    /** @todo I need a regex for making the following URL in the URL parameter */
                    got(`http://saucenao.com/search.php?output_type=${this.outputType}&numres=${this.numRes}&api_key=${this.key}&url=${encodeURIComponent(link)}`).then(res => {
                        resolve(res.body);
                    }).catch(reject);
                }
            }
        });
    }
}

module.exports = SauceHandler;<commit_msg>changed some thing in handler<commit_after>/* SauceNao query Handler
 *
 *
 * Contributed by Capuccino
 */

const got = require('got');
const urlRegex = str => /(http(s)?:\/\/)?(www\.)?[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi.test(str);

/**
 * Query Handler for SauceNao-related requests.
 */

class SauceHandler {
    /**
     * @param {String} key your API Key for Saucenao
     * @param {Number} outputType type of output you want the API to show. Default is value 2 (JSON Response)
     * @param {Number} numRes amount of responses you want returned from the API. Default is 5 Responses.
     * @see {link} https://saucenao.com/user.php?page=search-api
     */
    constructor({key, numRes, outputType}) {
        if (!key) throw new TypeError('NO API Key provided!');
        this.key = key,
        this.outputType = outputType || 2,
        this.numRes = numRes || 5;
    }
    /**
     * Gets the source and outputs it in your preffered output type
     * @param {String} path filepath for the image you want to get the source from (Not Implemented yet).
     * @param {String} link web address for the source, must be a valid HTTP/HTTPS address.
     * @returns {Promise} and JSON output.
     * @example client.getSauce(path/link).then(res => { console.log(res); });
     */
    getSauce(path, link) {
        return new Promise((resolve, reject) => {
            if (!path === typeof path) {
                throw new TypeError('path is not string');
            } else if (path) {
                /** @todo Finalize this. We need to input file as a stream then convert to MIMEType */
                throw new Error('This is not implemented yet!');
                /* got(`http://saucenao.com/search.php?output_type=${this.outputType}&numres=${this.numRes}&api_key=${this.key}`, {}).then(res => {
                 });*/
            } else if (link) {
                if (!urlRegex) {
                    throw new TypeError('Link is not valid HTTP/HTTPS Address.');
                } else {
                    /** @todo I need a regex for making the following URL in the URL parameter */
                    got(`http://saucenao.com/search.php?output_type=${this.outputType}&numres=${this.numRes}&api_key=${this.key}&url=${encodeURIComponent(link)}`).then(res => {
                        resolve(res.body);
                    }).catch(reject);
                }
            }
        });
    }
}

module.exports = SauceHandler;<|endoftext|>


<commit_before>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var xPosition = 0;
  var yPosition = 0;
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }
    socket = new WebSocket("ws:25.117.117.69:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
    var regex = /pos:(\d+),(\d+)/;
    var pos = regex.exec(message.data);
    xPosition = pos[1];
    yPosition = pos[2];
  //console.log(xPosition + ", " + yPosition);
} else if (message.data.indexOf("dat") == 0){
  var splitted = message.data.split(":")[1].split(",");
  enemies = [];
  for (i = 0; i < splitted.length; i++){
    enemies[i] = splitted[i];
  }
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var $canvas = $('#GameCanvas');
$canvas.attr('width', $(window).innerWidth());
$canvas.attr('height', $(window).innerHeight());
var canvas = $canvas[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
  context.fillStyle = '#fe57a1';
  context.fillRect(xPosition, yPosition, 30, 50);
  context.font = "15px Arial";
  context.fillText(user,(xPosition - (context.measureText(user).width / 2)) + 15,yPosition - 5);
  context.fillStyle = '#a157fe';
  for (i = 0; i < enemies.length; i += 2){
    context.fillRect(enemies[i], enemies[i+1], 30, 50);
  }
  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();<commit_msg>changed ip<commit_after>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var xPosition = 0;
  var yPosition = 0;
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }
    socket = new WebSocket("ws:167.88.120.57:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
    var regex = /pos:(\d+),(\d+)/;
    var pos = regex.exec(message.data);
    xPosition = pos[1];
    yPosition = pos[2];
  //console.log(xPosition + ", " + yPosition);
} else if (message.data.indexOf("dat") == 0){
  var splitted = message.data.split(":")[1].split(",");
  enemies = [];
  for (i = 0; i < splitted.length; i++){
    enemies[i] = splitted[i];
  }
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var $canvas = $('#GameCanvas');
$canvas.attr('width', $(window).innerWidth());
$canvas.attr('height', $(window).innerHeight());
var canvas = $canvas[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
  context.fillStyle = '#fe57a1';
  context.fillRect(xPosition, yPosition, 30, 50);
  context.font = "15px Arial";
  context.fillText(user,(xPosition - (context.measureText(user).width / 2)) + 15,yPosition - 5);
  context.fillStyle = '#a157fe';
  for (i = 0; i < enemies.length; i += 2){
    context.fillRect(enemies[i], enemies[i+1], 30, 50);
  }
  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();<|endoftext|>


<commit_before>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var xPosition = 0;
  var yPosition = 0;
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }
    socket = new WebSocket("ws:167.88.120.57:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
    var regex = /pos:(\d+),(\d+)/;
    var pos = regex.exec(message.data);
    xPosition = pos[1];
    yPosition = pos[2];
  //console.log(xPosition + ", " + yPosition);
} else if (message.data.indexOf("dat") == 0){
  var splitted = message.data.split(":")[1].split(",");
  enemies = [];
  for (i = 0; i < splitted.length; i++){
    enemies[i] = splitted[i];
  }
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var $canvas = $('#GameCanvas');
$canvas.attr('width', $(window).innerWidth());
$canvas.attr('height', $(window).innerHeight());
var canvas = $canvas[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
  context.fillStyle = '#fe57a1';
  context.fillRect(xPosition, yPosition, 30, 50);
  context.font = "15px Arial";
  context.fillText(user,(xPosition - (context.measureText(user).width / 2)) + 15,yPosition - 5);
  context.fillStyle = '#a157fe';
  for (i = 0; i < enemies.length; i += 3){
    context.fillRect(enemies[i], enemies[i+1], 30, 50);
  context.fillText(enemies[i+2],(xPosition - (context.measureText(enemies[i+2]).width / 2)) + 15,yPosition - 5);
  }
  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();<commit_msg>text positions<commit_after>var JS_GAME = {};

JS_GAME.game = (function () {
  var isKeyDown = [];
  var context;
  var xPosition = 0;
  var yPosition = 0;
  var enemies = [];
  var frameLength = 1;// in milliseconds
  var socket;
  var connected = false;
  var user = "";
  var pass = "";

  function init() {
    user = document.getElementById("loginInfo").user.value;
    pass = document.getElementById("loginInfo").pass.value;
    if (user.indexOf(",") >= 0 || user.indexOf(":") >= 0){
      document.getElementById("loginInfo").user.value = "INVALID INPUT";
      return;
    }
    socket = new WebSocket("ws:167.88.120.57:38734");

    socket.onopen = function() {
      connected = true;
      socket.send("login:" + user + "," + pass);
    };

socket.onmessage = function(message) {
  if (message.data.indexOf("pos") == 0){
    var regex = /pos:(\d+),(\d+)/;
    var pos = regex.exec(message.data);
    xPosition = pos[1];
    yPosition = pos[2];
  //console.log(xPosition + ", " + yPosition);
} else if (message.data.indexOf("dat") == 0){
  var splitted = message.data.split(":")[1].split(",");
  enemies = [];
  for (i = 0; i < splitted.length; i++){
    enemies[i] = splitted[i];
  }
}
};

socket.onclose = function() {
  window.location.reload(false);
};

socket.onerror = function() {
};

document.body.innerHTML = "";
for (i = 0; i < 256; i++){
  isKeyDown[i] = false;
}
$('body').append('<canvas id="GameCanvas">');
var $canvas = $('#GameCanvas');
$canvas.attr('width', $(window).innerWidth());
$canvas.attr('height', $(window).innerHeight());
var canvas = $canvas[0];
window.addEventListener( "keydown", keyPress, false);
window.addEventListener( "keyup", keyRelease, false);
context = canvas.getContext('2d');
gameLoop();
}

function gameLoop() {
  var keyData = "";
  for (i = 0; i < 256; i++){
    keyData += isKeyDown[i] ? 1 : 0;
  }
  try {
    socket.send("keys:" + keyData);
  } catch (err) {
  }
  context.clearRect(0, 0, document.body.clientWidth, document.body.clientHeight);
  context.fillStyle = '#fe57a1';
  context.fillRect(xPosition, yPosition, 30, 50);
  context.font = "15px Arial";
  context.fillText(user,(xPosition - (context.measureText(user).width / 2)) + 15,yPosition - 5);
  context.fillStyle = '#a157fe';
  for (i = 0; i < enemies.length; i += 3){
    context.fillRect(enemies[i], enemies[i+1], 30, 50);
  context.fillText(enemies[i+2],enemies[i] - (context.measureText(enemies[i+2]).width / 2)) + 15,enemies[i+1] - 5);
  }
  setTimeout(gameLoop, frameLength);
}

function keyPress(e){
  isKeyDown[e.keyCode] = true;
}
function keyRelease(e){
  isKeyDown[e.keyCode] = false;
}
function isKeyPressed(c){
  return isKeyDown[c.charCodeAt(0)];
}

return {
  init: init
};
})();<|endoftext|>


<commit_before>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define([
    "nbextensions/widgets/widgets/js/widget",
    "base/js/utils",
    "jquery",
    "bootstrap",
], function(widget, utils, $){

    var AccordionView = widget.DOMWidgetView.extend({
        initialize: function(){
            AccordionView.__super__.initialize.apply(this, arguments);

            this.containers = [];
            this.model_containers = {};
            this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
            this.listenTo(this.model, 'change:children', function(model, value) {
                this.children_views.update(value);
            }, this);
        },

        render: function(){
            /**
             * Called when view is rendered.
             */
            var guid = 'panel-group' + utils.uuid();
            this.$el
                .attr('id', guid)
                .addClass('panel-group');
            this.listenTo(this.model, 'change:selected_index', function(model, value, options) {
                this.update_selected_index(options);
            }, this);
            this.listenTo(this.model, 'change:_titles', function(model, value, options) {
                this.update_titles(options);
            }, this);
            this.on('displayed', function() {
                this.update_titles();
            }, this);
            this.children_views.update(this.model.get('children'));
        },

        /**
         * Update the contents of this view
         *
         * Called when the model is changed.  The model may have been
         * changed by another view or by a state update from the back-end.
         */
        update: function(options) {
            this.update_titles();
            this.update_selected_index(options);
            return TabView.__super__.update.apply(this);
        },

        update_titles: function() {
            /**
             * Set tab titles
             */
            var titles = this.model.get('_titles');
            var that = this;
            _.each(titles, function(title, page_index) {
                var accordian = that.containers[page_index];
                if (accordian !== undefined) {
                    accordian
                        .children('.panel-heading')
                        .find('.accordion-toggle')
                        .text(title);
                }
            });
        },

        update_selected_index: function(options) {
            /**
             * Only update the selection if the selection wasn't triggered
             * by the front-end.  It must be triggered by the back-end.
             */
            if (options === undefined || options.updated_view != this) {
                var old_index = this.model.previous('selected_index');
                var new_index = this.model.get('selected_index');
                this.containers[old_index].find('.panel-collapse').collapse('hide');
                if (0 <= new_index && new_index < this.containers.length) {
                    this.containers[new_index].find('.panel-collapse').collapse('show');
                }
            }
        },

        remove_child_view: function(view) {
            /**
             * Called when a child is removed from children list.
             * TODO: does this handle two different views of the same model as children?
             */
            var model = view.model;
            var accordion_group = this.model_containers[model.id];
            this.containers.splice(accordion_group.container_index, 1);
            delete this.model_containers[model.id];
            accordion_group.remove();
        },

        add_child_view: function(model) {
            /**
             * Called when a child is added to children list.
             */
            var index = this.containers.length;
            var uuid = utils.uuid();
            var accordion_group = $('<div />')
                .addClass('panel panel-default')
                .appendTo(this.$el);
            var accordion_heading = $('<div />')
                .addClass('panel-heading')
                .appendTo(accordion_group);
            var that = this;
            var accordion_toggle = $('<a />')
                .addClass('accordion-toggle')
                .attr('data-toggle', 'collapse')
                .attr('data-parent', '#' + this.$el.attr('id'))
                .attr('href', '#' + uuid)
                .click(function(evt){

                    // Calling model.set will trigger all of the other views of the
                    // model to update.
                    that.model.set("selected_index", index, {updated_view: that});
                    that.touch();
                 })
                .text('Page ' + index)
                .appendTo(accordion_heading);
            var accordion_body = $('<div />', {id: uuid})
                .addClass('panel-collapse collapse')
                .appendTo(accordion_group);
            var accordion_inner = $('<div />')
                .addClass('panel-body')
                .appendTo(accordion_body);
            var container_index = this.containers.push(accordion_group) - 1;
            accordion_group.container_index = container_index;
            this.model_containers[model.id] = accordion_group;

            var dummy = $('<div/>');
            accordion_inner.append(dummy);
            return this.create_child_view(model).then(function(view) {
                dummy.replaceWith(view.$el);
                that.update();
                that.update_titles();

                // Trigger the displayed event of the child view.
                that.displayed.then(function() {
                    view.trigger('displayed');
                });
                return view;
            }).catch(utils.reject("Couldn't add child view to box", true));
        },

        remove: function() {
            /**
             * We remove this widget before removing the children as an optimization
             * we want to remove the entire container from the DOM first before
             * removing each individual child separately.
             */
            AccordionView.__super__.remove.apply(this, arguments);
            this.children_views.remove();
        },
    });

    var TabView = widget.DOMWidgetView.extend({
        initialize: function() {
            /**
             * Public constructor.
             */
            TabView.__super__.initialize.apply(this, arguments);

            this.containers = [];
            this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
            this.listenTo(this.model, 'change:children', function(model, value) {
                this.children_views.update(value);
            }, this);
        },

        render: function(){
            /**
             * Called when view is rendered.
             */
            var uuid = 'tabs'+utils.uuid();
            this.$tabs = $('<div />', {id: uuid})
                .addClass('nav')
                .addClass('nav-tabs')
                .appendTo(this.$el);
            this.$tab_contents = $('<div />', {id: uuid + 'Content'})
                .addClass('tab-content')
                .appendTo(this.$el);
            this.children_views.update(this.model.get('children'));
        },

        update_attr: function(name, value) {
            /**
             * Set a css attr of the widget view.
             */
            if (['padding', 'margin', 'height', 'width'].indexOf(name) !== -1) {
                this.$el.css(name, value);
            } else {
                this.$tabs.css(name, value);
            }
        },

        remove_child_view: function(view) {
            /**
             * Called when a child is removed from children list.
             */
            this.containers.splice(view.parent_tab.tab_text_index, 1);
            view.parent_tab.remove();
            view.parent_container.remove();
            view.remove();
        },

        add_child_view: function(model) {
            /**
             * Called when a child is added to children list.
             */
            var index = this.containers.length;
            var uuid = utils.uuid();

            var that = this;
            var tab = $('<li />')
                .css('list-style-type', 'none')
                .appendTo(this.$tabs);

            var tab_text = $('<a />')
                .attr('href', '#' + uuid)
                .attr('data-toggle', 'tab')
                .text('Page ' + index)
                .appendTo(tab)
                .click(function (e) {

                    // Calling model.set will trigger all of the other views of the
                    // model to update.
                    that.model.set("selected_index", index, {updated_view: that});
                    that.touch();
                    that.select_page(index);
                });
            tab.tab_text_index = that.containers.push(tab_text) - 1;

            var dummy = $('<div />');
            var contents_div = $('<div />', {id: uuid})
                .addClass('tab-pane')
                .addClass('fade')
                .append(dummy)
                .appendTo(that.$tab_contents);

            this.update();
            return this.create_child_view(model).then(function(view) {
                dummy.replaceWith(view.$el);
                view.parent_tab = tab;
                view.parent_container = contents_div;

                // Trigger the displayed event of the child view.
                that.displayed.then(function() {
                    view.trigger('displayed');
                    that.update();
                });
                return view;
            }).catch(utils.reject("Couldn't add child view to box", true));
        },

        update: function(options) {
            /**
             * Update the contents of this view
             *
             * Called when the model is changed.  The model may have been
             * changed by another view or by a state update from the back-end.
             */
            this.update_titles();
            this.update_selected_index(options);
            return TabView.__super__.update.apply(this);
        },

        /**
         * Updates the tab page titles.
         */
        update_titles: function() {
            var titles = this.model.get('_titles');
            var that = this;
            _.each(titles, function(title, page_index) {
               var tab_text = that.containers[page_index];
                if (tab_text !== undefined) {
                    tab_text.text(title);
                }
            });
        },

        /**
         * Updates the tab page titles.
         */
        update_selected_index: function(options) {
            if (options === undefined || options.updated_view != this) {
                var selected_index = this.model.get('selected_index');
                if (0 <= selected_index && selected_index < this.containers.length) {
                    this.select_page(selected_index);
                }
            }
        },

        select_page: function(index) {
            /**
             * Select a page.
             */
            this.$tabs.find('li')
                .removeClass('active');
            this.containers[index].tab('show');
        },

        remove: function() {
            /**
             * We remove this widget before removing the children as an optimization
             * we want to remove the entire container from the DOM first before
             * removing each individual child separately.
             */
            TabView.__super__.remove.apply(this, arguments);
            this.children_views.remove();
        },
    });

    return {
        'AccordionView': AccordionView,
        'TabView': TabView,
    };
});
<commit_msg>Only collapse/hide immediate children of accordion panel<commit_after>// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.

define([
    "nbextensions/widgets/widgets/js/widget",
    "base/js/utils",
    "jquery",
    "bootstrap",
], function(widget, utils, $){

    var AccordionView = widget.DOMWidgetView.extend({
        initialize: function(){
            AccordionView.__super__.initialize.apply(this, arguments);

            this.containers = [];
            this.model_containers = {};
            this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
            this.listenTo(this.model, 'change:children', function(model, value) {
                this.children_views.update(value);
            }, this);
        },

        render: function(){
            /**
             * Called when view is rendered.
             */
            var guid = 'panel-group' + utils.uuid();
            this.$el
                .attr('id', guid)
                .addClass('panel-group');
            this.listenTo(this.model, 'change:selected_index', function(model, value, options) {
                this.update_selected_index(options);
            }, this);
            this.listenTo(this.model, 'change:_titles', function(model, value, options) {
                this.update_titles(options);
            }, this);
            this.on('displayed', function() {
                this.update_titles();
            }, this);
            this.children_views.update(this.model.get('children'));
        },

        /**
         * Update the contents of this view
         *
         * Called when the model is changed.  The model may have been
         * changed by another view or by a state update from the back-end.
         */
        update: function(options) {
            this.update_titles();
            this.update_selected_index(options);
            return TabView.__super__.update.apply(this);
        },

        update_titles: function() {
            /**
             * Set tab titles
             */
            var titles = this.model.get('_titles');
            var that = this;
            _.each(titles, function(title, page_index) {
                var accordian = that.containers[page_index];
                if (accordian !== undefined) {
                    accordian
                        .children('.panel-heading')
                        .find('.accordion-toggle')
                        .text(title);
                }
            });
        },

        update_selected_index: function(options) {
            /**
             * Only update the selection if the selection wasn't triggered
             * by the front-end.  It must be triggered by the back-end.
             */
            if (options === undefined || options.updated_view != this) {
                var old_index = this.model.previous('selected_index');
                var new_index = this.model.get('selected_index');
                this.containers[old_index].children('.panel-collapse').collapse('hide');
                if (0 <= new_index && new_index < this.containers.length) {
                    this.containers[new_index].children('.panel-collapse').collapse('show');
                }
            }
        },

        remove_child_view: function(view) {
            /**
             * Called when a child is removed from children list.
             * TODO: does this handle two different views of the same model as children?
             */
            var model = view.model;
            var accordion_group = this.model_containers[model.id];
            this.containers.splice(accordion_group.container_index, 1);
            delete this.model_containers[model.id];
            accordion_group.remove();
        },

        add_child_view: function(model) {
            /**
             * Called when a child is added to children list.
             */
            var index = this.containers.length;
            var uuid = utils.uuid();
            var accordion_group = $('<div />')
                .addClass('panel panel-default')
                .appendTo(this.$el);
            var accordion_heading = $('<div />')
                .addClass('panel-heading')
                .appendTo(accordion_group);
            var that = this;
            var accordion_toggle = $('<a />')
                .addClass('accordion-toggle')
                .attr('data-toggle', 'collapse')
                .attr('data-parent', '#' + this.$el.attr('id'))
                .attr('href', '#' + uuid)
                .click(function(evt){

                    // Calling model.set will trigger all of the other views of the
                    // model to update.
                    that.model.set("selected_index", index, {updated_view: that});
                    that.touch();
                 })
                .text('Page ' + index)
                .appendTo(accordion_heading);
            var accordion_body = $('<div />', {id: uuid})
                .addClass('panel-collapse collapse')
                .appendTo(accordion_group);
            var accordion_inner = $('<div />')
                .addClass('panel-body')
                .appendTo(accordion_body);
            var container_index = this.containers.push(accordion_group) - 1;
            accordion_group.container_index = container_index;
            this.model_containers[model.id] = accordion_group;

            var dummy = $('<div/>');
            accordion_inner.append(dummy);
            return this.create_child_view(model).then(function(view) {
                dummy.replaceWith(view.$el);
                that.update();
                that.update_titles();

                // Trigger the displayed event of the child view.
                that.displayed.then(function() {
                    view.trigger('displayed');
                });
                return view;
            }).catch(utils.reject("Couldn't add child view to box", true));
        },

        remove: function() {
            /**
             * We remove this widget before removing the children as an optimization
             * we want to remove the entire container from the DOM first before
             * removing each individual child separately.
             */
            AccordionView.__super__.remove.apply(this, arguments);
            this.children_views.remove();
        },
    });

    var TabView = widget.DOMWidgetView.extend({
        initialize: function() {
            /**
             * Public constructor.
             */
            TabView.__super__.initialize.apply(this, arguments);

            this.containers = [];
            this.children_views = new widget.ViewList(this.add_child_view, this.remove_child_view, this);
            this.listenTo(this.model, 'change:children', function(model, value) {
                this.children_views.update(value);
            }, this);
        },

        render: function(){
            /**
             * Called when view is rendered.
             */
            var uuid = 'tabs'+utils.uuid();
            this.$tabs = $('<div />', {id: uuid})
                .addClass('nav')
                .addClass('nav-tabs')
                .appendTo(this.$el);
            this.$tab_contents = $('<div />', {id: uuid + 'Content'})
                .addClass('tab-content')
                .appendTo(this.$el);
            this.children_views.update(this.model.get('children'));
        },

        update_attr: function(name, value) {
            /**
             * Set a css attr of the widget view.
             */
            if (['padding', 'margin', 'height', 'width'].indexOf(name) !== -1) {
                this.$el.css(name, value);
            } else {
                this.$tabs.css(name, value);
            }
        },

        remove_child_view: function(view) {
            /**
             * Called when a child is removed from children list.
             */
            this.containers.splice(view.parent_tab.tab_text_index, 1);
            view.parent_tab.remove();
            view.parent_container.remove();
            view.remove();
        },

        add_child_view: function(model) {
            /**
             * Called when a child is added to children list.
             */
            var index = this.containers.length;
            var uuid = utils.uuid();

            var that = this;
            var tab = $('<li />')
                .css('list-style-type', 'none')
                .appendTo(this.$tabs);

            var tab_text = $('<a />')
                .attr('href', '#' + uuid)
                .attr('data-toggle', 'tab')
                .text('Page ' + index)
                .appendTo(tab)
                .click(function (e) {

                    // Calling model.set will trigger all of the other views of the
                    // model to update.
                    that.model.set("selected_index", index, {updated_view: that});
                    that.touch();
                    that.select_page(index);
                });
            tab.tab_text_index = that.containers.push(tab_text) - 1;

            var dummy = $('<div />');
            var contents_div = $('<div />', {id: uuid})
                .addClass('tab-pane')
                .addClass('fade')
                .append(dummy)
                .appendTo(that.$tab_contents);

            this.update();
            return this.create_child_view(model).then(function(view) {
                dummy.replaceWith(view.$el);
                view.parent_tab = tab;
                view.parent_container = contents_div;

                // Trigger the displayed event of the child view.
                that.displayed.then(function() {
                    view.trigger('displayed');
                    that.update();
                });
                return view;
            }).catch(utils.reject("Couldn't add child view to box", true));
        },

        update: function(options) {
            /**
             * Update the contents of this view
             *
             * Called when the model is changed.  The model may have been
             * changed by another view or by a state update from the back-end.
             */
            this.update_titles();
            this.update_selected_index(options);
            return TabView.__super__.update.apply(this);
        },

        /**
         * Updates the tab page titles.
         */
        update_titles: function() {
            var titles = this.model.get('_titles');
            var that = this;
            _.each(titles, function(title, page_index) {
               var tab_text = that.containers[page_index];
                if (tab_text !== undefined) {
                    tab_text.text(title);
                }
            });
        },

        /**
         * Updates the tab page titles.
         */
        update_selected_index: function(options) {
            if (options === undefined || options.updated_view != this) {
                var selected_index = this.model.get('selected_index');
                if (0 <= selected_index && selected_index < this.containers.length) {
                    this.select_page(selected_index);
                }
            }
        },

        select_page: function(index) {
            /**
             * Select a page.
             */
            this.$tabs.find('li')
                .removeClass('active');
            this.containers[index].tab('show');
        },

        remove: function() {
            /**
             * We remove this widget before removing the children as an optimization
             * we want to remove the entire container from the DOM first before
             * removing each individual child separately.
             */
            TabView.__super__.remove.apply(this, arguments);
            this.children_views.remove();
        },
    });

    return {
        'AccordionView': AccordionView,
        'TabView': TabView,
    };
});
<|endoftext|>


<commit_before>module.exports = {
    options: {
        spawn: false,
        livereload: false
    },
    javascript_vendors: {
        files: [ '<%= paths.js.src %>/vendors/**/*.js', '<%= paths.grunt %>/config/concat.js' ],
        tasks: [ 'concat', 'notify:javascript' ]
    },
    sass: {
        files: [ '<%= paths.css.src %>/**/*.scss' ],
        tasks: [ 'sass', 'postcss', 'notify:sass' ]
    },
    svg: {
        files: [ '<%= paths.img.dist %>/**/*.svg' ],
        tasks: [ 'svg_sprite', 'notify:svg' ]
    },
    tasks: {
        options: {
            reload: true
        },
        files: [
            'Gruntfile.js',
            '<%= paths.grunt %>/**/*'
        ]
    }
};
<commit_msg>Fix watch svg for sprite<commit_after>module.exports = {
    options: {
        spawn: false,
        livereload: false
    },
    javascript_vendors: {
        files: [ '<%= paths.js.src %>/vendors/**/*.js', '<%= paths.grunt %>/config/concat.js' ],
        tasks: [ 'concat', 'notify:javascript' ]
    },
    sass: {
        files: [ '<%= paths.css.src %>/**/*.scss' ],
        tasks: [ 'sass', 'postcss', 'notify:sass' ]
    },
    svg: {
        files: [ '<%= paths.img.src %>/**/*.svg' ],
        tasks: [ 'svg_sprite', 'notify:svg' ]
    },
    tasks: {
        options: {
            reload: true
        },
        files: [
            'Gruntfile.js',
            '<%= paths.grunt %>/**/*'
        ]
    }
};
<|endoftext|>


<commit_before>var $ = require('jquery');
var cdb = require('cartodb.js');
var HistogramChartView = require('../histogram/chart');

/**
 * Time-series histogram view.
 */
module.exports = cdb.core.View.extend({
  className: 'CDB-Widget-content CDB-Widget-content--timeSeries',

  defaults: {
    mobileThreshold: 960, // px; should match CSS media-query
    histogramChartHeight: 48 + // inline bars height
      4 + // bottom margin
      16 + // labels
      4, // margins
    histogramChartMobileHeight: 20 + // inline bars height (no bottom labels)
      4 // margins
  },

  initialize: function () {
    this.filter = this.options.filter;

    this.model.bind('change:data', this._onChangeData, this);
  },

  render: function () {
    this.clearSubViews();
    this._createHistogramView();
    return this;
  },

  _createHistogramView: function () {
    this._chartView = new HistogramChartView({
      type: 'time',
      animationSpeed: 100,
      margin: {
        top: 4,
        right: 4,
        bottom: 4,
        left: 4
      },
      hasHandles: true,
      animationBarDelay: function (d, i) {
        return (i * 3);
      },
      height: this.defaults.histogramChartHeight,
      data: this.model.getData()
    });
    this.addView(this._chartView);
    this.$el.append(this._chartView.render().el);
    this._chartView.show();

    this._chartView.bind('on_brush_end', this._onBrushEnd, this);
    this._chartView.model.bind('change:width', this._onChangeChartWidth, this);
    this.add_related_model(this._chartView.model);
  },

  _onChangeData: function () {
    if (this._chartView) {
      this._chartView.replaceData(this.model.getData());
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this.model.getData();
    this.filter.setRange(
      data[loBarIndex].start,
      data[hiBarIndex - 1].end
    );
  },

  _onChangeChartWidth: function () {
    var isMobileSize = $(window).width() < this.defaults.mobileThreshold;

    this._chartView.toggleLabels(!isMobileSize);

    var height = isMobileSize
      ? this.defaults.histogramChartMobileHeight
      : this.defaults.histogramChartHeight;
    this._chartView.model.set('height', height);
  }

});
<commit_msg>fixes range filter<commit_after>var $ = require('jquery');
var cdb = require('cartodb.js');
var HistogramChartView = require('../histogram/chart');

/**
 * Time-series histogram view.
 */
module.exports = cdb.core.View.extend({
  className: 'CDB-Widget-content CDB-Widget-content--timeSeries',

  defaults: {
    mobileThreshold: 960, // px; should match CSS media-query
    histogramChartHeight: 48 + // inline bars height
      4 + // bottom margin
      16 + // labels
      4, // margins
    histogramChartMobileHeight: 20 + // inline bars height (no bottom labels)
      4 // margins
  },

  initialize: function () {
    this._rangeFilter = this.options.rangeFilter;

    this.model.bind('change:data', this._onChangeData, this);
  },

  render: function () {
    this.clearSubViews();
    this._createHistogramView();
    return this;
  },

  _createHistogramView: function () {
    this._chartView = new HistogramChartView({
      type: 'time',
      animationSpeed: 100,
      margin: {
        top: 4,
        right: 4,
        bottom: 4,
        left: 4
      },
      hasHandles: true,
      animationBarDelay: function (d, i) {
        return (i * 3);
      },
      height: this.defaults.histogramChartHeight,
      data: this.model.getData()
    });
    this.addView(this._chartView);
    this.$el.append(this._chartView.render().el);
    this._chartView.show();

    this._chartView.bind('on_brush_end', this._onBrushEnd, this);
    this._chartView.model.bind('change:width', this._onChangeChartWidth, this);
    this.add_related_model(this._chartView.model);
  },

  _onChangeData: function () {
    if (this._chartView) {
      this._chartView.replaceData(this.model.getData());
    }
  },

  _onBrushEnd: function (loBarIndex, hiBarIndex) {
    var data = this.model.getData();
    this._rangeFilter.setRange(
       data[loBarIndex].start,
       data[hiBarIndex - 1].end
    );
  },

  _onChangeChartWidth: function () {
    var isMobileSize = $(window).width() < this.defaults.mobileThreshold;

    this._chartView.toggleLabels(!isMobileSize);

    var height = isMobileSize
      ? this.defaults.histogramChartMobileHeight
      : this.defaults.histogramChartHeight;
    this._chartView.model.set('height', height);
  }

});
<|endoftext|>


<commit_before>"use strict";
var db = require('../neo4j/neo-connection.js');

/* /claims/:claimid
 * returns:
 *
 *  - the claim who's ID has been passed
 *  - the arguments that link to it
 *  - the claims that make up those arguments
 */

/*
var flatQuery = `OPTIONAL MATCH p = ((subClaim:Claim)-[subLink]->(argument:ArgGroup)-[argLink]->(claim:Claim))
                    WHERE ID(claim) = ${req.params.claimid}
                    RETURN
                        {id: id(claim), body: claim.body, state: claim.state, type: "claim"} AS claim,
                        COLLECT(DISTINCT {id: ID(argument), state: argument.state, type: "argument"}) AS arguments,
                        COLLECT(DISTINCT {id: ID(argLink), type: argLink.type, source: ID(startNode(argLink)), target: ID(endNode(argLink))}) AS argLinks,
                        COLLECT(DISTINCT {id: ID(subClaim), body: subClaim.body, state: subClaim.state, type: "claim"}) AS subClaims,
                        COLLECT(DISTINCT {id: ID(subLink), type: subLink.type, source: ID(startNode(subLink)), target: ID(endNode(subLink))}) AS subLinks,
                        COLLECT(argLink) AS argLinkCheck
                    LIMIT 100`;
*/

var nestedReturn = {
    claim: {
        id:1, body: "the focus claim body"
    },
    arguments: [
        {
            id: 2,
            subClaims: [
                { id: 3, body: "a sub claim" }
            ]
        }
    ],
    argLinks: [
        { id: 99, source: 1, target: 2 }
    ],
    subLinks: [
        { id: 98, source: 3, target: 3 }
    ]
}
module.exports = function(req, res){

    try {
        db.cypher({
            query: `OPTIONAL MATCH p = ((subClaim:Claim)-[subLink]->(argument:ArgGroup)-[argLink]->(claim:Claim))
                    WHERE ID(claim) = ${req.params.claimid}
                    RETURN
                        {id: id(claim), body: claim.body, state: claim.state, type: "claim"} AS claim,
                        COLLECT(DISTINCT {id: ID(argument), state: argument.state, type: "argument"}) AS arguments,
                        COLLECT(DISTINCT {id: ID(argLink), type: argLink.type, source: ID(startNode(argLink)), target: ID(endNode(argLink))}) AS argLinks,
                        COLLECT(DISTINCT {id: ID(subClaim), body: subClaim.body, state: subClaim.state, type: "claim"}) AS subClaims,
                        COLLECT(DISTINCT {id: ID(subLink), type: subLink.type, source: ID(startNode(subLink)), target: ID(endNode(subLink))}) AS subLinks,
                        COLLECT(argLink) AS argLinkCheck
                    LIMIT 100`
        }, function (err, results) {
            if (err) throw err;

            if (!results) {
                console.log('No claims found.');
                res.json({ error: 'No claims found' });
            } else {

                if (results.length == 0){
                    res.json({
                        meta: 'No claims match ' + req.params.claimid,
                        data: {}
                    });
                }

                if (results.length > 1){
                    //should only return one claim when getting by id... something's wrong with the data (scream!)

                }

                res.json({
                    meta: 'No meta yet',
                    data: results[0]
                });
            }
        });

    }
    catch(err){
        console.log('error happened - meep moop');
        res.json({
            errors: JSON.stringify(err),
        });
    }
}<commit_msg>return empty arrays when claim has no arguments<commit_after>"use strict";
var db = require('../neo4j/neo-connection.js');

/* /claims/:claimid
 * returns:
 *
 *  - the claim who's ID has been passed
 *  - the arguments that link to it
 *  - the claims that make up those arguments
 */

module.exports = function(req, res){

    try {
        db.cypher({
            query: `MATCH (claim:Claim)
                    WHERE ID(claim) = ${req.params.claimid}
                    OPTIONAL MATCH p = ((subClaim:Claim)-[subLink]->(argument:ArgGroup)-[argLink]->(claim))
                    RETURN
                        {id: id(claim), body: claim.body, state: claim.state, type: "claim"} AS claim,
                        CASE WHEN ID(argument) IS NULL THEN [] ELSE COLLECT(DISTINCT {id: ID(argument), state: argument.state, type: "argument"}) END AS arguments,
                        CASE WHEN ID(argument) IS NULL THEN [] ELSE COLLECT(DISTINCT {id: ID(argLink), type: argLink.type, source: ID(startNode(argLink)), target: ID(endNode(argLink))}) END AS argLinks,
                        CASE WHEN ID(argument) IS NULL THEN [] ELSE COLLECT(DISTINCT {id: ID(subClaim), body: subClaim.body, state: subClaim.state, type: "claim"}) END AS subClaims,
                        CASE WHEN ID(argument) IS NULL THEN [] ELSE COLLECT(DISTINCT {id: ID(subLink), type: subLink.type, source: ID(startNode(subLink)), target: ID(endNode(subLink))}) END AS subLinks
                    LIMIT 100`
        }, function (err, results) {
            if (err) throw err;

            if (!results) {
                console.log('No claims found.');
                res.json({ error: 'No claims found' });
            } else {
                if (results.length == 0){

                    res.json({
                        meta: 'No claims match ' + req.params.claimid,
                        data: {}
                    });
                    return;
                }

                if (results.length > 1){
                    //should only return one claim when getting by id... something's wrong with the data (scream!)

                }

                res.json({
                    meta: 'No meta yet',
                    data: results[0]
                });
            }
        });

    }
    catch(err){
        console.log('error happened - meep moop');
        res.json({
            errors: JSON.stringify(err),
        });
    }
}<|endoftext|>


<commit_before>(function() {
  var module = angular.module('loom_statistics_service', []);

  //var http_ = null;
  var service_ = null;
  //var q_ = null;

  module.provider('statisticsService', function() {
    this.$get = function($q, $http) {
      http_ = $http;
      service_ = this;
      q_ = $q;
      return this;
    };

    this.getBarData = function(layer, field) {
      return [
        {name: 'Station 1', score: 98},
        {name: 'Station 2', score: 96},
        {name: 'Station 3', score: 75},
        {name: 'Station 4', score: 48}
      ];
    };

    this.getSummaryStatistics = function(layer, field) {
      return {
        count: 1462,
        unqiueValues: 1091,
        min: 36.618,
        max: 86.2747,
        range: 49.6567,
        sum: 106165,
        mean: 72.6165,
        median: 74.2404
      };
    };

    this.getStatistics = function(layer, field) {
      var deferred = q_.defer();

      var statistics = {};
      statistics.field = field;
      statistics.fieldname = field.name;
      statistics.barData = service_.getBarData(layer, field);
      statistics.summaryStatistics = service_.getSummaryStatistics(layer, field);

      deferred.resolve(statistics);
      return deferred.promise;
    };

  });
}());

<commit_msg>Update statistics data structures.<commit_after>(function() {
  var module = angular.module('loom_statistics_service', []);

  //var http_ = null;
  var service_ = null;
  //var q_ = null;

  module.provider('statisticsService', function() {
    this.$get = function($q, $http) {
      http_ = $http;
      service_ = this;
      q_ = $q;
      return this;
    };

    this.getBarData = function(layer, field) {
      return [
        {bin: 'Station 1', value: 98},
       {bin: 'Station 2', value: 96},
        {bin: 'Station 3', value: 75},
        {bin: 'Station 4', value: 48}
      ];
    };

    this.getSummaryStatistics = function(layer, field) {
      number = {
        type: 'number',
        populatedCount: 1462,
        totalCount: 2043,
        unqiueValues: {
          '10.5': 23
        },
        min: 36.618,
        max: 86.2747,
        range: 49.6567,
        sum: 106165,
        mean: 72.6165,
        median: 74.2404,
        stdDev: 15.324,
        coefficient: 0.0123
      };

      strStats = {
        type: 'string',
        count: 13,
        uniqueValueCounts: {
          'In Progress': 12,
          'Finalized': 1100
        }
      };

      dateStats = {
        type: 'date',
        count: 13,
        uniqueValueCounts: {
          '12321323434': 11,
          '12321323423': 23
        },
        min: 12321323423,
        max: 12321323434,
        range: 49.6567,
        // sum will likely not make sense in date/time/datetime cases
        sum: 106165,
        // delta in ms
        mean: 72.6165,
        median: 74.2404,
        stdDev: 15.324,
        coefficient: 0.0123
      };

      return number;

    };

    this.getStatistics = function(layer, field) {
      var deferred = q_.defer();

      var statistics = {};
      statistics.field = field;
      statistics.fieldname = field.name;
      statistics.barData = service_.getBarData(layer, field);
      statistics.summaryStatistics = service_.getSummaryStatistics(layer, field);

      deferred.resolve(statistics);
      return deferred.promise;
    };

  });
}());

<|endoftext|>


<commit_before>'use strict';

exports.type = 'full';

exports.active = true;

exports.description =
  'minifies styles and removes unused styles based on usage data';

exports.params = {
  // ... CSSO options goes here

  // additional
  usage: {
    force: false, // force to use usage data even if it unsafe (document contains <script> or on* attributes)
    ids: true,
    classes: true,
    tags: true,
  },
};

var csso = require('csso');

/**
 * Minifies styles (<style> element + style attribute) using CSSO
 *
 * @author strarsis <strarsis@gmail.com>
 */
exports.fn = function (ast, options) {
  options = options || {};

  var minifyOptionsForStylesheet = cloneObject(options);
  var minifyOptionsForAttribute = cloneObject(options);
  var elems = findStyleElems(ast);

  minifyOptionsForStylesheet.usage = collectUsageData(ast, options);
  minifyOptionsForAttribute.usage = null;

  elems.forEach(function (elem) {
    if (elem.isElem('style')) {
      if (elem.content[0].type === 'text' || elem.content[0].type === 'cdata') {
        const styleCss = elem.content[0].value;
        const minified = csso.minify(styleCss, minifyOptionsForStylesheet).css;
        // TODO figure out if this check is necessary
        if (styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0) {
          elem.content[0].type = 'cdata';
          elem.content[0].value = minified;
        } else {
          elem.content[0].type = 'text';
          elem.content[0].value = minified;
        }
      }
    } else {
      // style attribute
      var elemStyle = elem.attr('style').value;

      elem.attr('style').value = csso.minifyBlock(
        elemStyle,
        minifyOptionsForAttribute
      ).css;
    }
  });

  return ast;
};

function cloneObject(obj) {
  return { ...obj };
}

function findStyleElems(ast) {
  function walk(items, styles) {
    for (var i = 0; i < items.content.length; i++) {
      var item = items.content[i];

      // go deeper
      if (item.content) {
        walk(item, styles);
      }

      if (item.isElem('style') && !item.isEmpty()) {
        styles.push(item);
      } else if (item.isElem() && item.hasAttr('style')) {
        styles.push(item);
      }
    }

    return styles;
  }

  return walk(ast, []);
}

function shouldFilter(options, name) {
  if ('usage' in options === false) {
    return true;
  }

  if (options.usage && name in options.usage === false) {
    return true;
  }

  return Boolean(options.usage && options.usage[name]);
}

function collectUsageData(ast, options) {
  function walk(items, usageData) {
    for (var i = 0; i < items.content.length; i++) {
      var item = items.content[i];

      // go deeper
      if (item.content) {
        walk(item, usageData);
      }

      if (item.isElem('script')) {
        safe = false;
      }

      if (item.isElem()) {
        usageData.tags[item.elem] = true;

        if (item.hasAttr('id')) {
          usageData.ids[item.attr('id').value] = true;
        }

        if (item.hasAttr('class')) {
          item
            .attr('class')
            .value.replace(/^\s+|\s+$/g, '')
            .split(/\s+/)
            .forEach(function (className) {
              usageData.classes[className] = true;
            });
        }

        if (
          item.attrs &&
          Object.keys(item.attrs).some(function (name) {
            return /^on/i.test(name);
          })
        ) {
          safe = false;
        }
      }
    }

    return usageData;
  }

  var safe = true;
  var usageData = {};
  var hasData = false;
  var rawData = walk(ast, {
    ids: Object.create(null),
    classes: Object.create(null),
    tags: Object.create(null),
  });

  if (!safe && options.usage && options.usage.force) {
    safe = true;
  }

  for (const [key, data] of Object.entries(rawData)) {
    if (shouldFilter(options, key)) {
      usageData[key] = Object.keys(data);
      hasData = true;
    }
  }

  return safe && hasData ? usageData : null;
}
<commit_msg>Clarify condition and add todo with new plugin<commit_after>'use strict';

exports.type = 'full';

exports.active = true;

exports.description =
  'minifies styles and removes unused styles based on usage data';

exports.params = {
  // ... CSSO options goes here

  // additional
  usage: {
    force: false, // force to use usage data even if it unsafe (document contains <script> or on* attributes)
    ids: true,
    classes: true,
    tags: true,
  },
};

var csso = require('csso');

/**
 * Minifies styles (<style> element + style attribute) using CSSO
 *
 * @author strarsis <strarsis@gmail.com>
 */
exports.fn = function (ast, options) {
  options = options || {};

  var minifyOptionsForStylesheet = cloneObject(options);
  var minifyOptionsForAttribute = cloneObject(options);
  var elems = findStyleElems(ast);

  minifyOptionsForStylesheet.usage = collectUsageData(ast, options);
  minifyOptionsForAttribute.usage = null;

  elems.forEach(function (elem) {
    if (elem.isElem('style')) {
      if (elem.content[0].type === 'text' || elem.content[0].type === 'cdata') {
        const styleCss = elem.content[0].value;
        const minified = csso.minify(styleCss, minifyOptionsForStylesheet).css;
        // preserve cdata if necessary
        // TODO split cdata -> text optimisation into separate plugin
        if (styleCss.indexOf('>') >= 0 || styleCss.indexOf('<') >= 0) {
          elem.content[0].type = 'cdata';
          elem.content[0].value = minified;
        } else {
          elem.content[0].type = 'text';
          elem.content[0].value = minified;
        }
      }
    } else {
      // style attribute
      var elemStyle = elem.attr('style').value;

      elem.attr('style').value = csso.minifyBlock(
        elemStyle,
        minifyOptionsForAttribute
      ).css;
    }
  });

  return ast;
};

function cloneObject(obj) {
  return { ...obj };
}

function findStyleElems(ast) {
  function walk(items, styles) {
    for (var i = 0; i < items.content.length; i++) {
      var item = items.content[i];

      // go deeper
      if (item.content) {
        walk(item, styles);
      }

      if (item.isElem('style') && !item.isEmpty()) {
        styles.push(item);
      } else if (item.isElem() && item.hasAttr('style')) {
        styles.push(item);
      }
    }

    return styles;
  }

  return walk(ast, []);
}

function shouldFilter(options, name) {
  if ('usage' in options === false) {
    return true;
  }

  if (options.usage && name in options.usage === false) {
    return true;
  }

  return Boolean(options.usage && options.usage[name]);
}

function collectUsageData(ast, options) {
  function walk(items, usageData) {
    for (var i = 0; i < items.content.length; i++) {
      var item = items.content[i];

      // go deeper
      if (item.content) {
        walk(item, usageData);
      }

      if (item.isElem('script')) {
        safe = false;
      }

      if (item.isElem()) {
        usageData.tags[item.elem] = true;

        if (item.hasAttr('id')) {
          usageData.ids[item.attr('id').value] = true;
        }

        if (item.hasAttr('class')) {
          item
            .attr('class')
            .value.replace(/^\s+|\s+$/g, '')
            .split(/\s+/)
            .forEach(function (className) {
              usageData.classes[className] = true;
            });
        }

        if (
          item.attrs &&
          Object.keys(item.attrs).some(function (name) {
            return /^on/i.test(name);
          })
        ) {
          safe = false;
        }
      }
    }

    return usageData;
  }

  var safe = true;
  var usageData = {};
  var hasData = false;
  var rawData = walk(ast, {
    ids: Object.create(null),
    classes: Object.create(null),
    tags: Object.create(null),
  });

  if (!safe && options.usage && options.usage.force) {
    safe = true;
  }

  for (const [key, data] of Object.entries(rawData)) {
    if (shouldFilter(options, key)) {
      usageData[key] = Object.keys(data);
      hasData = true;
    }
  }

  return safe && hasData ? usageData : null;
}
<|endoftext|>


<commit_before>// Call spamassassin via spamd

var sock = require('./line_socket');
var prettySize = require('./utils').prettySize;

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.hook_data_post = function (next, connection) {
    var plugin = this;
    var config = this.config.get('spamassassin.ini');

    setup_defaults(config);

    if (msg_too_big(config, connection, plugin)) return next();

    var username        = get_spamd_username(config, connection);
    var headers         = get_spamd_headers(connection, username);
    var socket          = get_spamd_socket(config, next, connection, plugin);
    socket.is_connected = false;
    var results_timeout = parseInt(config.main.results_timeout) || 300;

    socket.on('connect', function () {
        this.is_connected = true;
        // Reset timeout
        this.setTimeout(results_timeout * 1000);
        socket.write(headers.join("\r\n"));
        connection.transaction.message_stream.pipe(socket);
    });

    var spamd_response = { headers: {} };
    var state = 'line0';
    var last_header;

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.score = matches[2];
                    spamd_response.hits = matches[2];  // backwards compat
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No';
                }
            }
            else {
                state = 'headers';
            }
        }
        else if (state === 'headers') {
            var m;   // printable ASCII: [ -~]
            if (m = line.match(/^X-Spam-([ -~]+):(.*)/)) {
                // connection.logdebug(plugin, "header: " + line);
                last_header = m[1];
                spamd_response.headers[m[1]] = m[2];
                return;
            }
            var fold;
            if (last_header && (fold = line.match(/^(\s+.*)/))) {
                spamd_response.headers[last_header] += "\r\n" + fold[1];
                return;
            }
            last_header = '';
        }
    });

    socket.on('end', function () {
        // Abort if the transaction is gone
        if (!connection.transaction) return next();

        if (spamd_response.headers && spamd_response.headers.Tests) {
            spamd_response.tests = spamd_response.headers.Tests;
        }
        if (spamd_response.tests === undefined) {
            // strip the 'tests' from the X-Spam-Status header
            if (spamd_response.headers && spamd_response.headers.Status) {
                var tests = /tests=([^ ]+)/.exec(spamd_response.headers.Status.replace(/\r?\n\t/g,''));
                if (tests) { spamd_response.tests = tests[1]; }
            }
        }

        // do stuff with the results...
        connection.transaction.notes.spamassassin = spamd_response;

        plugin.fixup_old_headers(config.main.old_headers_action, connection.transaction);
        plugin.do_header_updates(connection, spamd_response, config);
        log_results(connection, plugin, spamd_response, config);

        var exceeds_err = score_too_high(config, connection, spamd_response);
        if (exceeds_err) return next(DENY, exceeds_err);

        munge_subject(connection, config, spamd_response.score);

        return next();
    });
};

exports.fixup_old_headers = function (action, transaction) {
    var plugin = this;
    var headers = transaction.notes.spamassassin.headers;

    switch (action) {
        case "keep":
            break;
        case "drop":
            for (var key in headers) {
                if (!key) continue;
                transaction.remove_header('X-Spam-' + key);
            }
            break;
        case "rename":
        default:
            for (var key in headers) {
                if (!key) continue;
                key = 'X-Spam-' + key;
                var old_val = transaction.header.get(key);
                transaction.remove_header(key);
                if (old_val) {
                    // plugin.logdebug(plugin, "header: " + key + ', ' + old_val);
                    transaction.add_header(key.replace(/^X-/, 'X-Old-'), old_val);
                }
            }
            break;
    }
};

function munge_subject(connection, config, score) {
    var munge = config.main.munge_subject_threshold;
    if (!munge) return;
    if (parseFloat(score) < parseFloat(munge)) return;

    var subj = connection.transaction.header.get('Subject');
    var subject_re = new RegExp('^' + config.main.subject_prefix);
    if (subject_re.test(subj)) return;    // prevent double munge

    connection.transaction.remove_header('Subject');
    connection.transaction.add_header('Subject', config.main.subject_prefix + " " + subj);
};

function setup_defaults(config) {
    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    ['reject_threshold', 'relay_reject_threshold',
     'munge_subject_threshold', 'max_size'].forEach(function (item) {
        if (!config.main[item]) return;
        config.main[item] = Number(config.main[item]);
    });
};

exports.do_header_updates = function (connection, spamd_response, config) {
    var plugin = this;
    if (spamd_response.flag === 'Yes') {
        // X-Spam-Flag is added by SpamAssassin
        connection.transaction.remove_header('precedence');
        connection.transaction.add_header('Precedence', 'junk');
    }

    var modern = config.main.modern_status_syntax;
    for (var key in spamd_response.headers) {
        if (!key || key === '' || key === undefined) continue;
        var val = spamd_response.headers[key];
        if (val === undefined) { val = ''; }

        if (key === 'Status' && !modern) {
            var legacy = spamd_response.headers[key].replace(/ score=/,' hits=');
            connection.transaction.add_header('X-Spam-Status', legacy);
            continue;
        }
        connection.transaction.add_header('X-Spam-' + key, val);
    }
};

function score_too_high(config, connection, spamd_response) {
    var score = spamd_response.score;
    if (connection.relaying) {
        var rmax = config.main.relay_reject_threshold;
        if (rmax && (score >= rmax)) {
            return "spam score exceeded relay threshold";
        }
    };

    var max = config.main.reject_threshold;
    if (max && (score >= max)) {
        return "spam score exceeded threshold";
    }

    return;
}

function get_spamd_username(config, connection) {

    var user = connection.transaction.notes.spamd_user;  // 1st priority
    if (user && user !== undefined) return user;

    if (!config.main.spamd_user) return 'default';   // when not defined
    user = config.main.spamd_user;

    // Enable per-user SA prefs
    if (user === 'first-recipient') {                // special cases
        return connection.transaction.rcpt_to[0].address();
    }
    if (user === 'all-recipients') {
        throw "Unimplemented";
        // TODO: pass the message through SA for each recipient. Then apply
        // the least strict result to the connection. That is useful when
        // one user blacklists a sender that another user wants to get mail
        // from. If this is something you care about, this is the spot.
    }
    return user;
}

function get_spamd_headers(connection, username) {
    // http://svn.apache.org/repos/asf/spamassassin/trunk/spamd/PROTOCOL
    var headers = [
        'HEADERS SPAMC/1.3',
        'User: ' + username,
        '',
        'X-Envelope-From: ' + connection.transaction.mail_from.address(),
        'X-Haraka-UUID: ' + connection.transaction.uuid,
    ];
    if (connection.relaying) {
        headers.push('X-Haraka-Relay: true');
    }
    return headers;
}

function get_spamd_socket(config, next, connection, plugin) {
    // TODO: support multiple spamd backends
    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {    // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect((hostport[1] || 783), hostport[0]);
    }

    var connect_timeout = parseInt(config.main.connect_timeout) || 30;
    socket.setTimeout(connect_timeout * 1000);

    socket.on('timeout', function () {
        if (!this.is_connected) {
            connection.logerror(plugin, 'connection timed out');
        }
        else {
            connection.logerror(plugin, 'timeout waiting for results');
        }
        socket.end();
        return next();
    });
    socket.on('error', function (err) {
        connection.logerror(plugin, 'connection failed: ' + err);
        // TODO: optionally DENYSOFT
        // TODO: add a transaction note
        return next();
    });
    return socket;
};

function msg_too_big(config, connection, plugin) {
    if (!config.main.max_size) return false;

    var size = connection.transaction.data_bytes;
    var max = config.main.max_size;
    if (size > max) {
        connection.loginfo(plugin, 'skipping, size ' + prettySize(size) + ' exceeds max: ' + prettySize(max));
        return true;
    }
    return false;
};

function log_results(connection, plugin, spamd_response, config) {
    connection.loginfo(plugin, "status=" + spamd_response.flag +
          ', score=' + spamd_response.score +
          ', required=' + spamd_response.reqd +
          ', reject=' + ((connection.relaying) ?
             (config.main.relay_reject_threshold || config.main.reject_threshold) :
             config.main.reject_threshold) +
          ', tests="' + spamd_response.tests + '"');
};
<commit_msg>spamassassin: check transaction before accessing<commit_after>// Call spamassassin via spamd

var sock = require('./line_socket');
var prettySize = require('./utils').prettySize;

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.hook_data_post = function (next, connection) {
    var plugin = this;
    var config = this.config.get('spamassassin.ini');

    setup_defaults(config);

    if (msg_too_big(config, connection, plugin)) return next();

    var username        = get_spamd_username(config, connection);
    var headers         = get_spamd_headers(connection, username);
    var socket          = get_spamd_socket(config, next, connection, plugin);
    socket.is_connected = false;
    var results_timeout = parseInt(config.main.results_timeout) || 300;

    socket.on('connect', function () {
        if (!connection.transaction) {
            socket.end();
            return next();
        }
        this.is_connected = true;
        // Reset timeout
        this.setTimeout(results_timeout * 1000);
        socket.write(headers.join("\r\n"));
        connection.transaction.message_stream.pipe(socket);
    });

    var spamd_response = { headers: {} };
    var state = 'line0';
    var last_header;

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.score = matches[2];
                    spamd_response.hits = matches[2];  // backwards compat
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No';
                }
            }
            else {
                state = 'headers';
            }
        }
        else if (state === 'headers') {
            var m;   // printable ASCII: [ -~]
            if (m = line.match(/^X-Spam-([ -~]+):(.*)/)) {
                // connection.logdebug(plugin, "header: " + line);
                last_header = m[1];
                spamd_response.headers[m[1]] = m[2];
                return;
            }
            var fold;
            if (last_header && (fold = line.match(/^(\s+.*)/))) {
                spamd_response.headers[last_header] += "\r\n" + fold[1];
                return;
            }
            last_header = '';
        }
    });

    socket.on('end', function () {
        // Abort if the transaction is gone
        if (!connection.transaction) return next();

        if (spamd_response.headers && spamd_response.headers.Tests) {
            spamd_response.tests = spamd_response.headers.Tests;
        }
        if (spamd_response.tests === undefined) {
            // strip the 'tests' from the X-Spam-Status header
            if (spamd_response.headers && spamd_response.headers.Status) {
                var tests = /tests=([^ ]+)/.exec(spamd_response.headers.Status.replace(/\r?\n\t/g,''));
                if (tests) { spamd_response.tests = tests[1]; }
            }
        }

        // do stuff with the results...
        connection.transaction.notes.spamassassin = spamd_response;

        plugin.fixup_old_headers(config.main.old_headers_action, connection.transaction);
        plugin.do_header_updates(connection, spamd_response, config);
        log_results(connection, plugin, spamd_response, config);

        var exceeds_err = score_too_high(config, connection, spamd_response);
        if (exceeds_err) return next(DENY, exceeds_err);

        munge_subject(connection, config, spamd_response.score);

        return next();
    });
};

exports.fixup_old_headers = function (action, transaction) {
    var plugin = this;
    var headers = transaction.notes.spamassassin.headers;

    switch (action) {
        case "keep":
            break;
        case "drop":
            for (var key in headers) {
                if (!key) continue;
                transaction.remove_header('X-Spam-' + key);
            }
            break;
        case "rename":
        default:
            for (var key in headers) {
                if (!key) continue;
                key = 'X-Spam-' + key;
                var old_val = transaction.header.get(key);
                transaction.remove_header(key);
                if (old_val) {
                    // plugin.logdebug(plugin, "header: " + key + ', ' + old_val);
                    transaction.add_header(key.replace(/^X-/, 'X-Old-'), old_val);
                }
            }
            break;
    }
};

function munge_subject(connection, config, score) {
    var munge = config.main.munge_subject_threshold;
    if (!munge) return;
    if (parseFloat(score) < parseFloat(munge)) return;

    var subj = connection.transaction.header.get('Subject');
    var subject_re = new RegExp('^' + config.main.subject_prefix);
    if (subject_re.test(subj)) return;    // prevent double munge

    connection.transaction.remove_header('Subject');
    connection.transaction.add_header('Subject', config.main.subject_prefix + " " + subj);
}

function setup_defaults(config) {
    for (var key in defaults) {
        config.main[key] = config.main[key] || defaults[key];
    }

    ['reject_threshold', 'relay_reject_threshold',
     'munge_subject_threshold', 'max_size'].forEach(function (item) {
        if (!config.main[item]) return;
        config.main[item] = Number(config.main[item]);
    });
}

exports.do_header_updates = function (connection, spamd_response, config) {
    var plugin = this;
    if (spamd_response.flag === 'Yes') {
        // X-Spam-Flag is added by SpamAssassin
        connection.transaction.remove_header('precedence');
        connection.transaction.add_header('Precedence', 'junk');
    }

    var modern = config.main.modern_status_syntax;
    for (var key in spamd_response.headers) {
        if (!key || key === '' || key === undefined) continue;
        var val = spamd_response.headers[key];
        if (val === undefined) { val = ''; }

        if (key === 'Status' && !modern) {
            var legacy = spamd_response.headers[key].replace(/ score=/,' hits=');
            connection.transaction.add_header('X-Spam-Status', legacy);
            continue;
        }
        connection.transaction.add_header('X-Spam-' + key, val);
    }
};

function score_too_high(config, connection, spamd_response) {
    var score = spamd_response.score;
    if (connection.relaying) {
        var rmax = config.main.relay_reject_threshold;
        if (rmax && (score >= rmax)) {
            return "spam score exceeded relay threshold";
        }
    }

    var max = config.main.reject_threshold;
    if (max && (score >= max)) {
        return "spam score exceeded threshold";
    }

    return;
}

function get_spamd_username(config, connection) {

    var user = connection.transaction.notes.spamd_user;  // 1st priority
    if (user && user !== undefined) return user;

    if (!config.main.spamd_user) return 'default';   // when not defined
    user = config.main.spamd_user;

    // Enable per-user SA prefs
    if (user === 'first-recipient') {                // special cases
        return connection.transaction.rcpt_to[0].address();
    }
    if (user === 'all-recipients') {
        throw "Unimplemented";
        // TODO: pass the message through SA for each recipient. Then apply
        // the least strict result to the connection. That is useful when
        // one user blacklists a sender that another user wants to get mail
        // from. If this is something you care about, this is the spot.
    }
    return user;
}

function get_spamd_headers(connection, username) {
    // http://svn.apache.org/repos/asf/spamassassin/trunk/spamd/PROTOCOL
    var headers = [
        'HEADERS SPAMC/1.3',
        'User: ' + username,
        '',
        'X-Envelope-From: ' + connection.transaction.mail_from.address(),
        'X-Haraka-UUID: ' + connection.transaction.uuid,
    ];
    if (connection.relaying) {
        headers.push('X-Haraka-Relay: true');
    }
    return headers;
}

function get_spamd_socket(config, next, connection, plugin) {
    // TODO: support multiple spamd backends
    var socket = new sock.Socket();
    if (config.main.spamd_socket.match(/\//)) {    // assume unix socket
        socket.connect(config.main.spamd_socket);
    }
    else {
        var hostport = config.main.spamd_socket.split(/:/);
        socket.connect((hostport[1] || 783), hostport[0]);
    }

    var connect_timeout = parseInt(config.main.connect_timeout) || 30;
    socket.setTimeout(connect_timeout * 1000);

    socket.on('timeout', function () {
        if (!this.is_connected) {
            connection.logerror(plugin, 'connection timed out');
        }
        else {
            connection.logerror(plugin, 'timeout waiting for results');
        }
        socket.end();
        return next();
    });
    socket.on('error', function (err) {
        connection.logerror(plugin, 'connection failed: ' + err);
        // TODO: optionally DENYSOFT
        // TODO: add a transaction note
        return next();
    });
    return socket;
}

function msg_too_big(config, connection, plugin) {
    if (!config.main.max_size) return false;

    var size = connection.transaction.data_bytes;
    var max = config.main.max_size;
    if (size > max) {
        connection.loginfo(plugin, 'skipping, size ' + prettySize(size) + ' exceeds max: ' + prettySize(max));
        return true;
    }
    return false;
}

function log_results(connection, plugin, spamd_response, config) {
    connection.loginfo(plugin, "status=" + spamd_response.flag +
          ', score=' + spamd_response.score +
          ', required=' + spamd_response.reqd +
          ', reject=' + ((connection.relaying) ?
             (config.main.relay_reject_threshold || config.main.reject_threshold) :
             config.main.reject_threshold) +
          ', tests="' + spamd_response.tests + '"');
}
<|endoftext|>