<commit_before>function whatToTestSpidermonkeyTrunk(code)
{
  // regexps can't match across lines, so replace whitespace with spaces.
  var codeL = code.replace(/\s/g, " ");

  return {

    allowParse: true,

    allowExec: unlikelyToHang(code)
      && (jsshell || code.indexOf("nogeckoex") == -1)
    ,

    allowIter: true,

    // Ideally we'd detect whether the shell was compiled with --enable-more-deterministic
    expectConsistentOutput: true
       && (gcIsQuiet || code.indexOf("gc") == -1)
       && code.indexOf("Date") == -1                // time marches on
       && code.indexOf("random") == -1
       && code.indexOf("dumpObject") == -1          // shows heap addresses
       && code.indexOf("oomAfterAllocations") == -1
       && code.indexOf("reducePar") == -1           // only deterministic for associative elemental-reducers
       && code.indexOf("scanPar") == -1             // only deterministic for associative elemental-reducers
       && code.indexOf("scatterPar") == -1          // only deterministic for associative conflict-resolvers
    ,

    expectConsistentOutputAcrossIter: true
       && code.indexOf("options") == -1             // options() is per-cx, and the js shell doesn't create a new cx for each sandbox/compartment
    ,

    expectConsistentOutputAcrossJITs: true
       && code.indexOf("/*NODIFF*/") == -1          // Ignore diff testing on these labels
       && code.indexOf("'strict") == -1             // see bug 743425
       && code.indexOf("Object.seal") == -1         // bug 937922
       && code.indexOf("Math.fround") == -1         // bug 995673
       && code.indexOf("valueOf") == -1             // bug 995673
       && code.indexOf("Math.cos") == -1            // bug 995675
       && code.indexOf("Math.pow") == -1            // bug 995675
       && code.indexOf("ArrayBuffer") == -1         // bug 995679
       && code.indexOf("neuter") == -1              // bug 995679
       && code.indexOf("gc") == -1                  // bug 995816
       && code.indexOf("usecount.trigger") == -1    // bug 995817
       && !( codeL.match(/\/.*[\u0000\u0080-\uffff]/)) // doesn't stay valid utf-8 after going through python (?)

  };
}

function whatToTestSpidermonkeyMozilla24(code)
{
  // regexps can't match across lines, so replace whitespace with spaces.
  var codeL = code.replace(/\s/g, " ");

  return {

    allowParse: true,

    allowExec: unlikelyToHang(code)
      && (jsshell || code.indexOf("nogeckoex") == -1)
    ,

    allowIter: true,

    // Ideally we'd detect whether the shell was compiled with --enable-more-deterministic
    expectConsistentOutput: true
       && (gcIsQuiet || code.indexOf("gc") == -1)
       && code.indexOf("Date") == -1                // time marches on
       && code.indexOf("random") == -1
       && code.indexOf("dumpObject") == -1          // shows heap addresses
       && code.indexOf("oomAfterAllocations") == -1
    ,

    expectConsistentOutputAcrossIter: true
       && code.indexOf("options") == -1             // options() is per-cx, and the js shell doesn't create a new cx for each sandbox/compartment
    ,

    expectConsistentOutputAcrossJITs: true
       && code.indexOf("'strict") == -1             // see bug 743425
       && code.indexOf("Object.seal") == -1         // bug 937922 (24 branch)
       && code.indexOf("RegExp") == -1              // bug 945512 (24 branch)
       && !( codeL.match(/\/.*[\u0000\u0080-\uffff]/)) // doesn't stay valid utf-8 after going through python (?)

  };
}

function whatToTestJavaScriptCore(code)
{
  return {

    allowParse: true,
    allowExec: unlikelyToHang(code),
    allowIter: false, // JavaScriptCore does not support |yield| and |Iterator|
    expectConsistentOutput: false,
    expectConsistentOutputAcrossIter: false,
    expectConsistentOutputAcrossJITs: false

  };
}

function whatToTestGeneric(code)
{
  return {
    allowParse: true,
    allowExec: unlikelyToHang(code),
    allowIter: (typeof Iterator == "function"),
    expectConsistentOutput: false,
    expectConsistentOutputAcrossIter: false,
    expectConsistentOutputAcrossJITs: false
  };
}

var whatToTest;
if (engine == ENGINE_SPIDERMONKEY_TRUNK)
  whatToTest = whatToTestSpidermonkeyTrunk;
else if (engine == ENGINE_SPIDERMONKEY_MOZILLA24)
  whatToTest = whatToTestSpidermonkeyMozilla24;
else if (engine == ENGINE_JAVASCRIPTCORE)
  whatToTest = whatToTestJavaScriptCore;
else
  whatToTest = whatToTestGeneric;

function unlikelyToHang(code)
{
  var codeL = code.replace(/\s/g, " ");

  // Things that are likely to hang in all JavaScript engines
  return true
    && code.indexOf("infloop") == -1
    && !( codeL.match( /const.*for/ )) // can be an infinite loop: function() { const x = 1; for each(x in ({a1:1})) dumpln(3); }
    && !( codeL.match( /for.*const/ )) // can be an infinite loop: for each(x in ...); const x;
    && !( codeL.match( /for.*in.*uneval/ )) // can be slow to loop through the huge string uneval(this), for example
    && !( codeL.match( /for.*for.*for/ )) // nested for loops (including for..in, array comprehensions, etc) can take a while
    && !( codeL.match( /for.*for.*gc/ ))
    ;
}
<commit_msg>Add bug 995826.<commit_after>function whatToTestSpidermonkeyTrunk(code)
{
  // regexps can't match across lines, so replace whitespace with spaces.
  var codeL = code.replace(/\s/g, " ");

  return {

    allowParse: true,

    allowExec: unlikelyToHang(code)
      && (jsshell || code.indexOf("nogeckoex") == -1)
    ,

    allowIter: true,

    // Ideally we'd detect whether the shell was compiled with --enable-more-deterministic
    expectConsistentOutput: true
       && (gcIsQuiet || code.indexOf("gc") == -1)
       && code.indexOf("Date") == -1                // time marches on
       && code.indexOf("random") == -1
       && code.indexOf("dumpObject") == -1          // shows heap addresses
       && code.indexOf("oomAfterAllocations") == -1
       && code.indexOf("reducePar") == -1           // only deterministic for associative elemental-reducers
       && code.indexOf("scanPar") == -1             // only deterministic for associative elemental-reducers
       && code.indexOf("scatterPar") == -1          // only deterministic for associative conflict-resolvers
    ,

    expectConsistentOutputAcrossIter: true
       && code.indexOf("options") == -1             // options() is per-cx, and the js shell doesn't create a new cx for each sandbox/compartment
    ,

    expectConsistentOutputAcrossJITs: true
       && code.indexOf("/*NODIFF*/") == -1          // Ignore diff testing on these labels
       && code.indexOf("'strict") == -1             // see bug 743425
       && code.indexOf("Object.seal") == -1         // bug 937922
       && code.indexOf("Math.fround") == -1         // bug 995673
       && code.indexOf("valueOf") == -1             // bug 995673
       && code.indexOf("Math.cos") == -1            // bug 995675
       && code.indexOf("Math.pow") == -1            // bug 995675
       && code.indexOf("ArrayBuffer") == -1         // bug 995679
       && code.indexOf("neuter") == -1              // bug 995679
       && code.indexOf("gc") == -1                  // bug 995816
       && code.indexOf("usecount.trigger") == -1    // bug 995817
       && code.indexOf("Math.round") == -1          // bug 995826
       && code.indexOf("Math.tan") == -1            // bug 995826
       && !( codeL.match(/\/.*[\u0000\u0080-\uffff]/)) // doesn't stay valid utf-8 after going through python (?)

  };
}

function whatToTestSpidermonkeyMozilla24(code)
{
  // regexps can't match across lines, so replace whitespace with spaces.
  var codeL = code.replace(/\s/g, " ");

  return {

    allowParse: true,

    allowExec: unlikelyToHang(code)
      && (jsshell || code.indexOf("nogeckoex") == -1)
    ,

    allowIter: true,

    // Ideally we'd detect whether the shell was compiled with --enable-more-deterministic
    expectConsistentOutput: true
       && (gcIsQuiet || code.indexOf("gc") == -1)
       && code.indexOf("Date") == -1                // time marches on
       && code.indexOf("random") == -1
       && code.indexOf("dumpObject") == -1          // shows heap addresses
       && code.indexOf("oomAfterAllocations") == -1
    ,

    expectConsistentOutputAcrossIter: true
       && code.indexOf("options") == -1             // options() is per-cx, and the js shell doesn't create a new cx for each sandbox/compartment
    ,

    expectConsistentOutputAcrossJITs: true
       && code.indexOf("'strict") == -1             // see bug 743425
       && code.indexOf("Object.seal") == -1         // bug 937922 (24 branch)
       && code.indexOf("RegExp") == -1              // bug 945512 (24 branch)
       && !( codeL.match(/\/.*[\u0000\u0080-\uffff]/)) // doesn't stay valid utf-8 after going through python (?)

  };
}

function whatToTestJavaScriptCore(code)
{
  return {

    allowParse: true,
    allowExec: unlikelyToHang(code),
    allowIter: false, // JavaScriptCore does not support |yield| and |Iterator|
    expectConsistentOutput: false,
    expectConsistentOutputAcrossIter: false,
    expectConsistentOutputAcrossJITs: false

  };
}

function whatToTestGeneric(code)
{
  return {
    allowParse: true,
    allowExec: unlikelyToHang(code),
    allowIter: (typeof Iterator == "function"),
    expectConsistentOutput: false,
    expectConsistentOutputAcrossIter: false,
    expectConsistentOutputAcrossJITs: false
  };
}

var whatToTest;
if (engine == ENGINE_SPIDERMONKEY_TRUNK)
  whatToTest = whatToTestSpidermonkeyTrunk;
else if (engine == ENGINE_SPIDERMONKEY_MOZILLA24)
  whatToTest = whatToTestSpidermonkeyMozilla24;
else if (engine == ENGINE_JAVASCRIPTCORE)
  whatToTest = whatToTestJavaScriptCore;
else
  whatToTest = whatToTestGeneric;

function unlikelyToHang(code)
{
  var codeL = code.replace(/\s/g, " ");

  // Things that are likely to hang in all JavaScript engines
  return true
    && code.indexOf("infloop") == -1
    && !( codeL.match( /const.*for/ )) // can be an infinite loop: function() { const x = 1; for each(x in ({a1:1})) dumpln(3); }
    && !( codeL.match( /for.*const/ )) // can be an infinite loop: for each(x in ...); const x;
    && !( codeL.match( /for.*in.*uneval/ )) // can be slow to loop through the huge string uneval(this), for example
    && !( codeL.match( /for.*for.*for/ )) // nested for loops (including for..in, array comprehensions, etc) can take a while
    && !( codeL.match( /for.*for.*gc/ ))
    ;
}
<|endoftext|>


<commit_before>'use strict';
if (typeof Levels === 'undefined') var Levels = {};

Levels.MultirotorObstacles = function()
{
	this.name = "MultirotorObstacles";
	this.title = "Multirotor Obstacles";
	this.boilerPlateCode = "function controlFunction(vehicle){  \n  return {thrustLeft: 9.81/2+.001, thrustRight: 9.81/2-0.001};\n};";
	this.sampleSolution = "function attitude_control(theta_ref, thrust_ref, vehicle){\n  var P = theta_ref-vehicle.theta;\n  var kP = 100;\n  var kD = 20;\n  var y = kP * P - kD * vehicle.dtheta;  \n  return {thrustLeft: thrust_ref/2+y, thrustRight: thrust_ref/2-y};\n}\n\nfunction acceleration_control(ax_ref, ay_ref, vehicle){\n  ay_ref += 9.81;\n  var theta_ref = Math.atan2(ax_ref, ay_ref);\n  var thrust_ref = Math.sqrt(1e-3 + ax_ref*ax_ref + ay_ref*ay_ref);\n  return attitude_control(theta_ref, thrust_ref, vehicle);\n}\n\nvar waypoint_idx = 0;\nfunction controlFunction(vehicle){ \n  var waypoints = [\n    {x: -2, y: 0},\n    {x: -1, y: 0},\n    {x: -1, y: 2},\n    {x: 0, y: 2},\n    {x: 0, y: 0},\n  ];  \n  var p = waypoints[waypoint_idx];\n  if(Math.abs(p.x-vehicle.x) < 0.2 && Math.abs(p.y-vehicle.y) < 0.2)\n    waypoint_idx = Math.min(waypoints.length-1,waypoint_idx+1);\n  var p = waypoints[waypoint_idx];  \n  return acceleration_control(-2*vehicle.dx-vehicle.x+p.x, -2*vehicle.dy-vehicle.y+p.y, vehicle);  \n};";
	this.description = "Same rules as in the intro. Plus, don't touch the walls!

Hint: This may be useful:
[{x:-2,y:0},{x:-1,y:0},{x:-1,y:2},{x:0,y:2},{x:0,y:0}]
";
	ImageDataCache.load('multirotor_lowres.png');
	this.model = new Models.Multirotor({imgURL:'multirotor_lowres.png',	theta: 0,dtheta: 0,x: -2,dx: 0,y: 1,dy: 0});
}

Levels.MultirotorObstacles.prototype.levelComplete = function(){
	return Math.abs(this.model.x) < 0.01
		&& Math.abs(this.model.y) < 0.01
		&& Math.abs(this.model.dx) < 0.01
		&& Math.abs(this.model.dy) < 0.01
		&& Math.abs(this.model.theta) < 0.01
		&& Math.abs(this.model.dtheta) < 0.01;
}

Levels.MultirotorObstacles.prototype.levelFailed = function(){
	return (Math.abs(this.model.x+1.5) < 0.1 && this.model.y > 0.8)
		|| (Math.abs(this.model.x+0.5) < 0.1 && this.model.y < 1.2);
}

Levels.MultirotorObstacles.prototype.simulate = function (dt, controlFunc)
{ this.model = this.model.simulate (dt, controlFunc); }

Levels.MultirotorObstacles.prototype.getSimulationTime = function()	{return this.model.T;}

Levels.MultirotorObstacles.prototype.draw = function(ctx, canvas){
	resetCanvas(ctx,canvas);
	ctx.scale(2,2);
	ctx.translate(1,-1);
	ctx.fillStyle="#88ff88";
	var s = this.model.Length;
	ctx.fillRect(-2*s,-.8*s,4*s,1.6*s);

	ctx.fillStyle="#883300";
	var d = 0.1;
	ctx.fillRect(-1.5-d,.5-d,2*d,20);
	ctx.fillRect(-0.5-d,1.5+d-20,2*d,20);

	this.model.drawVehicle(ctx, canvas);

	if(this.levelFailed()){
		ctx.save();
		ctx.translate(-1,0);
		ctx.scale(0.005,-0.005);
		ctx.font="10px Verdana";
		ctx.textAlign="center";
		ctx.fillStyle="#990000";
		ctx.fillText("CRASHED!",0,0);
		ctx.restore();
	}
}

Levels.MultirotorObstacles.prototype.infoText = function(ctx, canvas){return this.model.infoText();}<commit_msg>tuned sample soln<commit_after>'use strict';
if (typeof Levels === 'undefined') var Levels = {};

Levels.MultirotorObstacles = function()
{
	this.name = "MultirotorObstacles";
	this.title = "Multirotor Obstacles";
	this.boilerPlateCode = "function controlFunction(vehicle){  \n  return {thrustLeft: 9.81/2+.001, thrustRight: 9.81/2-0.001};\n};";
	this.sampleSolution = "function attitude_control(theta_ref, thrust_ref, vehicle){\n  var P = theta_ref-vehicle.theta;\n  var kP = 100;\n  var kD = 20;\n  var y = kP * P - kD * vehicle.dtheta;  \n  return {thrustLeft: thrust_ref/2+y, thrustRight: thrust_ref/2-y};\n}\n\nfunction acceleration_control(ax_ref, ay_ref, vehicle){\n  ay_ref += 9.81;\n  var theta_ref = Math.atan2(ax_ref, ay_ref);\n  var thrust_ref = Math.sqrt(1e-3 + ax_ref*ax_ref + ay_ref*ay_ref);\n  return attitude_control(theta_ref, thrust_ref, vehicle);\n}\n\nvar waypoint_idx = 0;\nfunction controlFunction(vehicle){ \n  var waypoints = [\n    {x: -2, y: 0},\n    {x: -1, y: 0},\n    {x: -1, y: 1.6},\n    {x: 0, y: 1.8},\n    {x: 0, y: 0},\n  ];  \n  var p = waypoints[waypoint_idx];\n  if(Math.abs(p.x-vehicle.x) < 0.4 && Math.abs(p.y-vehicle.y) < 0.4)\n    waypoint_idx = Math.min(waypoints.length-1,waypoint_idx+1);\n  var p = waypoints[waypoint_idx];  \n  var kP = 3;\n  var kD = 2.5;\n  return acceleration_control(-kD*vehicle.dx+kP*(-vehicle.x+p.x), -kD*vehicle.dy+kP*(-vehicle.y+p.y), vehicle);  \n};";
	this.description = "Same rules as in the intro. Plus, don't touch the walls!

Hint: This may be useful:
[{x:-2,y:0},{x:-1,y:0},{x:-1,y:2},{x:0,y:2},{x:0,y:0}]
";
	ImageDataCache.load('multirotor_lowres.png');
	this.model = new Models.Multirotor({imgURL:'multirotor_lowres.png',	theta: 0,dtheta: 0,x: -2,dx: 0,y: 1,dy: 0});
}

Levels.MultirotorObstacles.prototype.levelComplete = function(){
	return Math.abs(this.model.x) < 0.01
		&& Math.abs(this.model.y) < 0.01
		&& Math.abs(this.model.dx) < 0.01
		&& Math.abs(this.model.dy) < 0.01
		&& Math.abs(this.model.theta) < 0.01
		&& Math.abs(this.model.dtheta) < 0.01;
}

Levels.MultirotorObstacles.prototype.levelFailed = function(){
	return (Math.abs(this.model.x+1.5) < 0.1 && this.model.y > 0.8)
		|| (Math.abs(this.model.x+0.5) < 0.1 && this.model.y < 1.2);
}

Levels.MultirotorObstacles.prototype.simulate = function (dt, controlFunc)
{ this.model = this.model.simulate (dt, controlFunc); }

Levels.MultirotorObstacles.prototype.getSimulationTime = function()	{return this.model.T;}

Levels.MultirotorObstacles.prototype.draw = function(ctx, canvas){
	resetCanvas(ctx,canvas);
	ctx.scale(2,2);
	ctx.translate(1,-1);
	ctx.fillStyle="#88ff88";
	var s = this.model.Length;
	ctx.fillRect(-2*s,-.8*s,4*s,1.6*s);

	ctx.fillStyle="#883300";
	var d = 0.1;
	ctx.fillRect(-1.5-d,.5-d,2*d,20);
	ctx.fillRect(-0.5-d,1.5+d-20,2*d,20);

	this.model.drawVehicle(ctx, canvas);

	if(this.levelFailed()){
		ctx.save();
		ctx.translate(-1,0);
		ctx.scale(0.005,-0.005);
		ctx.font="10px Verdana";
		ctx.textAlign="center";
		ctx.fillStyle="#990000";
		ctx.fillText("CRASHED!",0,0);
		ctx.restore();
	}
}

Levels.MultirotorObstacles.prototype.infoText = function(ctx, canvas){return this.model.infoText();}<|endoftext|>


<commit_before>Benchmark.options.maxTime = 1;

function assertEquality(actual, expected) {
  if (actual instanceof Lazy.ObjectLikeSequence) {
    actual = actual.toObject();
  } else if (actual instanceof Lazy.Sequence) {
    actual = actual.toArray();
  }
  expect(actual).toEqual(expected);
}

// This is a totally unnecessary bit of fanciness that I just felt like writing.
$(window).on('scroll', function() {
  $('.index nav').css('top', Math.max(110 - window.pageYOffset, 0) + 'px');
});
<commit_msg>added selection of current nav link from current site<commit_after>Benchmark.options.maxTime = 1;

function assertEquality(actual, expected) {
  if (actual instanceof Lazy.ObjectLikeSequence) {
    actual = actual.toObject();
  } else if (actual instanceof Lazy.Sequence) {
    actual = actual.toArray();
  }
  expect(actual).toEqual(expected);
}

// This is a totally unnecessary bit of fanciness that I just felt like writing.
$(window).on('scroll', function() {
  $('.index nav').css('top', Math.max(110 - window.pageYOffset, 0) + 'px');
});

$(document).on('click', 'a[href^="#"]', function(e) {
  var navLink = $('.index nav li a[href="' + $(this).attr('href') + '"]');

  if (navLink.length > 0) {
    $('.index nav li.selected').removeClass('selected');
    navLink.parent().addClass('selected');
  }
});
<|endoftext|>


<commit_before>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * Renders a chart for each workspace
 */
var Chart = Backbone.View.extend({
    initialize: function(args) {
        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("chart_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "receive_data", "process_data", "show",
            "setOptions");
        this.workspace.bind('query:result', this.receive_data);

        // Add chart button
        this.add_button();
        this.workspace.toolbar.chart = this.show;

        // Listen to adjust event and rerender chart
        this.workspace.bind('workspace:adjust', this.render);

        // Create navigation
        this.nav = $("
" +
        		"bar" +
                "stacked bar" +
        		"line" +
        		"pie" +
                "heatgrid" +
        		"
").css({
        		    'padding-bottom': '10px'
        		});
        this.nav.find('a').css({
                    color: '#666',
                    'margin-right': '5px',
                    'text-decoration': 'none',
                    'border': '1px solid #ccc',
                    padding: '5px'
                })
                .click(this.setOptions);

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.nav.hide());
    },

    add_button: function() {
        var $chart_button =
            $('')
            .css({  'background-image': "url('js/saiku/plugins/Chart/chart.png')",
                    'background-repeat':'no-repeat',
                    'background-position':'50% 50%'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function(event, ui) {
        $(this.workspace.el).find('.workspace_results table').toggle();
        $(this.el).toggle();
        $(this.nav).toggle();
        $(event.target).toggleClass('on');

        if ($(event.target).hasClass('on')) {
            this.render();
        }
    },

    setOptions: function(event) {
        var type = $(event.target).attr('href').replace('#', '');
        try {
            this[type]();
        } catch (e) { }

        return false;
    },

    stackedBar: function() {
        this.options.stacked = true;
        this.options.type = "BarChart";
        this.render();
    },

    bar: function() {
        this.options.stacked = false;
        this.options.type = "BarChart";
        this.render();
    },

    line: function() {
        this.options.stacked = false;
        this.options.type = "LineChart";
        this.render();
    },

    pie: function() {
        this.options.stacked = false;
        this.options.type = "PieChart";
        this.render();
    },

    heatgrid: function() {
        this.options.stacked = false;
        this.options.type = "HeatGridChart";
        this.render();
    },

    render: function() {
        if (! $(this.workspace.toolbar.el).find('.chart').hasClass('on')) {
            return;
        }

        var options = _.extend({
            canvas: this.id,
            width: $(this.workspace.el).find('.workspace_results').width() - 40,
            height: $(this.workspace.el).find('.workspace_results').height() - 40,
            yAxisSize: 70,
            orientation: 'vertical',
            stacked: false,
            animate: false,
            showValues: false,
            legend: true,
            legendPosition:"top",
            legendAlign: "right",
            colors: ["#B40010", "#CCC8B4", "#DDB965", "#72839D", "#1D2D40"],
            type: 'BarChart'
        }, this.options);

        if (options.type == "HeatGridChart") {
            options = _.extend({
                    canvas: this.id,
                    width: $(this.workspace.el).find('.workspace_results').width() - 40,
                    height: $(this.workspace.el).find('.workspace_results').height() - 40,
                    animate: false,
                    clickable: false,
                    orientation: "horizontal",
                    showValues: false,
                    showXScale: true,
                    xAxisPosition: "bottom",
                    showYScale: true,
                    panelSizeRatio: 0.8,
                    yAxisPosition: "left",
                    yAxisSize: 150,
                    minColor: "#FEDFE1",
                    maxColor: "#F11929",
                    extensionPoints: {
                        xAxisLabel_textAngle: -(Math.PI / 2),
                        xAxisLabel_textAlign: "right",
                        xAxisLabel_bottom: 10
                    }
            }, this.options);
        }
        if (this.data.resultset.length > 5 ) {
            options.extensionPoints = {
                xAxisLabel_textAngle: -(Math.PI / 2),
                xAxisLabel_textAlign: "right",
                xAxisLabel_bottom: 10
            };

            options.xAxisSize = 100;
        }

        this.chart = new pvc[options.type](options);

        this.chart.setData(this.data, {
            crosstabMode: true,
            seriesInRows: false
        });

        try {
            this.chart.render();
            Saiku.i18n.automatic_i18n();
        } catch (e) {
            $(this.el).text("Could not render chart");
        }
    },

    receive_data: function(args) {
        return _.delay(this.process_data, 0, args);
    },

    process_data: function(args) {
        this.data = {};
        this.data.resultset = [];
        this.data.metadata = [];
        this.data.height = 0;
        this.data.width = 0;

        function makeSureUniqueLabels(resultset) {
            function appendUniqueCounter() {
                for(var i = 0; i < resultset.length; ++i) {
                    var record = resultset[i];
                    record[0] = record[0] + ' [' + (i + 1) + ']';
                }
            }

            var labelsSet = {};
            for(var i = 0; i < resultset.length; ++i) {
                var record = resultset[i];
                var label = record[0];
                if(labelsSet[label]) {
                    appendUniqueCounter();
                    return;
                } else {
                    labelsSet[label] = true;
                }
            }
        }

        if (args.data.cellset && args.data.cellset.length > 0) {

            var lowest_level = 0;

            for (var row = 0; row < args.data.cellset.length; row++) {
                if (args.data.cellset[row][0].type == "ROW_HEADER_HEADER") {
                    this.data.metadata = [];
                    for (var field = 0; field < args.data.cellset[row].length; field++) {
                        if (args.data.cellset[row][field].type == "ROW_HEADER_HEADER") {
                            this.data.metadata.shift();
                            lowest_level = field;
                        }

                        this.data.metadata.push({
                            colIndex: field,
                            colType: typeof(args.data.cellset[row + 1][field].value) !== "number" &&
                                isNaN(args.data.cellset[row + 1][field].value
                                .replace(/[^a-zA-Z 0-9.]+/g,'')) ? "String" : "Numeric",
                            colName: args.data.cellset[row][field].value
                        });
                    }
                } else if (args.data.cellset[row][0].value !== "") {
                    var record = [];
                    this.data.width = args.data.cellset[row].length;
                    var label = [];
                    for (var labelCol = lowest_level; labelCol >= 0; labelCol--) {
                        var lastKnownUpperLevelRow = row;
                        while(args.data.cellset[lastKnownUpperLevelRow][labelCol].value === 'null') {
                            --lastKnownUpperLevelRow;
                        }
                        label.push(args.data.cellset[lastKnownUpperLevelRow][labelCol].value);
                    }
                    record.push(label.join('/'));
                    for (var col = lowest_level + 1; col < args.data.cellset[row].length; col++) {
                        var value = args.data.cellset[row][col].value;
                        // check if the resultset contains the raw value, if not try to parse the given value
                        if (args.data.cellset[row][col].properties.raw && args.data.cellset[row][col].properties.raw !== "null")
                        {
                            value = parseFloat(args.data.cellset[row][col].properties.raw);
                        } else if (typeof(args.data.cellset[row][col].value) !== "number" &&
                            parseFloat(args.data.cellset[row][col].value.replace(/[^a-zA-Z 0-9.]+/g,'')))
                        {
                            value = parseFloat(args.data.cellset[row][col].value.replace(/[^a-zA-Z 0-9.]+/g,''));
                        }
                        record.push(value);
                    }
                    this.data.resultset.push(record);
                }
            }
            makeSureUniqueLabels(this.data.resultset);
            this.data.height = this.data.resultset.length;
            this.render();
        } else {
            $(this.el).text("No results");
        }
    }
});

/**
 * Loads CCC and initializes chart plugin
 */
(function() {
    // Initialize CCC
    $.ajax({
        url: "js/saiku/plugins/Chart/ccc.js",
        dataType: "script",
        cache: true,
        success: function() {

            var initPlugin = function(session) {
                function new_workspace(args) {
                    // Add chart element
                    if (typeof args.workspace.chart == "undefined") {
                        args.workspace.chart = new Chart({ workspace: args.workspace });
                    }
                }

                function clear_workspace(args) {
                    if (typeof args.workspace.chart != "undefined") {
                        $(args.workspace.chart.nav).hide();
                        $(args.workspace.chart.el).hide();
                        $(args.workspace.chart.el).parents().find('.workspace_results table').show();
                    }
                }

                // Attach chart to existing tabs
                for(var i = 0; i < Saiku.tabs._tabs.length; i++) {
                    var tab = Saiku.tabs._tabs[i];
                    new_workspace({
                        workspace: tab.content
                    });
                };

                // Attach chart to future tabs
                session.bind("workspace:new", new_workspace);
                session.bind("workspace:clear", clear_workspace);
            };

            if (typeof Saiku.session == "undefined") {
                Saiku.events.bind('session:new', initPlugin);
            } else {
                initPlugin(Saiku.session);
            }
        }
    });
}());
<commit_msg>Edge condition if no label is available<commit_after>/*
 *   Copyright 2012 OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * Renders a chart for each workspace
 */
var Chart = Backbone.View.extend({
    initialize: function(args) {
        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("chart_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "receive_data", "process_data", "show",
            "setOptions");
        this.workspace.bind('query:result', this.receive_data);

        // Add chart button
        this.add_button();
        this.workspace.toolbar.chart = this.show;

        // Listen to adjust event and rerender chart
        this.workspace.bind('workspace:adjust', this.render);

        // Create navigation
        this.nav = $("
" +
        		"bar" +
                "stacked bar" +
        		"line" +
        		"pie" +
                "heatgrid" +
        		"
").css({
        		    'padding-bottom': '10px'
        		});
        this.nav.find('a').css({
                    color: '#666',
                    'margin-right': '5px',
                    'text-decoration': 'none',
                    'border': '1px solid #ccc',
                    padding: '5px'
                })
                .click(this.setOptions);

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.nav.hide());
    },

    add_button: function() {
        var $chart_button =
            $('')
            .css({  'background-image': "url('js/saiku/plugins/Chart/chart.png')",
                    'background-repeat':'no-repeat',
                    'background-position':'50% 50%'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function(event, ui) {
        $(this.workspace.el).find('.workspace_results table').toggle();
        $(this.el).toggle();
        $(this.nav).toggle();
        $(event.target).toggleClass('on');

        if ($(event.target).hasClass('on')) {
            this.render();
        }
    },

    setOptions: function(event) {
        var type = $(event.target).attr('href').replace('#', '');
        try {
            this[type]();
        } catch (e) { }

        return false;
    },

    stackedBar: function() {
        this.options.stacked = true;
        this.options.type = "BarChart";
        this.render();
    },

    bar: function() {
        this.options.stacked = false;
        this.options.type = "BarChart";
        this.render();
    },

    line: function() {
        this.options.stacked = false;
        this.options.type = "LineChart";
        this.render();
    },

    pie: function() {
        this.options.stacked = false;
        this.options.type = "PieChart";
        this.render();
    },

    heatgrid: function() {
        this.options.stacked = false;
        this.options.type = "HeatGridChart";
        this.render();
    },

    render: function() {
        if (! $(this.workspace.toolbar.el).find('.chart').hasClass('on')) {
            return;
        }

        var options = _.extend({
            canvas: this.id,
            width: $(this.workspace.el).find('.workspace_results').width() - 40,
            height: $(this.workspace.el).find('.workspace_results').height() - 40,
            yAxisSize: 70,
            orientation: 'vertical',
            stacked: false,
            animate: false,
            showValues: false,
            legend: true,
            legendPosition:"top",
            legendAlign: "right",
            colors: ["#B40010", "#CCC8B4", "#DDB965", "#72839D", "#1D2D40"],
            type: 'BarChart'
        }, this.options);

        if (options.type == "HeatGridChart") {
            options = _.extend({
                    canvas: this.id,
                    width: $(this.workspace.el).find('.workspace_results').width() - 40,
                    height: $(this.workspace.el).find('.workspace_results').height() - 40,
                    animate: false,
                    clickable: false,
                    orientation: "horizontal",
                    showValues: false,
                    showXScale: true,
                    xAxisPosition: "bottom",
                    showYScale: true,
                    panelSizeRatio: 0.8,
                    yAxisPosition: "left",
                    yAxisSize: 150,
                    minColor: "#FEDFE1",
                    maxColor: "#F11929",
                    extensionPoints: {
                        xAxisLabel_textAngle: -(Math.PI / 2),
                        xAxisLabel_textAlign: "right",
                        xAxisLabel_bottom: 10
                    }
            }, this.options);
        }
        if (this.data.resultset.length > 5 ) {
            options.extensionPoints = {
                xAxisLabel_textAngle: -(Math.PI / 2),
                xAxisLabel_textAlign: "right",
                xAxisLabel_bottom: 10
            };

            options.xAxisSize = 100;
        }

        this.chart = new pvc[options.type](options);

        this.chart.setData(this.data, {
            crosstabMode: true,
            seriesInRows: false
        });

        try {
            this.chart.render();
            Saiku.i18n.automatic_i18n();
        } catch (e) {
            $(this.el).text("Could not render chart");
        }
    },

    receive_data: function(args) {
        return _.delay(this.process_data, 0, args);
    },

    process_data: function(args) {
        this.data = {};
        this.data.resultset = [];
        this.data.metadata = [];
        this.data.height = 0;
        this.data.width = 0;

        function makeSureUniqueLabels(resultset) {
            function appendUniqueCounter() {
                for(var i = 0; i < resultset.length; ++i) {
                    var record = resultset[i];
                    record[0] = record[0] + ' [' + (i + 1) + ']';
                }
            }

            var labelsSet = {};
            for(var i = 0; i < resultset.length; ++i) {
                var record = resultset[i];
                var label = record[0];
                if(labelsSet[label]) {
                    appendUniqueCounter();
                    return;
                } else {
                    labelsSet[label] = true;
                }
            }
        }

        if (args.data.cellset && args.data.cellset.length > 0) {

            var lowest_level = 0;

            for (var row = 0; row < args.data.cellset.length; row++) {
                if (args.data.cellset[row][0].type == "ROW_HEADER_HEADER") {
                    this.data.metadata = [];
                    for (var field = 0; field < args.data.cellset[row].length; field++) {
                        if (args.data.cellset[row][field].type == "ROW_HEADER_HEADER") {
                            this.data.metadata.shift();
                            lowest_level = field;
                        }

                        this.data.metadata.push({
                            colIndex: field,
                            colType: typeof(args.data.cellset[row + 1][field].value) !== "number" &&
                                isNaN(args.data.cellset[row + 1][field].value
                                .replace(/[^a-zA-Z 0-9.]+/g,'')) ? "String" : "Numeric",
                            colName: args.data.cellset[row][field].value
                        });
                    }
                } else if (args.data.cellset[row][0].value !== "") {
                    var record = [];
                    this.data.width = args.data.cellset[row].length;
                    var label = [];
                    for (var labelCol = lowest_level; labelCol >= 0; labelCol--) {
                        var lastKnownUpperLevelRow = row;
                        while(args.data.cellset[lastKnownUpperLevelRow] && args.data.cellset[lastKnownUpperLevelRow][labelCol].value === 'null') {
                            --lastKnownUpperLevelRow;
                        }
                        if(args.data.cellset[lastKnownUpperLevelRow]) {
                            label.push(args.data.cellset[lastKnownUpperLevelRow][labelCol].value);
                        }
                    }
                    record.push(label.join('/'));
                    for (var col = lowest_level + 1; col < args.data.cellset[row].length; col++) {
                        var value = args.data.cellset[row][col].value;
                        // check if the resultset contains the raw value, if not try to parse the given value
                        if (args.data.cellset[row][col].properties.raw && args.data.cellset[row][col].properties.raw !== "null")
                        {
                            value = parseFloat(args.data.cellset[row][col].properties.raw);
                        } else if (typeof(args.data.cellset[row][col].value) !== "number" &&
                            parseFloat(args.data.cellset[row][col].value.replace(/[^a-zA-Z 0-9.]+/g,'')))
                        {
                            value = parseFloat(args.data.cellset[row][col].value.replace(/[^a-zA-Z 0-9.]+/g,''));
                        }
                        record.push(value);
                    }
                    this.data.resultset.push(record);
                }
            }
            makeSureUniqueLabels(this.data.resultset);
            this.data.height = this.data.resultset.length;
            this.render();
        } else {
            $(this.el).text("No results");
        }
    }
});

/**
 * Loads CCC and initializes chart plugin
 */
(function() {
    // Initialize CCC
    $.ajax({
        url: "js/saiku/plugins/Chart/ccc.js",
        dataType: "script",
        cache: true,
        success: function() {

            var initPlugin = function(session) {
                function new_workspace(args) {
                    // Add chart element
                    if (typeof args.workspace.chart == "undefined") {
                        args.workspace.chart = new Chart({ workspace: args.workspace });
                    }
                }

                function clear_workspace(args) {
                    if (typeof args.workspace.chart != "undefined") {
                        $(args.workspace.chart.nav).hide();
                        $(args.workspace.chart.el).hide();
                        $(args.workspace.chart.el).parents().find('.workspace_results table').show();
                    }
                }

                // Attach chart to existing tabs
                for(var i = 0; i < Saiku.tabs._tabs.length; i++) {
                    var tab = Saiku.tabs._tabs[i];
                    new_workspace({
                        workspace: tab.content
                    });
                };

                // Attach chart to future tabs
                session.bind("workspace:new", new_workspace);
                session.bind("workspace:clear", clear_workspace);
            };

            if (typeof Saiku.session == "undefined") {
                Saiku.events.bind('session:new', initPlugin);
            } else {
                initPlugin(Saiku.session);
            }
        }
    });
}());
<|endoftext|>


<commit_before>(function() {
    var CKEDITOR = window.CKEDITOR;

    function isUnstylable(el) {
        if (el.hasClass('cke_widget_mediatag')) {
            return false;
        }
        var b = el.getAttribute('contentEditable') === 'false' ||
            el.getAttribute('data-nostyle');
        return b;
    }

    var color1 = 'rgba(249, 230, 65, 1.0)';
    var color2 = 'rgba(252, 181, 0, 1.0)';

    CKEDITOR.plugins.add('comments', {
        onLoad: function() {
            CKEDITOR.addCss('comment { background-color: ' + color1 + '; }' +
                '@keyframes color { 0% { background-color: ' + color2 + '; } 50% { background-color: ' + color1 + '; } 100% { background-color: ' + color2 + '; } }' +
                'comment.active { animation-name: color; animation-duration: 1s; animation-iteration-count: 2; background-color: ' + color2 + '; outline: none;}' +
                'comment media-tag { border: 2px solid ' + color1 + ' !important; }' +
                'comment.active media-tag { border: 2px solid ' + color2 + ' !important; }' +
                'comment * { background-color: transparent !important; }');
        },
        init: function(editor) {
            var Messages = CKEDITOR._commentsTranslations;

            var styleDef = {
                element: 'comment',
                attributes: {
                    'data-uid': '#(uid)',
                },
                overrides: [{
                    element: 'comment'
                }],
                childRule: isUnstylable
            };
            var removeStyle = new CKEDITOR.style(styleDef, { 'uid': '' });

            var isApplicable = editor.plugins.comments.isApplicable = function(path, sel) {
                path = path || editor.elementPath();
                sel = sel || editor.getSelection();
                var applicable = removeStyle.checkApplicable(path, editor);
                var hasComments = editor.getSelectedHtml().$.querySelectorAll('comment').length;
                var isComment = removeStyle.checkActive(path, editor);
                var empty = !sel.getSelectedText();
                return applicable && !empty && !hasComments && !isComment;
            };

            // Register the command.
            editor.addCommand('comment', {
                exec: function(editor) {
                    if (editor.readOnly) { return; }
                    editor.focus();

                    var uid = CKEDITOR.tools.getUniqueId();
                    editor.plugins.comments.addComment(uid, function() {
                        // Make an undo spnashot
                        editor.fire('saveSnapshot');
                        // Make sure comments won't overlap
                        editor.removeStyle(removeStyle);

                        // Add the comment marker
                        var s = new CKEDITOR.style(styleDef, { 'uid': uid });
                        editor.applyStyle(s);

                        // Save the undo snapshot after all changes are affected.
                        setTimeout(function() {
                            editor.fire('saveSnapshot');
                        }, 0);
                    });

                }
            });

            // Uncomment provided element
            editor.plugins.comments.uncomment = function(id, els) {
                if (editor.readOnly) { return; }
                editor.fire('saveSnapshot');

                //Create style for this id
                var style = new CKEDITOR.style({
                    element: 'comment',
                    attributes: {
                        'data-uid': id,
                    },
                });
                style.alwaysRemoveElement = true;
                els.forEach(function(el) {
                    // Create range for this element
                    el.removeAttribute('class');
                    var node = new CKEDITOR.dom.node(el);
                    var range = editor.createRange();
                    range.setStart(node, 0);
                    range.setEnd(node, Number.MAX_SAFE_INTEGER);
                    // Remove style for the comment
                    try {
                        style.removeFromRange(range, editor);
                    } catch (e) {
                        console.error(e);
                    }
                });

                setTimeout(function() {
                    editor.fire('saveSnapshot');
                }, 0);
            };

            // Uncomment from context menu, disabled for now...
            editor.addCommand('uncomment', {
                exec: function(editor, data) {
                    if (editor.readOnly) { return; }
                    editor.fire('saveSnapshot');
                    if (!data ||  !data.id) {
                        editor.focus();
                        editor.removeStyle(removeStyle);
                        setTimeout(function() {
                            editor.fire('saveSnapshot');
                        }, 0);
                        return;
                    }
                }
            });

            // Register the toolbar button.
            if (editor.ui.addButton) {
                editor.ui.addButton('Comment', {
                    label: Messages.comment,
                    command: 'comment',
                    icon: '/pad/icons/comment.png',
                    toolbar: 'insert,10'
                });
            }

            if (editor.addMenuItems) {
                editor.addMenuGroup('comments');
                editor.addMenuItem('comment', {
                    label: Messages.comment,
                    icon: '/pad/icons/comment.png',
                    command: 'comment',
                    group: 'comments'
                });
                /*
                editor.addMenuItem('uncomment', {
                    label: Messages.uncomment,
                    icon : '/pad/icons/uncomment.png',
                    command: 'uncomment',
                    group: 'comments'
                });
                */
            }
            if (editor.contextMenu) {
                /*
                editor.contextMenu.addListener(function (element, sel, path) {
                    var isComment = removeStyle.checkActive(path, editor);
                    if (!isComment) { return; }
                    return {
                        uncomment: CKEDITOR.TRISTATE_OFF,
                    };
                });
                */
                editor.contextMenu.addListener(function(element, sel, path) {
                    var applicable = isApplicable(path, sel);
                    if (!applicable) { return; }
                    return {
                        comment: CKEDITOR.TRISTATE_OFF,
                    };
                });
            }
        }
    });

})();
<commit_msg>Allow comments on mathjax<commit_after>(function() {
    var CKEDITOR = window.CKEDITOR;

    function isUnstylable(el) {
        if (el.hasClass('cke_widget_mathjax')) {
            return false;
        }
        if (el.hasClass('cke_widget_mediatag')) {
            return false;
        }
        var b = el.getAttribute('contentEditable') === 'false' ||
            el.getAttribute('data-nostyle');
        return b;
    }

    var color1 = 'rgba(249, 230, 65, 1.0)';
    var color2 = 'rgba(252, 181, 0, 1.0)';

    CKEDITOR.plugins.add('comments', {
        onLoad: function() {
            CKEDITOR.addCss('comment { background-color: ' + color1 + '; }' +
                '@keyframes color { 0% { background-color: ' + color2 + '; } 50% { background-color: ' + color1 + '; } 100% { background-color: ' + color2 + '; } }' +
                'comment.active { animation-name: color; animation-duration: 1s; animation-iteration-count: 2; background-color: ' + color2 + '; outline: none;}' +
                'comment media-tag { border: 2px solid ' + color1 + ' !important; }' +
                'comment.active media-tag { border: 2px solid ' + color2 + ' !important; }' +
                'comment * { background-color: transparent !important; }');
        },
        init: function(editor) {
            var Messages = CKEDITOR._commentsTranslations;

            var styleDef = {
                element: 'comment',
                attributes: {
                    'data-uid': '#(uid)',
                },
                overrides: [{
                    element: 'comment'
                }],
                childRule: isUnstylable
            };
            var removeStyle = new CKEDITOR.style(styleDef, { 'uid': '' });

            var isApplicable = editor.plugins.comments.isApplicable = function(path, sel) {
                path = path || editor.elementPath();
                sel = sel || editor.getSelection();
                var applicable = removeStyle.checkApplicable(path, editor);
                var hasComments = editor.getSelectedHtml().$.querySelectorAll('comment').length;
                var isComment = removeStyle.checkActive(path, editor);
                var empty = !sel.getSelectedText();
                return applicable && !empty && !hasComments && !isComment;
            };

            // Register the command.
            editor.addCommand('comment', {
                exec: function(editor) {
                    if (editor.readOnly) { return; }
                    editor.focus();

                    var uid = CKEDITOR.tools.getUniqueId();
                    editor.plugins.comments.addComment(uid, function() {
                        // Make an undo spnashot
                        editor.fire('saveSnapshot');
                        // Make sure comments won't overlap
                        editor.removeStyle(removeStyle);

                        // Add the comment marker
                        var s = new CKEDITOR.style(styleDef, { 'uid': uid });
                        editor.applyStyle(s);

                        // Save the undo snapshot after all changes are affected.
                        setTimeout(function() {
                            editor.fire('saveSnapshot');
                        }, 0);
                    });

                }
            });

            // Uncomment provided element
            editor.plugins.comments.uncomment = function(id, els) {
                if (editor.readOnly) { return; }
                editor.fire('saveSnapshot');

                //Create style for this id
                var style = new CKEDITOR.style({
                    element: 'comment',
                    attributes: {
                        'data-uid': id,
                    },
                });
                style.alwaysRemoveElement = true;
                els.forEach(function(el) {
                    // Create range for this element
                    el.removeAttribute('class');
                    var node = new CKEDITOR.dom.node(el);
                    var range = editor.createRange();
                    range.setStart(node, 0);
                    range.setEnd(node, Number.MAX_SAFE_INTEGER);
                    // Remove style for the comment
                    try {
                        style.removeFromRange(range, editor);
                    } catch (e) {
                        console.error(e);
                    }
                });

                setTimeout(function() {
                    editor.fire('saveSnapshot');
                }, 0);
            };

            // Uncomment from context menu, disabled for now...
            editor.addCommand('uncomment', {
                exec: function(editor, data) {
                    if (editor.readOnly) { return; }
                    editor.fire('saveSnapshot');
                    if (!data ||  !data.id) {
                        editor.focus();
                        editor.removeStyle(removeStyle);
                        setTimeout(function() {
                            editor.fire('saveSnapshot');
                        }, 0);
                        return;
                    }
                }
            });

            // Register the toolbar button.
            if (editor.ui.addButton) {
                editor.ui.addButton('Comment', {
                    label: Messages.comment,
                    command: 'comment',
                    icon: '/pad/icons/comment.png',
                    toolbar: 'insert,10'
                });
            }

            if (editor.addMenuItems) {
                editor.addMenuGroup('comments');
                editor.addMenuItem('comment', {
                    label: Messages.comment,
                    icon: '/pad/icons/comment.png',
                    command: 'comment',
                    group: 'comments'
                });
                /*
                editor.addMenuItem('uncomment', {
                    label: Messages.uncomment,
                    icon : '/pad/icons/uncomment.png',
                    command: 'uncomment',
                    group: 'comments'
                });
                */
            }
            if (editor.contextMenu) {
                /*
                editor.contextMenu.addListener(function (element, sel, path) {
                    var isComment = removeStyle.checkActive(path, editor);
                    if (!isComment) { return; }
                    return {
                        uncomment: CKEDITOR.TRISTATE_OFF,
                    };
                });
                */
                editor.contextMenu.addListener(function(element, sel, path) {
                    var applicable = isApplicable(path, sel);
                    if (!applicable) { return; }
                    return {
                        comment: CKEDITOR.TRISTATE_OFF,
                    };
                });
            }
        }
    });

})();
<|endoftext|>


<commit_before>'use strict';

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const jsdom = Promise.promisifyAll(require('jsdom'));

const BASE_URI = 'https://www.song365.name';

class Song365 {

  fetchTracksURI(leaves) {
    return Promise.map(leaves, leaf => {
      return jsdom.envAsync(`${BASE_URI}/search/artist?keyword=${encodeURI(leaf)}`, ['http://code.jquery.com/jquery.js'])
        .then(window => {
          const $ = window.jQuery;

          const nodes = $('.search-container .search-artist .list .item em.artist-name a');
          if (nodes.length > 0) {
            const node = nodes[0];
            return $(node).attr('href').replace('artist', 'artist/tracks');
          }

          return '';
        });
    }).then(results => {
      console.log(results);
      return results;
    });
  }

  fetchSongsURI(tracks) {
    return Promise.map(tracks, track => {
      return jsdom.envAsync(BASE_URI + track, ['http://code.jquery.com/jquery.js'])
        .then(window => {
          const $ = window.jQuery;

          const tracks = [];
          $('.artist-songs .list .item div.buttons a.play').each(function () {
            const track = BASE_URI + $(this).attr('href').replace('track', 'download');
            tracks.push(track);
          });

          return tracks;
        });
    }).then(results => {
      console.log(results);
      return results;
    });
  }

  fetchStreamsURI(songs) {
    return Promise.map(songs, song => {
      return Promise.map(song, dl => {
        console.log(dl);
        return request.getAsync(encodeURI(dl))
          .then(html => {
            const re = /var hqurl = '(.*)'/gi;
            let m;

            if ((m = re.exec(html.body)) !== null) {
              if (m.index === re.lastIndex) {
                re.lastIndex++;
              }

              return m[1];
            }
          });
      });
    })
    .then(results => {
      const stream = [];
      for (let i = 0; i < results.length; i++) {
        if (results[i].length > 0) {
          stream.push([]);

          for (let j = 0; j < results[i].length; j++) {
            if (results[i][j]) {
              stream[i].push(results[i][j]);
            }
          }
        }
      }
      return stream;
    });
  }

  refresh(leaves) {
    return this.fetchTracksURI(leaves)
      .then(this.fetchSongsURI)
      .then(this.fetchStreamsURI)
      .catch(e => {
        return Promise.reject(new Error(e));
      });
  }
}

module.exports.Song365 = new Song365();
<commit_msg>Modify log messages.<commit_after>'use strict';

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'));
const jsdom = Promise.promisifyAll(require('jsdom'));

const BASE_URI = 'https://www.song365.name';

class Song365 {

  fetchTracksURI(leaves) {
    return Promise.map(leaves, leaf => {
      return jsdom.envAsync(`${BASE_URI}/search/artist?keyword=${encodeURI(leaf)}`, ['http://code.jquery.com/jquery.js'])
        .then(window => {
          const $ = window.jQuery;

          const nodes = $('.search-container .search-artist .list .item em.artist-name a');
          if (nodes.length > 0) {
            const node = nodes[0];
            return $(node).attr('href').replace('artist', 'artist/tracks');
          }

          return '';
        });
    }).then(results => {
      console.log(results);
      return results;
    });
  }

  fetchSongsURI(tracks) {
    return Promise.map(tracks, track => {
      return jsdom.envAsync(BASE_URI + track, ['http://code.jquery.com/jquery.js'])
        .then(window => {
          const $ = window.jQuery;

          const tracks = [];
          $('.artist-songs .list .item div.buttons a.play').each(function () {
            const track = BASE_URI + $(this).attr('href').replace('track', 'download');
            tracks.push(track);
          });

          return tracks;
        });
    }).then(results => {
      return results;
    });
  }

  fetchStreamsURI(songs) {
    return Promise.map(songs, song => {
      return Promise.map(song, dl => {
        return request.getAsync(encodeURI(dl))
          .then(html => {
            console.log(dl);
            const re = /var hqurl = '(.*)'/gi;
            let m;

            if ((m = re.exec(html.body)) !== null) {
              if (m.index === re.lastIndex) {
                re.lastIndex++;
              }

              return m[1];
            }
          });
      });
    })
    .then(results => {
      const stream = [];
      for (let i = 0; i < results.length; i++) {
        if (results[i].length > 0) {
          stream.push([]);

          for (let j = 0; j < results[i].length; j++) {
            if (results[i][j]) {
              stream[i].push(results[i][j]);
            }
          }
        }
      }
      return stream;
    });
  }

  refresh(leaves) {
    return this.fetchTracksURI(leaves)
      .then(this.fetchSongsURI)
      .then(this.fetchStreamsURI)
      .catch(e => {
        return Promise.reject(new Error(e));
      });
  }
}

module.exports.Song365 = new Song365();
<|endoftext|>


<commit_before>

const pathLib = require('path');
const _ = require('underscore');
const Promise = require('bluebird');
const yaml = require('js-yaml');
const fs = require('fs');
const Err = require('@kartotherian/err');
const checkType = require('@kartotherian/input-validator');
const core = require('./core');
const { XmlLoader } = require('@kartotherian/module-loader');

Promise.promisifyAll(fs);

// constant
const sourceByRefProtocol = 'sourceref:';

function Sources() {
  this._variables = {};
  this._sources = {};

  // Set up a ref protocol resolver - this way its enough to specify the source
  // by sourceref:///?ref=sourceID URL, instead of a full source URL.
  const self = this;

  // ATTENTION: this must be a non-anonymous function, as it is a constructor
  core.tilelive.protocols[sourceByRefProtocol] = function tileLiveProtocols(uri, callback) {
    Promise.try(() => {
      // eslint-disable-next-line no-param-reassign
      uri = checkType.normalizeUrl(uri);
      if (!uri.query.ref) {
        throw new Err('ref uri parameter is not set');
      }
      return self.getHandlerById(uri.query.ref);
    }).nodeify(callback);
  };
}

/**
 * Regex string to match proper source IDs
 * @type {string}
 */
Sources.sourceIdReStr = '[A-Za-z][-A-Za-z0-9_]*';

/**
 * Precompiled regex to match source ID as a full string
 * @type {RegExp}
 */
Sources.sourceIdRe = new RegExp(`^${Sources.sourceIdReStr}$`);

/**
 * Source ID must begin with a letter, and may contain letters, digits, and underscores
 */
Sources.isValidSourceId = function isValidSourceId(sourceId) {
  return typeof sourceId === 'string' && sourceId.length > 0 && Sources.sourceIdRe.test(sourceId);
};

/**
 * Load both variables and sources in one function call
 * @param {object} conf
 * @param {object|string} conf.modules
 * @param {object|string} conf.variables
 * @param {object|string} conf.sources
 * @returns {*}
 */
Sources.prototype.init = function init(conf) {
  const self = this;
  return Promise.try(() => {
    if (!conf.modules) {
      throw new Err('Configuration must have a "modules" parameter listing all ' +
                'Tilelive/Kartotherian NPM plugin modules');
    }
    _.each(conf.modules, core.registerTileliveModule);
    self.loadVariablesAsync(self._localOrExternalDataAsync(conf.variables, 'variables'));
  }).then(() =>
    self.loadSourcesAsync(self._localOrExternalDataAsync(conf.sources, 'sources'))).return(self);
};

/**
 * Load variables (e.g. passwords, etc)
 * @param variables
 * @returns {*}
 */
Sources.prototype.loadVariablesAsync = function loadVariablesAsync(variables) {
  const self = this;
  // TODO: Fix this shadow properly
  // eslint-disable-next-line no-shadow
  return Promise.resolve(variables).then((variables) => {
    self._variables = _.extend(self._variables, variables);
  });
};

Sources.prototype.loadSourcesAsync = function loadSourcesAsync(sources) {
  const self = this;
  // TODO: Fix this shadow properly
  // eslint-disable-next-line no-shadow
  return Promise.resolve(sources).then((sources) => {
    if (!_.isObject(sources) || _.isArray(sources)) {
      throw new Err('Sources must be an object');
    }
    return Promise.each(
      Object.keys(sources),
      key => self._loadSourceAsync(sources[key], key)
    );
  });
};

/**
 * Override top-level values in the info object with the ones
 * from override object, or delete on null
 * @param {object} info
 * @param {object} override
 * @param {object} [source] if given, sets min/max zoom
 * @param {string} [sourceId]
 */
function updateInfo(info, override, source, sourceId) {
  if (source) {
    if (source.minzoom !== undefined) {
      // eslint-disable-next-line no-param-reassign
      info.minzoom = source.minzoom;
    }
    if (source.maxzoom !== undefined) {
      // eslint-disable-next-line no-param-reassign
      info.maxzoom = source.maxzoom;
    }
  }
  if (sourceId !== undefined) {
    // eslint-disable-next-line no-param-reassign
    info.name = sourceId;
  }
  if (override) {
    _.each(override, (v, k) => {
      if (v === null) {
        // When override.key == null, delete that key
        // eslint-disable-next-line no-param-reassign
        delete info[k];
      } else {
        // override info of the parent
        // eslint-disable-next-line no-param-reassign
        info[k] = v;
      }
    });
  }
}

/**
 * Load source from src config
 * @param {object} src
 * @param {string|object} src.uri
 * @param {object} src.params
 * @param {boolean} src.public
 * @param {int} src.minzoom
 * @param {int} src.maxzoom
 * @param {object} src.defaultHeaders
 * @param {object} src.headers
 * @param {string[]} src.formats
 * @param {int[]} src.scales
 * @param {boolean} src.static
 * @param {int} src.maxwidth
 * @param {int} src.maxheight
 * @param {string} src.pathname
 * @param {object|string} src.xml
 * @param {object} src.xmlSetAttrs
 * @param {object} src.xmlSetParams
 * @param {object} src.xmlLayers
 * @param {object} src.xmlExceptLayers
 * @param {object} src.xmlSetDataSource
 * @param {object} src.setInfo
 * @param {object} src.overrideInfo
 * @param sourceId
 * @returns {Promise}
 * @private
 */
Sources.prototype._loadSourceAsync = function _loadSourceAsync(src, sourceId) {
  const self = this;
  return Promise.try(() => {
    if (!Sources.isValidSourceId(sourceId)) {
      throw new Err('sourceId %j must only contain letters and digits', sourceId);
    }
    if (typeof src !== 'object') {
      throw new Err('source %j must be an object', sourceId);
    }

    checkType(src, 'uri', 'string', true, 1);
    const uri = checkType.normalizeUrl(src.uri);

    // These params are stored, but not acted on within core
    // Kartotherian service uses them when handling user's requests
    // If public is not true, the rest of the values are unused
    checkType(src, 'public', 'boolean');
    checkType(src, 'minzoom', 'zoom');
    checkType(src, 'maxzoom', 'zoom');
    checkType(src, 'defaultHeaders', 'object');
    checkType(src, 'headers', 'object');
    checkType(src, 'formats', 'string-array');
    if (checkType(src, 'scales', 'number-array')) {
      // store scales as an array of strings because it
      // must be an exact match - optimizes caching
      if (src.scales.length === 0) {
        // eslint-disable-next-line no-param-reassign
        delete src.scales;
      } else {
        // eslint-disable-next-line no-param-reassign
        src.scales = _.map(src.scales, v => v.toString());
      }
    }
    checkType(src, 'static', 'boolean');
    checkType(src, 'maxwidth', 'integer');
    checkType(src, 'maxheight', 'integer');
    checkType(src, 'setInfo', 'object');
    checkType(src, 'overrideInfo', 'object');

    // Add URI query values, e.g.  ?password=...
    if (checkType(src, 'params', 'object')) {
      _.each(src.params, (v, k) => {
        uri.query[k] = self._resolveValue(v, k);
      });
    }
    // Set URI's path, e.g. /srv/data/myDir
    if (checkType(src, 'pathname', 'object')) {
      uri.pathname = self._resolveValue(src.pathname, 'pathname');
    }
    if (src.xml) {
      const xmlLoader = new XmlLoader(src, self._resolveValue.bind(self), core.log);
      return xmlLoader.load(uri.protocol);
    }
    return uri;
  }).then(uri => core.loadSource(uri)).then((handler) => {
    const info = {
      // This is the only required field per spec, and it can be overwritten
      // https://github.com/mapbox/tilejson-spec
      tilejson: '2.1.0',
    };

    // minzoom/maxzoom is automatically added before
    // setInfo and overrideInfo if setInfo is given
    // but it is added after calling original getInfo() if setInfo is not given
    if (src.setInfo) {
      updateInfo(info, src.setInfo, src, sourceId);
      updateInfo(info, src.overrideInfo);
      return [handler, info];
    }
    return handler.getInfoAsync().then((sourceInfo) => {
      updateInfo(info, sourceInfo);
      updateInfo(info, src.overrideInfo, src, sourceId);
      return [handler, info];
    });
  }).spread((handler, info) => {
    // eslint-disable-next-line no-param-reassign
    handler.getInfo = (callback) => {
      callback(undefined, info);
    };
    // eslint-disable-next-line no-param-reassign
    handler.getInfoAsync = Promise.promisify(handler.getInfo);

    // eslint-disable-next-line no-param-reassign
    src.getHandler = () => handler;
  })
    .catch((err) => {
      // eslint-disable-next-line no-param-reassign
      err.message = `Unable to create source "${sourceId}"${err.message || ''}`;
      core.log('error', err);
      // eslint-disable-next-line no-param-reassign
      src.isDisabled = err || true;
    })
    .then(() => {
      self._sources[sourceId] = src;
    });
};

Sources.prototype._localOrExternalDataAsync = function _localOrExternalDataAsync(values, name) {
  return Promise.try(() => {
    if (values === undefined) {
      core.log('info', `${name} is not set in the config file`);
      return {};
    }
    if (!Array.isArray(values)) {
      // eslint-disable-next-line no-param-reassign
      values = [values];
    }
    // eslint-disable-next-line no-param-reassign
    values = _.map(values, (value) => {
      if (typeof value === 'string') {
        const path = pathLib.resolve(core.getAppRootDir(), value);
        core.log('info', `Loading ${name} from ${path}`);
        return fs
          .readFileAsync(path)
          .then(yaml.safeLoad);
      } else if (typeof value === 'object' && !Array.isArray(value)) {
        core.log('info', `Loading ${name} from the config file`);
        return value;
      }
      throw new Err('config.%s must be an object or filename,' +
                    ' or an array of objects and/or filenames', name);
    });

    return Promise.reduce(values, _.extend, {});
  });
};

/**
 * Resolves a config value into a string. If value is an object with exactly one
 * known key, it resolves it accordingly, otherwise it returns the value as-is.
 * For an array, each value is resolved separately.
 *
 * Supported keys are:
 *
 *  - npm (alias: npmpath): value should be the name npm module, it returns the path
 *
 *  - ref: value should be the name of a source defined in the sources section
 *         of the configuration file
 *
 *  - var: value should be the name of a variable defined in the variables section
 *         of the configuration file
 *
 *  - env: value should be the name of an environment variable
 *
 *  - loader (alias: npmloader): value should be the name of an npm module,
 *                               it returns the loaded module
 *
 * @param {*} value
 * @param {string} valueName
 * @param {boolean} allowLoader
 * @returns {string|object}
 * @private
 */
Sources.prototype._resolveValue = function(value, valueName, allowLoader) {
    let self = this;
    if (typeof value !== 'object') {
        return value;
    }
    if (Array.isArray(value)) {
        return _.map(value, v => self._resolveValue(v, valueName, allowLoader));
    }

    let keys = _.keys(value);
    if ( keys.length === 1 ) {
        let firstKey = keys[0];
        let firstValue = value[firstKey];
        switch (firstKey) {
            case 'npm':
            case 'npmpath':
                return this.getModulePath(firstValue);
            case 'ref':
                return this._getSourceUri(firstValue);
            case 'var':
                return this._getVariable(firstValue);
            case 'env':
                return this._getEnvVariable(firstValue);
            case 'loader':
            case 'npmloader':
                if (allowLoader) {
                    return this._getLoader(firstValue);
                }
        }
    }

    return value;
};

Sources.prototype.getSourceById = function getSourceById(sourceId, dontThrow, allowDisabled) {
  if (
    !Sources.isValidSourceId(sourceId) ||
    !Object.prototype.hasOwnProperty.call(this._sources, sourceId)
  ) {
    if (dontThrow) {
      return undefined;
    }
    throw new Err('Unknown source %j', sourceId);
  }

  const source = this._sources[sourceId];

  if (!allowDisabled && source.isDisabled) {
    if (dontThrow) {
      return undefined;
    }
    throw new Err('Source %j is disabled, possibly due to loading errors', sourceId);
  }

  return source;
};

Sources.prototype.getHandlerById = function getHandlerById(sourceId, dontThrow) {
  return this.getSourceById(sourceId, dontThrow).getHandler();
};

Sources.prototype.getSourceConfigs = function getSourceConfigs() {
  return this._sources;
};

Sources.prototype.getVariables = function getVariables() {
  return this._variables;
};

Sources.prototype._getSourceUri = function _getSourceUri(sourceId) {
  this.getSourceById(sourceId); // assert it exists
  return `${sourceByRefProtocol}///?ref=${sourceId}`;
};

Sources.prototype._getVariable = function _getVariable(name) {
  if (this._variables[name] === undefined) {
    throw new Err('Variable %j is not defined', name);
  }
  return this._variables[name];
};

Sources.prototype._getEnvVariable = function _getEnvVariable(name) {
  if (process.env[name] === undefined) {
    throw new Err('Environment variable %j is not set', name);
  }
  return process.env[name];
};

Sources.prototype.getModulePath = function getModulePath(moduleName) {
  let params;
  if (Array.isArray(moduleName)) {
    params = moduleName.slice(1);
    // eslint-disable-next-line prefer-destructuring,no-param-reassign
    moduleName = moduleName[0];
  } else if (typeof moduleName === 'string') {
    if (moduleName.indexOf('/') > -1) {
      return moduleName; // Already resolved path
    }
    params = [];
  } else {
    throw new Err('npm module name key must be a string or an array');
  }
  // remove the name of the startup js file, and use it as path
  params.unshift(pathLib.dirname(core.resolveModule(moduleName)));
  return pathLib.resolve.apply(undefined, params);
};

/**
 * Given a module name, require that module in the context of the main app.
 * Returns loaded module and the optional additional parameters.
 * @param {string|string[]} moduleName
 * @returns {object}
 * @private
 */
Sources.prototype._getLoader = function _getLoader(moduleName) {
  let params;
  if (typeof moduleName === 'string') {
    params = [];
  } else if (Array.isArray(moduleName)) {
    params = moduleName.slice(1);
    // eslint-disable-next-line prefer-destructuring,no-param-reassign
    moduleName = moduleName[0];
  }
  if (typeof moduleName !== 'string') {
    throw new Err('loader npm module name key must be a string or an array of strings');
  }
  // eslint-disable-next-line global-require,import/no-dynamic-require
  const module = require(core.resolveModule(moduleName));
  if (typeof module !== 'function') {
    throw new Err(
      'loader npm module %j is expected to return a function when loaded',
      moduleName
    );
  }
  return {
    module,
    params,
  };
};

module.exports = Sources;
<commit_msg>Rebase on top of add-linting<commit_after>

const pathLib = require('path');
const _ = require('underscore');
const Promise = require('bluebird');
const yaml = require('js-yaml');
const fs = require('fs');
const Err = require('@kartotherian/err');
const checkType = require('@kartotherian/input-validator');
const core = require('./core');
const { XmlLoader } = require('@kartotherian/module-loader');

Promise.promisifyAll(fs);

// constant
const sourceByRefProtocol = 'sourceref:';

function Sources() {
  this._variables = {};
  this._sources = {};

  // Set up a ref protocol resolver - this way its enough to specify the source
  // by sourceref:///?ref=sourceID URL, instead of a full source URL.
  const self = this;

  // ATTENTION: this must be a non-anonymous function, as it is a constructor
  core.tilelive.protocols[sourceByRefProtocol] = function tileLiveProtocols(uri, callback) {
    Promise.try(() => {
      // eslint-disable-next-line no-param-reassign
      uri = checkType.normalizeUrl(uri);
      if (!uri.query.ref) {
        throw new Err('ref uri parameter is not set');
      }
      return self.getHandlerById(uri.query.ref);
    }).nodeify(callback);
  };
}

/**
 * Regex string to match proper source IDs
 * @type {string}
 */
Sources.sourceIdReStr = '[A-Za-z][-A-Za-z0-9_]*';

/**
 * Precompiled regex to match source ID as a full string
 * @type {RegExp}
 */
Sources.sourceIdRe = new RegExp(`^${Sources.sourceIdReStr}$`);

/**
 * Source ID must begin with a letter, and may contain letters, digits, and underscores
 */
Sources.isValidSourceId = function isValidSourceId(sourceId) {
  return typeof sourceId === 'string' && sourceId.length > 0 && Sources.sourceIdRe.test(sourceId);
};

/**
 * Load both variables and sources in one function call
 * @param {object} conf
 * @param {object|string} conf.modules
 * @param {object|string} conf.variables
 * @param {object|string} conf.sources
 * @returns {*}
 */
Sources.prototype.init = function init(conf) {
  const self = this;
  return Promise.try(() => {
    if (!conf.modules) {
      throw new Err('Configuration must have a "modules" parameter listing all ' +
                'Tilelive/Kartotherian NPM plugin modules');
    }
    _.each(conf.modules, core.registerTileliveModule);
    self.loadVariablesAsync(self._localOrExternalDataAsync(conf.variables, 'variables'));
  }).then(() =>
    self.loadSourcesAsync(self._localOrExternalDataAsync(conf.sources, 'sources'))).return(self);
};

/**
 * Load variables (e.g. passwords, etc)
 * @param variables
 * @returns {*}
 */
Sources.prototype.loadVariablesAsync = function loadVariablesAsync(variables) {
  const self = this;
  // TODO: Fix this shadow properly
  // eslint-disable-next-line no-shadow
  return Promise.resolve(variables).then((variables) => {
    self._variables = _.extend(self._variables, variables);
  });
};

Sources.prototype.loadSourcesAsync = function loadSourcesAsync(sources) {
  const self = this;
  // TODO: Fix this shadow properly
  // eslint-disable-next-line no-shadow
  return Promise.resolve(sources).then((sources) => {
    if (!_.isObject(sources) || _.isArray(sources)) {
      throw new Err('Sources must be an object');
    }
    return Promise.each(
      Object.keys(sources),
      key => self._loadSourceAsync(sources[key], key)
    );
  });
};

/**
 * Override top-level values in the info object with the ones
 * from override object, or delete on null
 * @param {object} info
 * @param {object} override
 * @param {object} [source] if given, sets min/max zoom
 * @param {string} [sourceId]
 */
function updateInfo(info, override, source, sourceId) {
  if (source) {
    if (source.minzoom !== undefined) {
      // eslint-disable-next-line no-param-reassign
      info.minzoom = source.minzoom;
    }
    if (source.maxzoom !== undefined) {
      // eslint-disable-next-line no-param-reassign
      info.maxzoom = source.maxzoom;
    }
  }
  if (sourceId !== undefined) {
    // eslint-disable-next-line no-param-reassign
    info.name = sourceId;
  }
  if (override) {
    _.each(override, (v, k) => {
      if (v === null) {
        // When override.key == null, delete that key
        // eslint-disable-next-line no-param-reassign
        delete info[k];
      } else {
        // override info of the parent
        // eslint-disable-next-line no-param-reassign
        info[k] = v;
      }
    });
  }
}

/**
 * Load source from src config
 * @param {object} src
 * @param {string|object} src.uri
 * @param {object} src.params
 * @param {boolean} src.public
 * @param {int} src.minzoom
 * @param {int} src.maxzoom
 * @param {object} src.defaultHeaders
 * @param {object} src.headers
 * @param {string[]} src.formats
 * @param {int[]} src.scales
 * @param {boolean} src.static
 * @param {int} src.maxwidth
 * @param {int} src.maxheight
 * @param {string} src.pathname
 * @param {object|string} src.xml
 * @param {object} src.xmlSetAttrs
 * @param {object} src.xmlSetParams
 * @param {object} src.xmlLayers
 * @param {object} src.xmlExceptLayers
 * @param {object} src.xmlSetDataSource
 * @param {object} src.setInfo
 * @param {object} src.overrideInfo
 * @param sourceId
 * @returns {Promise}
 * @private
 */
Sources.prototype._loadSourceAsync = function _loadSourceAsync(src, sourceId) {
  const self = this;
  return Promise.try(() => {
    if (!Sources.isValidSourceId(sourceId)) {
      throw new Err('sourceId %j must only contain letters and digits', sourceId);
    }
    if (typeof src !== 'object') {
      throw new Err('source %j must be an object', sourceId);
    }

    checkType(src, 'uri', 'string', true, 1);
    const uri = checkType.normalizeUrl(src.uri);

    // These params are stored, but not acted on within core
    // Kartotherian service uses them when handling user's requests
    // If public is not true, the rest of the values are unused
    checkType(src, 'public', 'boolean');
    checkType(src, 'minzoom', 'zoom');
    checkType(src, 'maxzoom', 'zoom');
    checkType(src, 'defaultHeaders', 'object');
    checkType(src, 'headers', 'object');
    checkType(src, 'formats', 'string-array');
    if (checkType(src, 'scales', 'number-array')) {
      // store scales as an array of strings because it
      // must be an exact match - optimizes caching
      if (src.scales.length === 0) {
        // eslint-disable-next-line no-param-reassign
        delete src.scales;
      } else {
        // eslint-disable-next-line no-param-reassign
        src.scales = _.map(src.scales, v => v.toString());
      }
    }
    checkType(src, 'static', 'boolean');
    checkType(src, 'maxwidth', 'integer');
    checkType(src, 'maxheight', 'integer');
    checkType(src, 'setInfo', 'object');
    checkType(src, 'overrideInfo', 'object');

    // Add URI query values, e.g.  ?password=...
    if (checkType(src, 'params', 'object')) {
      _.each(src.params, (v, k) => {
        uri.query[k] = self._resolveValue(v, k);
      });
    }
    // Set URI's path, e.g. /srv/data/myDir
    if (checkType(src, 'pathname', 'object')) {
      uri.pathname = self._resolveValue(src.pathname, 'pathname');
    }
    if (src.xml) {
      const xmlLoader = new XmlLoader(src, self._resolveValue.bind(self), core.log);
      return xmlLoader.load(uri.protocol);
    }
    return uri;
  }).then(uri => core.loadSource(uri)).then((handler) => {
    const info = {
      // This is the only required field per spec, and it can be overwritten
      // https://github.com/mapbox/tilejson-spec
      tilejson: '2.1.0',
    };

    // minzoom/maxzoom is automatically added before
    // setInfo and overrideInfo if setInfo is given
    // but it is added after calling original getInfo() if setInfo is not given
    if (src.setInfo) {
      updateInfo(info, src.setInfo, src, sourceId);
      updateInfo(info, src.overrideInfo);
      return [handler, info];
    }
    return handler.getInfoAsync().then((sourceInfo) => {
      updateInfo(info, sourceInfo);
      updateInfo(info, src.overrideInfo, src, sourceId);
      return [handler, info];
    });
  }).spread((handler, info) => {
    // eslint-disable-next-line no-param-reassign
    handler.getInfo = (callback) => {
      callback(undefined, info);
    };
    // eslint-disable-next-line no-param-reassign
    handler.getInfoAsync = Promise.promisify(handler.getInfo);

    // eslint-disable-next-line no-param-reassign
    src.getHandler = () => handler;
  })
    .catch((err) => {
      // eslint-disable-next-line no-param-reassign
      err.message = `Unable to create source "${sourceId}"${err.message || ''}`;
      core.log('error', err);
      // eslint-disable-next-line no-param-reassign
      src.isDisabled = err || true;
    })
    .then(() => {
      self._sources[sourceId] = src;
    });
};

Sources.prototype._localOrExternalDataAsync = function _localOrExternalDataAsync(values, name) {
  return Promise.try(() => {
    if (values === undefined) {
      core.log('info', `${name} is not set in the config file`);
      return {};
    }
    if (!Array.isArray(values)) {
      // eslint-disable-next-line no-param-reassign
      values = [values];
    }
    // eslint-disable-next-line no-param-reassign
    values = _.map(values, (value) => {
      if (typeof value === 'string') {
        const path = pathLib.resolve(core.getAppRootDir(), value);
        core.log('info', `Loading ${name} from ${path}`);
        return fs
          .readFileAsync(path)
          .then(yaml.safeLoad);
      } else if (typeof value === 'object' && !Array.isArray(value)) {
        core.log('info', `Loading ${name} from the config file`);
        return value;
      }
      throw new Err('config.%s must be an object or filename,' +
                    ' or an array of objects and/or filenames', name);
    });

    return Promise.reduce(values, _.extend, {});
  });
};

/**
 * Resolves a config value into a string. If value is an object with exactly one
 * known key, it resolves it accordingly, otherwise it returns the value as-is.
 * For an array, each value is resolved separately.
 *
 * Supported keys are:
 *
 *  - npm (alias: npmpath): value should be the name npm module, it returns the path
 *
 *  - ref: value should be the name of a source defined in the sources section
 *         of the configuration file
 *
 *  - var: value should be the name of a variable defined in the variables section
 *         of the configuration file
 *
 *  - env: value should be the name of an environment variable
 *
 *  - loader (alias: npmloader): value should be the name of an npm module,
 *                               it returns the loaded module
 *
 * @param {*} value
 * @param {string} valueName
 * @param {boolean} allowLoader
 * @returns {string|object}
 * @private
 */
Sources.prototype._resolveValue = function _resolveValue(value, valueName, allowLoader) {
  const self = this;
  if (typeof value !== 'object') {
    return value;
  }
  if (Array.isArray(value)) {
    return _.map(value, v => self._resolveValue(v, valueName, allowLoader));
  }

  const keys = _.keys(value);
  if (keys.length === 1) {
    const firstKey = keys[0];
    const firstValue = value[firstKey];
    switch (firstKey) {
      case 'npm':
      case 'npmpath':
        return this.getModulePath(firstValue);
      case 'ref':
        return this._getSourceUri(firstValue);
      case 'var':
        return this._getVariable(firstValue);
      case 'env':
        return this._getEnvVariable(firstValue);
      case 'loader':
      case 'npmloader':
        if (allowLoader) {
          return this._getLoader(firstValue);
        }
        break;
      default:
        return value;
    }
  }

  return value;
};

Sources.prototype.getSourceById = function getSourceById(sourceId, dontThrow, allowDisabled) {
  if (
    !Sources.isValidSourceId(sourceId) ||
    !Object.prototype.hasOwnProperty.call(this._sources, sourceId)
  ) {
    if (dontThrow) {
      return undefined;
    }
    throw new Err('Unknown source %j', sourceId);
  }

  const source = this._sources[sourceId];

  if (!allowDisabled && source.isDisabled) {
    if (dontThrow) {
      return undefined;
    }
    throw new Err('Source %j is disabled, possibly due to loading errors', sourceId);
  }

  return source;
};

Sources.prototype.getHandlerById = function getHandlerById(sourceId, dontThrow) {
  return this.getSourceById(sourceId, dontThrow).getHandler();
};

Sources.prototype.getSourceConfigs = function getSourceConfigs() {
  return this._sources;
};

Sources.prototype.getVariables = function getVariables() {
  return this._variables;
};

Sources.prototype._getSourceUri = function _getSourceUri(sourceId) {
  this.getSourceById(sourceId); // assert it exists
  return `${sourceByRefProtocol}///?ref=${sourceId}`;
};

Sources.prototype._getVariable = function _getVariable(name) {
  if (this._variables[name] === undefined) {
    throw new Err('Variable %j is not defined', name);
  }
  return this._variables[name];
};

Sources.prototype._getEnvVariable = function _getEnvVariable(name) {
  if (process.env[name] === undefined) {
    throw new Err('Environment variable %j is not set', name);
  }
  return process.env[name];
};

Sources.prototype.getModulePath = function getModulePath(moduleName) {
  let params;
  if (Array.isArray(moduleName)) {
    params = moduleName.slice(1);
    // eslint-disable-next-line prefer-destructuring,no-param-reassign
    moduleName = moduleName[0];
  } else if (typeof moduleName === 'string') {
    if (moduleName.indexOf('/') > -1) {
      return moduleName; // Already resolved path
    }
    params = [];
  } else {
    throw new Err('npm module name key must be a string or an array');
  }
  // remove the name of the startup js file, and use it as path
  params.unshift(pathLib.dirname(core.resolveModule(moduleName)));
  return pathLib.resolve.apply(undefined, params);
};

/**
 * Given a module name, require that module in the context of the main app.
 * Returns loaded module and the optional additional parameters.
 * @param {string|string[]} moduleName
 * @returns {object}
 * @private
 */
Sources.prototype._getLoader = function _getLoader(moduleName) {
  let params;
  if (typeof moduleName === 'string') {
    params = [];
  } else if (Array.isArray(moduleName)) {
    params = moduleName.slice(1);
    // eslint-disable-next-line prefer-destructuring,no-param-reassign
    moduleName = moduleName[0];
  }
  if (typeof moduleName !== 'string') {
    throw new Err('loader npm module name key must be a string or an array of strings');
  }
  // eslint-disable-next-line global-require,import/no-dynamic-require
  const module = require(core.resolveModule(moduleName));
  if (typeof module !== 'function') {
    throw new Err(
      'loader npm module %j is expected to return a function when loaded',
      moduleName
    );
  }
  return {
    module,
    params,
  };
};

module.exports = Sources;
<|endoftext|>


<commit_before>// This file is used when a user tries to export the entire CryptDrive.
// Pads from the code app will be exported using this format instead of plain text.
define([
    '/customize/messages.js',
], function (Messages) {
    var module = {};

    var copyObject = function (obj) {
        return JSON.parse(JSON.stringify(obj));
    };

    module.getCSV = function (content) {
        var data = copyObject(content);
        var res = '';

        var count = {};
        data.rowsOrder.forEach(function (rId) {
            var c = Object.keys(data.cells)
                .filter(function (k) {
                    return k.indexOf(rId) !== -1 && data.cells[k] === 1;
                }).length;
            count[rId] = c;
        });

        var escapeStr = function (str) {
            return '"' + str.replace(/"/g, '""') + '"';
        };

        [null].concat(data.rowsOrder).forEach(function (rowId, i) {
            [null].concat(data.colsOrder).forEach(function (colId, j) {
                // thead
                if (i === 0) {
                    if (j === 0) { res += ','; return; }
                    if (!colId) { throw new Error("Invalid data"); }
                    res += escapeStr(data.cols[colId] || Messages.anonymous) + ',';
                    return;
                }
                // tbody
                if (!rowId) { throw new Error("Invalid data"); }
                if (j === 0) {
                    res += escapeStr(data.rows[rowId] || Messages.poll_optionPlaceholder) + ',';
                    return;
                }
                if (!colId) { throw new Error("Invalid data"); }
                res += (data.cells[colId + '_' + rowId] || 3) + ',';
            });
            // last column: total
            // thead
            if (i === 0) {
                res += escapeStr(Messages.poll_total) + '\n';
                return;
            }
            // tbody
            if (!rowId) { throw new Error("Invalid data"); }
            res += count[rowId] || '?';
            res += '\n';
        });

        return res;
    };
    module.main = function (userDoc, cb) {
        var content = userDoc.content;
        var csv;
        try {
            csv = module.getCSV(content);
        } catch (e) {
            console.error(e);
            var blob2 = new Blob([JSON.stringify(content, 0, 2)], {
                type: 'application/json',
            });
            return void cb(blob2, true);
        }
        var blob = new Blob([csv], {type: "application/csv;charset=utf-8"});
        cb(blob);
    };

    return module;
});

<commit_msg>Fix export with old polls<commit_after>// This file is used when a user tries to export the entire CryptDrive.
// Pads from the code app will be exported using this format instead of plain text.
define([
    '/customize/messages.js',
], function (Messages) {
    var module = {};

    var copyObject = function (obj) {
        return JSON.parse(JSON.stringify(obj));
    };

    module.getCSV = function (content) {
        var data = copyObject(content);
        var res = '';

        var count = {};
        data.rowsOrder.forEach(function (rId) {
            var c = Object.keys(data.cells)
                .filter(function (k) {
                    return k.indexOf(rId) !== -1 && data.cells[k] === 1;
                }).length;
            count[rId] = c;
        });

        var escapeStr = function (str) {
            return '"' + str.replace(/"/g, '""') + '"';
        };

        [null].concat(data.rowsOrder).forEach(function (rowId, i) {
            [null].concat(data.colsOrder).forEach(function (colId, j) {
                // thead
                if (i === 0) {
                    if (j === 0) { res += ','; return; }
                    if (!colId) { throw new Error("Invalid data"); }
                    res += escapeStr(data.cols[colId] || Messages.anonymous) + ',';
                    return;
                }
                // tbody
                if (!rowId) { throw new Error("Invalid data"); }
                if (j === 0) {
                    res += escapeStr(data.rows[rowId] || Messages.poll_optionPlaceholder) + ',';
                    return;
                }
                if (!colId) { throw new Error("Invalid data"); }
                res += (data.cells[colId + '_' + rowId] || 3) + ',';
            });
            // last column: total
            // thead
            if (i === 0) {
                res += escapeStr(Messages.poll_total) + '\n';
                return;
            }
            // tbody
            if (!rowId) { throw new Error("Invalid data"); }
            res += count[rowId] || '?';
            res += '\n';
        });

        return res;
    };
    module.main = function (userDoc, cb) {
        var content = userDoc.content || userDoc.table;
        var csv;
        try {
            csv = module.getCSV(content);
        } catch (e) {
            console.error(e);
            var blob2 = new Blob([JSON.stringify(content, 0, 2)], {
                type: 'application/json',
            });
            return void cb(blob2, true);
        }
        var blob = new Blob([csv], {type: "application/csv;charset=utf-8"});
        cb(blob);
    };

    return module;
});

<|endoftext|>


<commit_before>export default function tagIcon(tag, attrs = {}, settings = {}) {
  const hasIcon = tag && tag.icon();
  const useColor = settings.useColor || true;

  attrs.className = hasIcon ? 'icon ' + tag.icon() + ' ' + (attrs.className || '') : 'icon TagIcon ' + (attrs.className || '');

  if (tag) {
    attrs.style = attrs.style || {};

    if (hasIcon) {
      attrs.style.color = useColor ? tag.color() : '';
    } else {
      attrs.style.backgroundColor = tag.color();
    }

  } else {
    attrs.className += ' untagged';
  }

  return hasIcon ? <i {...attrs}/> : <span {...attrs}/>;
}
<commit_msg>Fix default value for useColor overriding falsy values (#63)<commit_after>export default function tagIcon(tag, attrs = {}, settings = {}) {
  const hasIcon = tag && tag.icon();
  const { useColor = true } = settings;

  attrs.className = hasIcon ? 'icon ' + tag.icon() + ' ' + (attrs.className || '') : 'icon TagIcon ' + (attrs.className || '');

  if (tag) {
    attrs.style = attrs.style || {};

    if (hasIcon) {
      attrs.style.color = useColor ? tag.color() : '';
    } else {
      attrs.style.backgroundColor = tag.color();
    }

  } else {
    attrs.className += ' untagged';
  }

  return hasIcon ? <i {...attrs}/> : <span {...attrs}/>;
}
<|endoftext|>