<commit_before>/**
 * `rawlist` type prompt
 */

var _ = require("lodash");
var util = require("util");
var clc = require("cli-color");
var Base = require("./base");

/**
 * Module exports
 */

module.exports = Prompt;

/**
 * Constructor
 */

function Prompt() {
  Base.apply( this, arguments );

  if (!this.opt.choices) {
    this.throwParamError("choices");
  }

  this.selected = 0;
  this.rawDefault = 0;

  var def = this.opt.default;
  if ( _.isNumber(def) && def >= 0 && def < this.opt.choices.length ) {
    this.selected = this.rawDefault = def;
  }

  // Make sure no default is set (so it won't be printed)
  this.opt.default = null;

  return this;
}
util.inherits( Prompt, Base );

/**
 * Start the Inquiry session
 * @param  {Function} cb      Callback when prompt is done
 * @return {this}
 */

Prompt.prototype._run = function( cb ) {
  this.done = cb;

  // Save user answer and update prompt to show selected option.
  this.rl.on( "line", this.onSubmit.bind(this) );
  this.rl.on( "keypress", this.onKeypress.bind(this) );

  // Init the prompt
  this.render();

  return this;
};

/**
 * Render the prompt to screen
 * @return {Prompt} self
 */

Prompt.prototype.render = function() {

  // Render question
  var message    = this.getQuestion();
  var choicesStr = this.getChoices();

  if ( this.answered ) {
    message += clc.cyan(this.opt.choices[this.selected].name) + "\n";
  } else {
    message += choicesStr;
    message += "\n  Answer: ";
  }

  var msgLines = message.split(/\n/);
  this.height  = msgLines.length;

  this.rl.setPrompt( _.last(msgLines) );
  this.write( message );

  return this;
};

/**
 * Generate the prompt choices string
 * @return {String}  Choices string
 */

Prompt.prototype.getChoices = function() {
  var output = "";

  this.opt.choices.forEach(function( choice, i ) {
    var display = "\n  " + (i + 1) + ") " + choice.name;
    if ( i === this.selected ) {
      display = clc.cyan( display );
    }
    output += display;
  }.bind(this));

  return output;
};

/**
 * When user press `enter` key
 */

Prompt.prototype.onSubmit = function( input ) {
  if ( input == null || input === "" ) {
    input = this.rawDefault;
  } else {
    input -= 1;
  }

  // Input is valid
  if ( this.opt.choices[input] != null ) {
    this.answered = true;
    this.selected = input;

    // Re-render prompt
    this.down().clean(2).render();

    this.rl.removeAllListeners("line");
    this.rl.removeAllListeners("keypress");
    this.done( this.opt.choices[this.selected].value );
    return;
  }

  // Input is invalid
  this
    .error("Please enter a valid index")
    .write( clc.bol(0, true) )
    .clean()
    .render();
};

/**
 * When user press a key
 */

Prompt.prototype.onKeypress = function( s, key ) {
  var input = this.rl.line.length ? Number(this.rl.line) : 1;
  var index = input - 1;
  if ( this.opt.choices[index] ) {
    this.selected = index;
  } else {
    this.selected = undefined;
  }
  this.down().clean(1).render();

  if ( /[a-z0-9]/i.test(s) ) {
    this.rl.output.write( input.toString() );
  } else {
    this.rl.output.write("");
  }
};
<commit_msg>Simplify rawlist autocomplete-ish feature<commit_after>/**
 * `rawlist` type prompt
 */

var _ = require("lodash");
var util = require("util");
var clc = require("cli-color");
var Base = require("./base");

/**
 * Module exports
 */

module.exports = Prompt;

/**
 * Constructor
 */

function Prompt() {
  Base.apply( this, arguments );

  if (!this.opt.choices) {
    this.throwParamError("choices");
  }

  this.selected = 0;
  this.rawDefault = 0;

  var def = this.opt.default;
  if ( _.isNumber(def) && def >= 0 && def < this.opt.choices.length ) {
    this.selected = this.rawDefault = def;
  }

  // Make sure no default is set (so it won't be printed)
  this.opt.default = null;

  return this;
}
util.inherits( Prompt, Base );

/**
 * Start the Inquiry session
 * @param  {Function} cb      Callback when prompt is done
 * @return {this}
 */

Prompt.prototype._run = function( cb ) {
  this.done = cb;

  // Save user answer and update prompt to show selected option.
  this.rl.on( "line", this.onSubmit.bind(this) );
  this.rl.on( "keypress", this.onKeypress.bind(this) );

  // Init the prompt
  this.render();

  return this;
};

/**
 * Render the prompt to screen
 * @return {Prompt} self
 */

Prompt.prototype.render = function() {

  // Render question
  var message    = this.getQuestion();
  var choicesStr = this.getChoices();

  if ( this.answered ) {
    message += clc.cyan(this.opt.choices[this.selected].name) + "\n";
  } else {
    message += choicesStr;
    message += "\n  Answer: ";
  }

  var msgLines = message.split(/\n/);
  this.height  = msgLines.length;

  this.rl.setPrompt( _.last(msgLines) );
  this.write( message );

  return this;
};

/**
 * Generate the prompt choices string
 * @return {String}  Choices string
 */

Prompt.prototype.getChoices = function() {
  var output = "";

  this.opt.choices.forEach(function( choice, i ) {
    var display = "\n  " + (i + 1) + ") " + choice.name;
    if ( i === this.selected ) {
      display = clc.cyan( display );
    }
    output += display;
  }.bind(this));

  return output;
};

/**
 * When user press `enter` key
 */

Prompt.prototype.onSubmit = function( input ) {
  if ( input == null || input === "" ) {
    input = this.rawDefault;
  } else {
    input -= 1;
  }

  // Input is valid
  if ( this.opt.choices[input] != null ) {
    this.answered = true;
    this.selected = input;

    // Re-render prompt
    this.down().clean(2).render();

    this.rl.removeAllListeners("line");
    this.rl.removeAllListeners("keypress");
    this.done( this.opt.choices[this.selected].value );
    return;
  }

  // Input is invalid
  this
    .error("Please enter a valid index")
    .write( clc.bol(0, true) )
    .clean()
    .render();
};

/**
 * When user press a key
 */

Prompt.prototype.onKeypress = function( s, key ) {
  var input = this.rl.line.length ? Number(this.rl.line) : 1;
  var index = input - 1;

  if ( this.opt.choices[index] ) {
    this.selected = index;
  } else {
    this.selected = undefined;
  }
  this.down().clean(1).render();

  this.write( this.rl.line );
};
<|endoftext|>


<commit_before>/* eslint-disable no-useless-escape */
/* eslint-disable no-undef */
/* eslint-disable no-unused-vars */

const sum = (array) => {
    if (array.length === 0) {
        return 0;
    }
    // eslint-disable-next-line no-confusing-arrow
    return array.reduce((acum, val) => (!isNaN(val) ? acum + val : 0), 0);
};

const slugify = (text) =>
    text
        .toString()
        .toLowerCase()
        .replace(/\s+/g, '-') // Replace spaces with -
        .replace(/#/g, '%23') // Change all '#' chars with '%23'
        .replace(/\+/g, '%2B') // Replace all '+' with '%2B'
        .replace(/\-\-+/g, '-') // Replace multiple - with single -
        .replace(/^-+/, '') // Trim - from start of text
        .replace(/-+$/, ''); // Trim - from end of text

const saveChart = () => {
    const ctx = document.getElementById('myChart').getContext('2d');
    const tcanvas = document.createElement('canvas');
    const tctx = tcanvas.getContext('2d');
    const filename = document.getElementById('canvas-container');
    tcanvas.width = ctx.canvas.width;
    tcanvas.height = ctx.canvas.height;
    tctx.fillStyle = '#0f229e';
    tctx.fillRect(0, 0, tcanvas.width, tcanvas.height);
    tctx.drawImage(document.getElementById('myChart'), 0, 0);

    const image = tcanvas
        .toDataURL('image/png')
        .replace('image/png', 'image/octet-stream');

    const link = document.createElement('a');
    link.download = `chart-${filename.dataset.username}.jpg`;
    link.href = image;
    link.click();
};

const chart = ({ limit, singleColor }) => {
    const canvasContainer = document.getElementById('canvas-container');
    const color = 'white';
    const username = canvasContainer.dataset.username;
    const languages = JSON.parse(canvasContainer.dataset.languages);
    const colors = singleColor
        ? 'white'
        : JSON.parse(canvasContainer.dataset.colors);
    const keys = Object.keys(languages);
    const values = Object.values(languages);
    if (typeof limit !== 'number') {
        limit = keys.length;
    }
    const labels = keys.slice(0, limit);
    const data = values.slice(0, limit);
    if (keys.length > 20) {
        const valueOthers = sum(values.slice(limit, keys.length));
        labels.push('Others');
        data.push(valueOthers);
    }
    const canvas = document.getElementById('myChart');
    const chart = (window.chart = new Chart(canvas, {
        type: 'bar',
        data: {
            labels,
            datasets: [
                {
                    label: 'Language Stats',
                    backgroundColor: colors,
                    borderColor: 'black',
                    borderWidth: 1,
                    data
                }
            ]
        },
        options: {
            maintainAspectRatio: false,
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Language Stats',
                fontSize: 16,
                fontColor: '#fff',
                position: 'top'
            },
            scales: {
                yAxes: [
                    {
                        ticks: {
                            beginAtZero: true,
                            fontColor: color
                        }
                    }
                ],
                xAxes: [
                    {
                        ticks: {
                            fontColor: color,
                            maxRotation: 90,
                            minRotation: 90
                        }
                    }
                ]
            },
            tooltips: {
                callbacks: {
                    afterBody: () =>
                        `Click to browse repos with the current language by ${username}`
                }
            }
        }
    }));

    const onCanvasClick = (e) => {
        const activeElement = chart.getElementAtEvent(e)[0];

        if (activeElement) {
            let language = chart.data.labels[activeElement._index];
            language = slugify(language);
            const value =
                chart.data.datasets[activeElement._datasetIndex].data[
                    activeElement._index
                ];
            const url = `https://github.com/${username}?tab=repositories&type=&language=${language}`;
            window.open(url, '_blank');
        }
    };
    canvas.addEventListener('click', onCanvasClick);
    return singleColor;
};

const toggleColor = () => {
    window.chart.destroy();
    // eslint-disable-next-line no-undef
    singleColor = chart({ limit: 20, singleColor: !singleColor });
    const btn = document.getElementById('btn-toggle');

    btn.classList.toggle('active');
    btn.classList.toggle('inactive');
};

module.exports = { chart, toggleColor, saveChart, sum, slugify };
<commit_msg>revert: fix undefined module on plainjs-based util<commit_after>/* eslint-disable no-useless-escape */
/* eslint-disable no-undef */
/* eslint-disable no-unused-vars */

const sum = (array) => {
    if (array.length === 0) {
        return 0;
    }
    // eslint-disable-next-line no-confusing-arrow
    return array.reduce((acum, val) => (!isNaN(val) ? acum + val : 0), 0);
};

const slugify = (text) =>
    text
        .toString()
        .toLowerCase()
        .replace(/\s+/g, '-') // Replace spaces with -
        .replace(/#/g, '%23') // Change all '#' chars with '%23'
        .replace(/\+/g, '%2B') // Replace all '+' with '%2B'
        .replace(/\-\-+/g, '-') // Replace multiple - with single -
        .replace(/^-+/, '') // Trim - from start of text
        .replace(/-+$/, ''); // Trim - from end of text

const saveChart = () => {
    const ctx = document.getElementById('myChart').getContext('2d');
    const tcanvas = document.createElement('canvas');
    const tctx = tcanvas.getContext('2d');
    const filename = document.getElementById('canvas-container');
    tcanvas.width = ctx.canvas.width;
    tcanvas.height = ctx.canvas.height;
    tctx.fillStyle = '#0f229e';
    tctx.fillRect(0, 0, tcanvas.width, tcanvas.height);
    tctx.drawImage(document.getElementById('myChart'), 0, 0);

    const image = tcanvas
        .toDataURL('image/png')
        .replace('image/png', 'image/octet-stream');

    const link = document.createElement('a');
    link.download = `chart-${filename.dataset.username}.jpg`;
    link.href = image;
    link.click();
};

const chart = ({ limit, singleColor }) => {
    const canvasContainer = document.getElementById('canvas-container');
    const color = 'white';
    const username = canvasContainer.dataset.username;
    const languages = JSON.parse(canvasContainer.dataset.languages);
    const colors = singleColor
        ? 'white'
        : JSON.parse(canvasContainer.dataset.colors);
    const keys = Object.keys(languages);
    const values = Object.values(languages);
    if (typeof limit !== 'number') {
        limit = keys.length;
    }
    const labels = keys.slice(0, limit);
    const data = values.slice(0, limit);
    if (keys.length > 20) {
        const valueOthers = sum(values.slice(limit, keys.length));
        labels.push('Others');
        data.push(valueOthers);
    }
    const canvas = document.getElementById('myChart');
    const chart = (window.chart = new Chart(canvas, {
        type: 'bar',
        data: {
            labels,
            datasets: [
                {
                    label: 'Language Stats',
                    backgroundColor: colors,
                    borderColor: 'black',
                    borderWidth: 1,
                    data
                }
            ]
        },
        options: {
            maintainAspectRatio: false,
            legend: {
                display: false
            },
            title: {
                display: true,
                text: 'Language Stats',
                fontSize: 16,
                fontColor: '#fff',
                position: 'top'
            },
            scales: {
                yAxes: [
                    {
                        ticks: {
                            beginAtZero: true,
                            fontColor: color
                        }
                    }
                ],
                xAxes: [
                    {
                        ticks: {
                            fontColor: color,
                            maxRotation: 90,
                            minRotation: 90
                        }
                    }
                ]
            },
            tooltips: {
                callbacks: {
                    afterBody: () =>
                        `Click to browse repos with the current language by ${username}`
                }
            }
        }
    }));

    const onCanvasClick = (e) => {
        const activeElement = chart.getElementAtEvent(e)[0];

        if (activeElement) {
            let language = chart.data.labels[activeElement._index];
            language = slugify(language);
            const value =
                chart.data.datasets[activeElement._datasetIndex].data[
                    activeElement._index
                ];
            const url = `https://github.com/${username}?tab=repositories&type=&language=${language}`;
            window.open(url, '_blank');
        }
    };
    canvas.addEventListener('click', onCanvasClick);
    return singleColor;
};

const toggleColor = () => {
    window.chart.destroy();
    // eslint-disable-next-line no-undef
    singleColor = chart({ limit: 20, singleColor: !singleColor });
    const btn = document.getElementById('btn-toggle');

    btn.classList.toggle('active');
    btn.classList.toggle('inactive');
};

(typeof module !== 'undefined' && module !== null ? module : {}).exports = { chart, toggleColor, saveChart, sum, slugify };
<|endoftext|>


<commit_before>var JSONSaver = require("../jsonsaver");

var QuoteServer = module.exports = function(filename) {
    this.filename = filename;
    this.changed = false;
    this.loaded = false;
    this.db = new JSONSaver(filename);
};

QuoteServer.prototype.save = function(nick, quote) {
    if (this.db.object.quotes.nick) {
        this.db.object.quotes.nick.push(quote);
    }
    else {
        this.db.object.quotes[nick] = [quote];
    }
    this.db.activity();
};

QuoteServer.prototype.random = function(nick) {
    var nick_quotes = this.db.object.quotes[nick];
    var random_number = Math.floor(Math.random() * nick_quotes.length);
    return nick_quotes[random_number];
};<commit_msg>Fixed multiple quotes and added toLowerCase<commit_after>var JSONSaver = require("../jsonsaver");

var QuoteServer = module.exports = function(filename) {
    this.filename = filename;
    this.changed = false;
    this.loaded = false;
    this.db = new JSONSaver(filename);
};

QuoteServer.prototype.save = function(nick, quote) {
    nick = nick.toLowerCase();
    if (this.db.object.quotes[nick]) {
        this.db.object.quotes[nick].push(quote);
    }
    else {
        this.db.object.quotes[nick] = [quote];
    }
    this.db.activity();
};

QuoteServer.prototype.random = function(nick) {
    nick = nick.toLowerCase();
    var nick_quotes = this.db.object.quotes[nick];
    var random_number = Math.floor(Math.random() * nick_quotes.length);
    return nick_quotes[random_number];
};<|endoftext|>


<commit_before>//= require vendor/powertip/jquery.powertip

//= require vendor/bootstrap/bootstrap
//= require babelfish-runtime
//= require pointer
//= require vendor/history/history.adapter.jquery
//= require vendor/history/history
//= require vendor/jade/runtime

//= require_tree ./lib

//= require ./n
<commit_msg>Bring back `jquery` and `lodash`.<commit_after>//= require vendor/jquery/jquery
//= require vendor/powertip/jquery.powertip

//= require vendor/lodash

//= require vendor/bootstrap/bootstrap
//= require babelfish-runtime
//= require pointer
//= require vendor/history/history.adapter.jquery
//= require vendor/history/history
//= require vendor/jade/runtime

//= require_tree ./lib

//= require ./n
<|endoftext|>


<commit_before>/**
 * /assets/linker/js/story.js
 *
 * This file contains all story specified javascript functions and handlers.
 * Basically file is divided into the following sections:
 *  - Event handlers
 *  - Form handlers
 */
"use strict";

/**
 * Story specified global event handlers. These events are following:
 *  - storyAdd
 *  - storyEdit
 *  - storySplit
 *  - storyDelete
 */
jQuery(document).ready(function() {
    var body = jQuery("body");

    /**
     * User story add event, this opens a modal bootbox dialog with user story add form on it.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                [projectId] Project id
     * @param   {Number}                [sprintId]  Sprint id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     * @param   {{}}                    [formData]  Possible form data, simple key/value
     */
    body.on("storyAdd", function(event, projectId, sprintId, trigger, formData) {
        projectId = projectId || myViewModel.project().id();
        trigger = trigger || false;
        formData = formData || {};

        if (typeof sprintId === "undefined") {
            if (myViewModel.sprint().id()) {
                sprintId = myViewModel.sprint().id();
            }
        }

        jQuery.get("/Story/add", {projectId: projectId, sprintId: sprintId, formData: formData})
        .done(function(content) {
            var title = "Add new user story";
            var buttons = [
                {
                    label: "Save and close",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, true);

                        return false;
                    }
                },
                {
                    label: "Save",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, false);

                        return false;
                    }
                }
            ];

            // Create bootbox modal
            var modal = createBootboxDialog(title, content, buttons, trigger);

            // Make form init when dialog is opened.
            modal.on("shown.bs.modal", function() {
                initStoryForm(modal);
            });

            // Open bootbox modal
            modal.modal("show");
        })
        .fail(function(jqXhr, textStatus, error) {
            handleAjaxError(jqXhr, textStatus, error);
        });

        /**
         * Method makes actual save function for current model and closes dialog + fire specified
         * trigger event OR opens edit modal with specified trigger event.
         *
         * @param   {jQuery|$}                  modal
         * @param   {sails.helper.trigger|bool} trigger
         * @param   {boolean}                   close
         */
        function save(modal, trigger, close) {
            var form = jQuery("#formStoryNew", modal);
            var formItems = form.serializeJSON();

            // Validate form and try to create new user story
            if (validateForm(formItems, modal)) {
                // Create new user story
                socket.post("/Story", formItems, function(/** sails.json.story */data) {
                    if (handleSocketError(data)) {
                        makeMessage("User story created successfully.");

                        modal.modal("hide");

                        // User wants to close modal so just handle trigger
                        if (close) {
                            handleEventTrigger(trigger);
                        } else { // Otherwise trigger edit with same trigger
                            body.trigger("storyEdit", [data.id, trigger]);
                        }
                    }
                });
            }
        }
    });

    /**
     * User story edit event, this opens a modal bootbox dialog with user story edit form on it.
     *
     * @param   {jQuery.Event}          event           Event object
     * @param   {Number}                storyId         Story id
     * @param   {sails.helper.trigger}  [trigger]       Trigger to process after actions
     * @param   {{}}                    [parameters]    Init parameters, this is passed to form init function
     */
    body.on("storyEdit", function(event, storyId, trigger, parameters) {
        trigger = trigger || false;
        parameters = parameters || {};

        jQuery.get("/Story/edit", {id: storyId})
        .done(function(content) {
            var title = "Edit user story";
            var buttons = [
                {
                    label: "Save and close",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, true);

                        return false;
                    }
                },
                {
                    label: "Save",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, false);

                        return false;
                    }
                },
                {
                    label: "Split story",
                    className: "btn-warning pull-right",
                    id: "split",
                    callback: function() {
                        // Trigger story split event
                        body.trigger("storySplit", [storyId, {trigger: "storyEdit", parameters: [storyId, trigger]}]);
                    }
                },
                {
                    label: "Delete",
                    className: "btn-danger pull-right",
                    callback: function() {
                        // Trigger story delete event
                        body.trigger("storyDelete", [storyId, {trigger: "storyEdit", parameters: [storyId, trigger]}]);
                    }
                }
            ];

            // Create bootbox modal
            var modal = createBootboxDialog(title, content, buttons, trigger);

            // Make form init when dialog is opened.
            modal.on("shown.bs.modal", function() {
                initStoryForm(modal, parameters);
            });

            // Open bootbox modal
            modal.modal("show");
        })
        .fail(function(jqXhr, textStatus, error) {
            handleAjaxError(jqXhr, textStatus, error);
        });

        /**
         * Method makes actual save function for current model and closes dialog + fire specified
         * trigger event OR opens edit modal with specified trigger event.
         *
         * @param   {jQuery|$}                  modal
         * @param   {sails.helper.trigger|bool} trigger
         * @param   {boolean}                   close
         */
        function save(modal, trigger, close) {
            var formBasic = jQuery("#formStoryEdit_1", modal);
            var formAdvanced = jQuery("#formStoryEdit_2", modal);
            var formItems = jQuery.extend({}, formBasic.serializeJSON(), formAdvanced.serializeJSON());

            // Validate current form items and try to update user story data
            if (validateForm(formItems, modal)) {
                // Update user story
                socket.put("/Story/" + storyId, formItems, function(/** sails.json.story */story) {
                    if (handleSocketError(story)) {
                        makeMessage("User story updated successfully.");

                        // User wants to close modal so just handle trigger
                        if (close) {
                            handleEventTrigger(trigger);

                            modal.modal("hide");
                        }
                    }
                });
            }
        }
    });

    /**
     * User story split event, this opens a modal bootbox prompt with sprint selection.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     */
    body.on("storySplit", function(event, storyId, trigger) {
        trigger = trigger || false;

        var options = [];

        // Add project backlog option
        options.push({value: 0, text: "Project backlog"});

        // Iterate project sprints and add those to option lists
        jQuery.each(myViewModel.sprints(), function(key, sprint) {
            options.push({value: sprint.id(), text: sprint.formattedTitle()});
        });

        // Make prompt box
        var prompt = bootbox.prompt({
            title: "Select sprint where to add new story",
            buttons: {
                cancel: {
                    label: "Cancel",
                    className: "btn-default pull-left"
                },
                confirm: {
                    label: "Split story",
                    className: "btn-primary pull-right"
                }
            },
            inputType: "select",
            inputOptions: options,
            show: false,
            callback: function(result) {
                if (result !== null) {
                    jQuery.ajax({
                        type: "POST",
                        url: "/Story/split",
                        data: {
                            storyId: storyId,
                            sprintId: result,
                            projectId: myViewModel.project().id(),
                            _csrf: getCsrfToken()
                        },
                        dataType: "json"
                    })
                    .done(function() {
                        makeMessage("User story splitted successfully.");

                        prompt.modal("hide");

                        handleEventTrigger(trigger);
                    })
                    .fail(function(jqXhr, textStatus, error) {
                        handleAjaxError(jqXhr, textStatus, error);
                    });
                } else {
                    prompt.modal("hide");

                    handleEventTrigger(trigger);
                }

                return false;
            }
        });

        // Initialize prompt select
        prompt.on("shown.bs.modal", function() {
            initSelectPrompt(prompt);
        });

        // Open bootbox prompt modal
        prompt.modal("show");
    });

    /**
     * User story delete event, this opens a modal bootbox confirm about story delete.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     */
    body.on("storyDelete", function(event, storyId, trigger) {
        trigger = trigger || false;

        // Make confirm box
        bootbox.confirm({
            title: "danger - danger - danger",
            message: "Are you sure of story delete?",
            buttons: {
                cancel: {
                    label: "Cancel",
                    className: "btn-default pull-left"
                },
                confirm: {
                    label: "Delete",
                    className: "btn-danger pull-right"
                }
            },
            callback: function(result) {
                if (result) {
                    // Delete story data
                    socket.delete("/Story/" + storyId, {_csrf: getCsrfToken()}, function(/** sails.json.story */story) {
                        if (handleSocketError(story)) {
                            makeMessage("User story deleted successfully.");

                            handleEventTrigger(trigger, "storyEdit");
                        }
                    });
                } else {
                    handleEventTrigger(trigger);
                }
            }
        });
    });

    /**
     * Story tasks event. This will open story edit modal dialog and activates tasks tab to
     * be opened. Note that this just fires storyEdit event with specified parameters.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  trigger     Trigger to process after actions
     */
    body.on("storyTasks", function(event, storyId, trigger) {
        // Used parameters for form init
        var parameters = {
            activeTab: "tasks"
        };

        body.trigger("storyEdit", [storyId, trigger, parameters]);
    });
});

/**
 * Function to initialize story form.
 *
 * @param   {jQuery|$}  modal           Current modal content
 * @param   {{}}        [parameters]    Parameters
 */
function initStoryForm(modal, parameters) {
    parameters = parameters || {};

    if (parameters.activeTab) {
        jQuery("#" + parameters.activeTab + "Tab", modal).click();
    }

    var inputTitle = jQuery("input[name='title']", modal);

    inputTitle.focus().val(inputTitle.val());

    var slider = jQuery(".estimateSlider", modal);
    var input = slider.next("input");
    var show = jQuery(".sliderValue", modal);
    var currentValue = input.val();

    // Specify fibonacci values for story sizes
    var values = [ "-1", "0", "0.5", "1", "2", "3", "5", "8", "13", "20", "40", "100" ];

    show.text(currentValue == -1 ? "???" : values[values.indexOf(currentValue)]);

    // Note that this slider changes index values to fibonacci values
    slider
        .slider({
            tooltip: "hide"
        })
        .on("slide", function(sliderEvent) {
            show.text(values[sliderEvent.value] == -1 ? "???" : values[sliderEvent.value]);
            input.val(values[sliderEvent.value]);
        })
    ;

    // Set value for slider
    slider
        .slider("setValue", values.indexOf(currentValue));
}

/**
 * Function initializes story tasks tab to use in task edit modal. Note that
 * this init can be called multiple times.
 *
 * Also note that this init is called dynamic from initTabs() function.
 *
 * @param   {jQuery|$}  modal       Current modal content
 * @param   {String}    contentId   Tab content div id
 */
function initStoryTabTasks(modal, contentId) {
    var body = jQuery("body");
    var container = modal.find(contentId);

    // Initialize action menu for stories
    initActionMenu(container, {});

    // Remove 'add new' click listeners, this prevents firing this event multiple times
    body.off("click", "[data-add-new-task='true']");

    // User wants to add new user to project
    body.on("click", "[data-add-new-task='true']", function() {
        var element = jQuery(this);
        var storyId = element.data("storyId");

        // Hide current modal
        modal.modal("hide");

        // Specify used trigger to come back to this view
        var trigger = {
            trigger: "storyEdit",
            parameters: [storyId, null, {activeTab: "tasks"}]
        };

        // Find specified story object
        var story = _.find(myViewModel.stories(), function(story) { return story.id() === storyId; });

        body.trigger("taskAdd", [story, trigger]);
    });

    // User clicks action menu link
    body.on("click", "ul.actionMenu-actions a", function() {
        var element = jQuery(this);
        var storyId = element.data("storyId");
        var taskId = element.data("taskId");
        var action = element.data("action");
        var selector = element.data("selector");

        // We have popover selector, so hide it
        if (selector) {
            jQuery(selector).popover("hide");
        }

        // Hide current modal
        modal.modal("hide");

        // Specify used trigger to come back to this view
        var trigger = {
            trigger: "storyEdit",
            parameters: [storyId, null, {activeTab: "tasks"}]
        };

        // Trigger milestone action event
        body.trigger(action, [taskId, trigger]);
    });

    if (myViewModel.role() < 0) {
        // User changes task priority order
        jQuery("#storyTasks tbody", container).sortable({
            axis: "y",
            helper: function(e, tr) {
                var helper = tr.clone();

                return helper.addClass("sortable");
            },
            stop: function() {
                var table = jQuery("#storyTasks", container);
                var rows = table.find("tbody tr.taskRow");
                var taskIds = [];

                rows.fadeTo(0, 0.5);

                // Get used task id and priority values
                rows.each(function(index) {
                    var taskId = jQuery(this).data("taskId");

                    if (taskId) {
                        taskIds.push({taskId: taskId, priority: index + 1, row: jQuery(this)});
                    }
                });

                // Make necessary updates
                async.map(
                    taskIds,
                    function (item, callback) {
                        // This is weird, why this is faster than socket.put() call? possible bug in sails.js?
                        jQuery
                            .ajax({
                                url: "/Task/" + item.taskId,
                                data: {
                                    priority: item.priority,
                                    _csrf: getCsrfToken()
                                },
                                type: "PUT",
                                dataType: "json"
                            })
                            .done(function() {
                                item.row.find("td.task-priority").html(item.priority);

                                callback(null, item.taskId);
                            })
                            .fail(function(jqXhr, textStatus, error) {
                                var errorMessage = {
                                    jqXhr: jqXhr,
                                    textStatus: textStatus,
                                    error: error
                                };

                                callback(errorMessage, null);
                            });
                    },
                    function(error, results) {
                        if (error) {
                            handleAjaxError(error.jqXhr, error.textStatus, error.error);
                        }

                        var message = "";
                        var type = "success";

                        if (error) {
                            type = "error";
                            message = "Error in task priority update";
                        } else {
                            message = "Tasks priorities changed successfully";
                        }

                        makeMessage(message, type);

                        rows.fadeTo(0, 1);
                    }
                );
            }
        });
    }
}
<commit_msg>Fixed story splitting bug, note this is just a hot fix for this.<commit_after>/**
 * /assets/linker/js/story.js
 *
 * This file contains all story specified javascript functions and handlers.
 * Basically file is divided into the following sections:
 *  - Event handlers
 *  - Form handlers
 */
"use strict";

/**
 * Story specified global event handlers. These events are following:
 *  - storyAdd
 *  - storyEdit
 *  - storySplit
 *  - storyDelete
 */
jQuery(document).ready(function() {
    var body = jQuery("body");

    /**
     * User story add event, this opens a modal bootbox dialog with user story add form on it.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                [projectId] Project id
     * @param   {Number}                [sprintId]  Sprint id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     * @param   {{}}                    [formData]  Possible form data, simple key/value
     */
    body.on("storyAdd", function(event, projectId, sprintId, trigger, formData) {
        projectId = projectId || myViewModel.project().id();
        trigger = trigger || false;
        formData = formData || {};

        if (typeof sprintId === "undefined") {
            if (myViewModel.sprint().id()) {
                sprintId = myViewModel.sprint().id();
            }
        }

        jQuery.get("/Story/add", {projectId: projectId, sprintId: sprintId, formData: formData})
        .done(function(content) {
            var title = "Add new user story";
            var buttons = [
                {
                    label: "Save and close",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, true);

                        return false;
                    }
                },
                {
                    label: "Save",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, false);

                        return false;
                    }
                }
            ];

            // Create bootbox modal
            var modal = createBootboxDialog(title, content, buttons, trigger);

            // Make form init when dialog is opened.
            modal.on("shown.bs.modal", function() {
                initStoryForm(modal);
            });

            // Open bootbox modal
            modal.modal("show");
        })
        .fail(function(jqXhr, textStatus, error) {
            handleAjaxError(jqXhr, textStatus, error);
        });

        /**
         * Method makes actual save function for current model and closes dialog + fire specified
         * trigger event OR opens edit modal with specified trigger event.
         *
         * @param   {jQuery|$}                  modal
         * @param   {sails.helper.trigger|bool} trigger
         * @param   {boolean}                   close
         */
        function save(modal, trigger, close) {
            var form = jQuery("#formStoryNew", modal);
            var formItems = form.serializeJSON();

            // Validate form and try to create new user story
            if (validateForm(formItems, modal)) {
                // Create new user story
                socket.post("/Story", formItems, function(/** sails.json.story */data) {
                    if (handleSocketError(data)) {
                        makeMessage("User story created successfully.");

                        modal.modal("hide");

                        // User wants to close modal so just handle trigger
                        if (close) {
                            handleEventTrigger(trigger);
                        } else { // Otherwise trigger edit with same trigger
                            body.trigger("storyEdit", [data.id, trigger]);
                        }
                    }
                });
            }
        }
    });

    /**
     * User story edit event, this opens a modal bootbox dialog with user story edit form on it.
     *
     * @param   {jQuery.Event}          event           Event object
     * @param   {Number}                storyId         Story id
     * @param   {sails.helper.trigger}  [trigger]       Trigger to process after actions
     * @param   {{}}                    [parameters]    Init parameters, this is passed to form init function
     */
    body.on("storyEdit", function(event, storyId, trigger, parameters) {
        trigger = trigger || false;
        parameters = parameters || {};

        jQuery.get("/Story/edit", {id: storyId})
        .done(function(content) {
            var title = "Edit user story";
            var buttons = [
                {
                    label: "Save and close",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, true);

                        return false;
                    }
                },
                {
                    label: "Save",
                    className: "btn-primary pull-right",
                    callback: function() {
                        save(modal, trigger, false);

                        return false;
                    }
                },
                {
                    label: "Split story",
                    className: "btn-warning pull-right",
                    id: "split",
                    callback: function() {
                        // Trigger story split event
                        body.trigger("storySplit", [storyId, {trigger: "storyEdit", parameters: [storyId, trigger]}]);
                    }
                },
                {
                    label: "Delete",
                    className: "btn-danger pull-right",
                    callback: function() {
                        // Trigger story delete event
                        body.trigger("storyDelete", [storyId, {trigger: "storyEdit", parameters: [storyId, trigger]}]);
                    }
                }
            ];

            // Create bootbox modal
            var modal = createBootboxDialog(title, content, buttons, trigger);

            // Make form init when dialog is opened.
            modal.on("shown.bs.modal", function() {
                initStoryForm(modal, parameters);
            });

            // Open bootbox modal
            modal.modal("show");
        })
        .fail(function(jqXhr, textStatus, error) {
            handleAjaxError(jqXhr, textStatus, error);
        });

        /**
         * Method makes actual save function for current model and closes dialog + fire specified
         * trigger event OR opens edit modal with specified trigger event.
         *
         * @param   {jQuery|$}                  modal
         * @param   {sails.helper.trigger|bool} trigger
         * @param   {boolean}                   close
         */
        function save(modal, trigger, close) {
            var formBasic = jQuery("#formStoryEdit_1", modal);
            var formAdvanced = jQuery("#formStoryEdit_2", modal);
            var formItems = jQuery.extend({}, formBasic.serializeJSON(), formAdvanced.serializeJSON());

            // Validate current form items and try to update user story data
            if (validateForm(formItems, modal)) {
                // Update user story
                socket.put("/Story/" + storyId, formItems, function(/** sails.json.story */story) {
                    if (handleSocketError(story)) {
                        makeMessage("User story updated successfully.");

                        // User wants to close modal so just handle trigger
                        if (close) {
                            handleEventTrigger(trigger);

                            modal.modal("hide");
                        }
                    }
                });
            }
        }
    });

    /**
     * User story split event, this opens a modal bootbox prompt with sprint selection.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     */
    body.on("storySplit", function(event, storyId, trigger) {
        trigger = trigger || false;

        var options = [];

        // Add project backlog option
        options.push({value: 0, text: "Project backlog"});

        // Iterate project sprints and add those to option lists
        jQuery.each(myViewModel.sprints(), function(key, sprint) {
            options.push({value: sprint.id(), text: sprint.formattedTitle()});
        });

        // Make prompt box
        var prompt = bootbox.prompt({
            title: "Select sprint where to add new story",
            buttons: {
                cancel: {
                    label: "Cancel",
                    className: "btn-default pull-left"
                },
                confirm: {
                    label: "Split story",
                    className: "btn-primary pull-right"
                }
            },
            inputType: "select",
            inputOptions: options,
            show: false,
            callback: function(result) {
                if (result !== null) {
                    jQuery.ajax({
                        type: "POST",
                        url: "/Story/split",
                        data: {
                            storyId: storyId,
                            sprintId: result,
                            projectId: myViewModel.project().id(),
                            _csrf: getCsrfToken()
                        },
                        dataType: "json"
                    })
                    .done(function() {
                        makeMessage("User story splitted successfully.");

                        prompt.modal("hide");

                        handleEventTrigger(trigger);
                    })
                    .fail(function(jqXhr, textStatus, error) {
                        handleAjaxError(jqXhr, textStatus, error);
                    });
                } else {
                    prompt.modal("hide");

                    handleEventTrigger(trigger);
                }

                return false;
            }
        });

        // Initialize prompt select
        prompt.on("shown.bs.modal", function() {
            // Needed to disable this, otherwise modal didn't contain anything...
           // initSelectPrompt(prompt);
        });

        // Open bootbox prompt modal
        prompt.modal("show");
    });

    /**
     * User story delete event, this opens a modal bootbox confirm about story delete.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  [trigger]   Trigger to process after actions
     */
    body.on("storyDelete", function(event, storyId, trigger) {
        trigger = trigger || false;

        // Make confirm box
        bootbox.confirm({
            title: "danger - danger - danger",
            message: "Are you sure of story delete?",
            buttons: {
                cancel: {
                    label: "Cancel",
                    className: "btn-default pull-left"
                },
                confirm: {
                    label: "Delete",
                    className: "btn-danger pull-right"
                }
            },
            callback: function(result) {
                if (result) {
                    // Delete story data
                    socket.delete("/Story/" + storyId, {_csrf: getCsrfToken()}, function(/** sails.json.story */story) {
                        if (handleSocketError(story)) {
                            makeMessage("User story deleted successfully.");

                            handleEventTrigger(trigger, "storyEdit");
                        }
                    });
                } else {
                    handleEventTrigger(trigger);
                }
            }
        });
    });

    /**
     * Story tasks event. This will open story edit modal dialog and activates tasks tab to
     * be opened. Note that this just fires storyEdit event with specified parameters.
     *
     * @param   {jQuery.Event}          event       Event object
     * @param   {Number}                storyId     Story id
     * @param   {sails.helper.trigger}  trigger     Trigger to process after actions
     */
    body.on("storyTasks", function(event, storyId, trigger) {
        // Used parameters for form init
        var parameters = {
            activeTab: "tasks"
        };

        body.trigger("storyEdit", [storyId, trigger, parameters]);
    });
});

/**
 * Function to initialize story form.
 *
 * @param   {jQuery|$}  modal           Current modal content
 * @param   {{}}        [parameters]    Parameters
 */
function initStoryForm(modal, parameters) {
    parameters = parameters || {};

    if (parameters.activeTab) {
        jQuery("#" + parameters.activeTab + "Tab", modal).click();
    }

    var inputTitle = jQuery("input[name='title']", modal);

    inputTitle.focus().val(inputTitle.val());

    var slider = jQuery(".estimateSlider", modal);
    var input = slider.next("input");
    var show = jQuery(".sliderValue", modal);
    var currentValue = input.val();

    // Specify fibonacci values for story sizes
    var values = [ "-1", "0", "0.5", "1", "2", "3", "5", "8", "13", "20", "40", "100" ];

    show.text(currentValue == -1 ? "???" : values[values.indexOf(currentValue)]);

    // Note that this slider changes index values to fibonacci values
    slider
        .slider({
            tooltip: "hide"
        })
        .on("slide", function(sliderEvent) {
            show.text(values[sliderEvent.value] == -1 ? "???" : values[sliderEvent.value]);
            input.val(values[sliderEvent.value]);
        })
    ;

    // Set value for slider
    slider
        .slider("setValue", values.indexOf(currentValue));
}

/**
 * Function initializes story tasks tab to use in task edit modal. Note that
 * this init can be called multiple times.
 *
 * Also note that this init is called dynamic from initTabs() function.
 *
 * @param   {jQuery|$}  modal       Current modal content
 * @param   {String}    contentId   Tab content div id
 */
function initStoryTabTasks(modal, contentId) {
    var body = jQuery("body");
    var container = modal.find(contentId);

    // Initialize action menu for stories
    initActionMenu(container, {});

    // Remove 'add new' click listeners, this prevents firing this event multiple times
    body.off("click", "[data-add-new-task='true']");

    // User wants to add new user to project
    body.on("click", "[data-add-new-task='true']", function() {
        var element = jQuery(this);
        var storyId = element.data("storyId");

        // Hide current modal
        modal.modal("hide");

        // Specify used trigger to come back to this view
        var trigger = {
            trigger: "storyEdit",
            parameters: [storyId, null, {activeTab: "tasks"}]
        };

        // Find specified story object
        var story = _.find(myViewModel.stories(), function(story) { return story.id() === storyId; });

        body.trigger("taskAdd", [story, trigger]);
    });

    // User clicks action menu link
    body.on("click", "ul.actionMenu-actions a", function() {
        var element = jQuery(this);
        var storyId = element.data("storyId");
        var taskId = element.data("taskId");
        var action = element.data("action");
        var selector = element.data("selector");

        // We have popover selector, so hide it
        if (selector) {
            jQuery(selector).popover("hide");
        }

        // Hide current modal
        modal.modal("hide");

        // Specify used trigger to come back to this view
        var trigger = {
            trigger: "storyEdit",
            parameters: [storyId, null, {activeTab: "tasks"}]
        };

        // Trigger milestone action event
        body.trigger(action, [taskId, trigger]);
    });

    if (myViewModel.role() < 0) {
        // User changes task priority order
        jQuery("#storyTasks tbody", container).sortable({
            axis: "y",
            helper: function(e, tr) {
                var helper = tr.clone();

                return helper.addClass("sortable");
            },
            stop: function() {
                var table = jQuery("#storyTasks", container);
                var rows = table.find("tbody tr.taskRow");
                var taskIds = [];

                rows.fadeTo(0, 0.5);

                // Get used task id and priority values
                rows.each(function(index) {
                    var taskId = jQuery(this).data("taskId");

                    if (taskId) {
                        taskIds.push({taskId: taskId, priority: index + 1, row: jQuery(this)});
                    }
                });

                // Make necessary updates
                async.map(
                    taskIds,
                    function (item, callback) {
                        // This is weird, why this is faster than socket.put() call? possible bug in sails.js?
                        jQuery
                            .ajax({
                                url: "/Task/" + item.taskId,
                                data: {
                                    priority: item.priority,
                                    _csrf: getCsrfToken()
                                },
                                type: "PUT",
                                dataType: "json"
                            })
                            .done(function() {
                                item.row.find("td.task-priority").html(item.priority);

                                callback(null, item.taskId);
                            })
                            .fail(function(jqXhr, textStatus, error) {
                                var errorMessage = {
                                    jqXhr: jqXhr,
                                    textStatus: textStatus,
                                    error: error
                                };

                                callback(errorMessage, null);
                            });
                    },
                    function(error, results) {
                        if (error) {
                            handleAjaxError(error.jqXhr, error.textStatus, error.error);
                        }

                        var message = "";
                        var type = "success";

                        if (error) {
                            type = "error";
                            message = "Error in task priority update";
                        } else {
                            message = "Tasks priorities changed successfully";
                        }

                        makeMessage(message, type);

                        rows.fadeTo(0, 1);
                    }
                );
            }
        });
    }
}
<|endoftext|>


<commit_before>/**
 * Blocks for designing a graphical user interface for Qooxdoo Mobile
 *
 * Copyright 2012 Derrell Lipman
 *
 * 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.
 */

/**
 * @fileoverview Gui blocks for the qooxdoo mobile UI
 * @author derrell.lipman@unwireduniverse.com (Derrell Lipman)
 */
if (!Blockly.Language) {
  Blockly.Language = {};
}

/**
 * A composite container's code generator.  The container may use either a
 * horizontal or vertical layout.
 */
Blockly.Language.qxmobileui_container_composite = {
  // A composite container
  category: "Container",
  semantics: { type : "container" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("purple");
    this.addTitle("Container");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    this.addTitle("  layout:");
    this.addTitle(new Blockly.FieldDropdown(
                    "horizontal",
                    function() {
                      return [ "horizontal", "vertical" ];
                    }));

    this.addTitle("  spacing:");
    this.addTitle(new Blockly.FieldTextInput('0', function(text) {
      var n = window.parseFloat(text || 0);
      return window.isNaN(n) ? null : String(n);
    }));

    this.addInput("", "", Blockly.NEXT_STATEMENT,
                  null, { type : [ "container", "widget" ] });
  }
};

/**
 * An atom widget. An item can easily align the common icon/text combination
 * in different ways.
 */
Blockly.Language.qxmobileui_atom = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Atom");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the text input
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });

    // Add the icon (url) input
    this.addInput("  icon:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * An image widget.
 */
Blockly.Language.qxmobileui_image = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Image");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the icon (url) input
    this.addInput("  icon:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A label widget.
 */
Blockly.Language.qxmobileui_label = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Label");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the text input
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A button widget.
 */
Blockly.Language.qxmobileui_button = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Button");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the button's label
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A checkbox widget.
 */
Blockly.Language.qxmobileui_checkbox = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Checkbox");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));
  }
};

/**
 * A number input field widget.
 */
Blockly.Language.qxmobileui_numberInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Number Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });
  }
};

/**
 * A password input field widget.
 */
Blockly.Language.qxmobileui_passwordInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Password Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A radio button widget.
 */
Blockly.Language.qxmobileui_radioButton = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Radio Button");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));
  }
};

/**
 * A select box widget.
 */
Blockly.Language.qxmobileui_selectBox = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Select Box");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));
  }
};

/**
 * A slider widget.
 */
Blockly.Language.qxmobileui_slider = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Slider");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });

    // Add the minimum value
    this.addInput("  minimum:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });

    // Add the maximum value
    this.addInput("  maximum:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });
  }
};

/**
 * A text area widget.
 */
Blockly.Language.qxmobileui_textArea = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Text Area");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A text input field widget.
 */
Blockly.Language.qxmobileui_textInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Text Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A toggle button widget.
 */
Blockly.Language.qxmobileui_toggleButton = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("Toggle Button");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));

    this.addTitle("  initial value:");
    this.addTitle(new Blockly.FieldDropdown(
                    "off",
                    function() {
                      return [ "off", "on" ];
                    }));
  }
};

/**
 * A list widget.
 */
Blockly.Language.qxmobileui_list = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour("green");
    this.addTitle("List");

    this.addTitle(new Blockly.FieldDropdown(
                    'temp',
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange));
  }
};

<commit_msg>colors from rgb to hsv; FieldDropdown() no longer has initial selection<commit_after>/**
 * Blocks for designing a graphical user interface for Qooxdoo Mobile
 *
 * Copyright 2012 Derrell Lipman
 *
 * 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.
 */

/**
 * @fileoverview Gui blocks for the qooxdoo mobile UI
 * @author derrell.lipman@unwireduniverse.com (Derrell Lipman)
 */
if (!Blockly.Language) {
  Blockly.Language = {};
}

/**
 * A composite container's code generator.  The container may use either a
 * horizontal or vertical layout.
 */
Blockly.Language.qxmobileui_container_composite = {
  // A composite container
  category: "Container",
  semantics: { type : "container" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(280);
    this.addTitle("Container");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    this.addTitle("  layout:");
    this.addTitle(new Blockly.FieldDropdown(
                    function() {
                      return [ "horizontal", "vertical" ];
                    }));

    this.addTitle("  spacing:");
    this.addTitle(new Blockly.FieldTextInput('0', function(text) {
      var n = window.parseFloat(text || 0);
      return window.isNaN(n) ? null : String(n);
    }));

    this.addInput("", "", Blockly.NEXT_STATEMENT,
                  null, { type : [ "container", "widget" ] });
  }
};

/**
 * An atom widget. An item can easily align the common icon/text combination
 * in different ways.
 */
Blockly.Language.qxmobileui_atom = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Atom");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the text input
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });

    // Add the icon (url) input
    this.addInput("  icon:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * An image widget.
 */
Blockly.Language.qxmobileui_image = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Image");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the icon (url) input
    this.addInput("  icon:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A label widget.
 */
Blockly.Language.qxmobileui_label = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Label");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the text input
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A button widget.
 */
Blockly.Language.qxmobileui_button = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Button");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the button's label
    this.addInput("  label:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A checkbox widget.
 */
Blockly.Language.qxmobileui_checkbox = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Checkbox");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');
  }
};

/**
 * A number input field widget.
 */
Blockly.Language.qxmobileui_numberInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Number Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });
  }
};

/**
 * A password input field widget.
 */
Blockly.Language.qxmobileui_passwordInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Password Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A radio button widget.
 */
Blockly.Language.qxmobileui_radioButton = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Radio Button");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');
  }
};

/**
 * A select box widget.
 */
Blockly.Language.qxmobileui_selectBox = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Select Box");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');
  }
};

/**
 * A slider widget.
 */
Blockly.Language.qxmobileui_slider = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Slider");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });

    // Add the minimum value
    this.addInput("  minimum:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });

    // Add the maximum value
    this.addInput("  maximum:", "", Blockly.INPUT_VALUE,
                  null, { type : "integer" });
  }
};

/**
 * A text area widget.
 */
Blockly.Language.qxmobileui_textArea = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Text Area");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A text input field widget.
 */
Blockly.Language.qxmobileui_textInput = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Text Input Field");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    // Add the initial value
    this.addInput("  initial value:", "", Blockly.INPUT_VALUE,
                  null, { type : "string" });
  }
};

/**
 * A toggle button widget.
 */
Blockly.Language.qxmobileui_toggleButton = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("Toggle Button");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');

    this.addTitle("  initial value:");
    this.addTitle(new Blockly.FieldDropdown(
                    function() {
                      return [ "off", "on" ];
                    }));
  }
};

/**
 * A list widget.
 */
Blockly.Language.qxmobileui_list = {
  category: "Widget",
  semantics: { type : "widget" },
  init: function() {
    this.setPreviousStatement(true);
    this.setNextStatement(true);
    this.setColour(120);
    this.addTitle("List");

    this.addTitle(new Blockly.FieldDropdown(
                    Blockly.Variables.dropdownCreate,
                    Blockly.Variables.dropdownChange))
      .setText('temp');
  }
};

<|endoftext|>


<commit_before>var Reaction = require("../reaction")
  , get = require("../util").get
  , rules = require("../rules")
  , path = require("path")
  , Backbone = require("backbone")
  , _ = require("underscore")

var ModelReaction = Reaction.extend({

  init: function(next, reload) {
    var newModel = get(this.presenter, this.modelName) || {}

    if(!(newModel instanceof Backbone.Model)) {
      newModel = new Backbone.Model(newModel || {})
    }

    this.el.data("model", newModel)
    this.stack.push(newModel)
    next(reload)
  },

  observe: function(next) {
    if(this.presenter.on) {
      this.presenter.on("change:" + this.modelName, function(model) {
        this.init(next, true)
      }, this)
    }
  }

}, {
  selector: "[class]",

  reactIf: function(el) {
    return rules.model(el)
  },

  parse: function(el, state) {
    return {
      modelName: rules.model(el),
      currentDirectory: state.currentDir()
    }
  }
})

module.exports = ModelReaction
<commit_msg>Auto save forms that are bound to models<commit_after>var Reaction = require("../reaction")
  , get = require("../util").get
  , rules = require("../rules")
  , path = require("path")
  , Backbone = require("backbone")
  , _ = require("underscore")

var ModelReaction = Reaction.extend({

  init: function(next, reload) {
    var newModel = get(this.presenter, this.modelName) || {}

    if(!(newModel instanceof Backbone.Model)) {
      newModel = new Backbone.Model(newModel || {})
    }

    this.el.data("model", newModel)
    this.stack.push(newModel)
    next(reload)
  },

  observe: function(next) {
    var that = this
    if(this.presenter.on) {
      this.presenter.on("change:" + this.modelName, function(model) {
        this.init(next, true)
      }, this)
    }
    if(this.el.is("form")) {
      this.el.on("submit", function(e) {
        that.model.save()
        e.preventDefault()
      })
    }
  }

}, {
  selector: "[class]",

  reactIf: function(el) {
    return rules.model(el)
  },

  parse: function(el, state) {
    return {
      modelName: rules.model(el),
      currentDirectory: state.currentDir()
    }
  }
})

module.exports = ModelReaction
<|endoftext|>


<commit_before>import chunk from 'lodash.chunk';
import deepAssign from '../util/deep-assign';
import errors from './errors';
import jsonp from 'jsonp';
import Promise from 'promise';
import qs from 'qs';

const debug = require('debug')('recurly:Request');

/**
 * XHR/XDM Reference, according to browser environment
 */
const XHR = (() => {
  const XHR = window.XMLHttpRequest;
  const XDM = window.XDomainRequest;
  if (XHR && 'withCredentials' in new XHR) return XHR;
  if (XDM) return XDM;
})();

const NOOP = () => {};

/**
 * Constructs and issues API requests according to
 * the Recurly /js/v1 API contract
 *
 * @param {Object} options
 * @param {Recurly} options.recurly Recurly instance
 */
export class Request {
  constructor (options) {
    this.recurly = options.recurly;
    this.cache = {};
  }

  get timeout () {
    return this.recurly.config.timeout || 60000;
  }

  get key () {
    return this.recurly.config.publicKey;
  }

  get version () {
    return this.recurly.isParent ? this.recurly.version : this.recurly.config.parentVersion;
  }

  get deviceId () {
    return this.recurly.deviceId;
  }

  get instanceId () {
    return this.recurly.id;
  }

  get sessionId () {
    return this.recurly.sessionId;
  }

  get shouldUseXHR () {
    return !!this.recurly.config.cors;
  }

  get isConfigured () {
    return !!this.recurly.configured;
  }

  get ({ route, data, done, cached }) {
    return this.requestWithCallback({ method: 'get', route, data, done, cached });
  }

  post ({ route, data, done, cached }) {
    return this.requestWithCallback({ method: 'post', route, data, done, cached });
  }

  requestWithCallback ({ method, route, data, done, cached = false }) {
    const request = this[cached ? 'cached' : 'request']({ method, route, data });
    if (done) return request.done(res => done(null, res), err => done(err, null));
    else return request;
  }

  /**
   * Issues an API request
   *
   * @param {String} method
   * @param {String} route
   * @param {Object} [data]
   * @throws {Error} If `configure` has not been called.
   * @return {Promise}
   * @private
   */
  request ({ method, route, data = {} }) {
    debug('request', method, route, data);

    if (!this.isConfigured) {
      return Promise.reject(errors('not-configured'));
    }

    const { version, key, deviceId, sessionId, instanceId } = this;
    const url = this.recurly.url(route);

    data = deepAssign({}, data, { version, key, deviceId, sessionId, instanceId });

    if (this.shouldUseXHR) {
      return this.xhr({ method, url, data });
    } else {
      return this.jsonp({ method, url, data });
    }
  }

  /**
   * Performs an API request with caching
   */
  cached ({ method, route, data }) {
    let { cache } = this;
    const slug = `${method}-${route}-${JSON.stringify(data)}`;
    if (cache[slug]) return Promise.resolve(cache[slug]);
    return this.request({ method, route, data }).then(res => cache[slug] = res);
  }

  /**
   * Performs an API request that queues until it is able to be sent
   */
  queued (...args) {
    if (this.isConfigured) return this.request(...args);
    return new Promise((resolve, reject) => {
      this.recurly.once('configured', () => this.request(...args).then(resolve, reject));
    });
  }

  /**
   * Pipelines a request across several requests
   *
   * Chunks are made of `data[by]` in `size`. If any request errors,
   * the entire pipeline errors
   *
   * - Ignores individual 404s
   * - Rejects if all responses are 404s
   * - Immediately rejects other errors
   * - Collects and concats array responses
   * - Resolves non-array responses with the first response
   *
   * @param {String} method
   * @param {String} route
   * @param {Object} data
   * @param {Array} data.by must be an Array
   * @param {String} by
   * @param {Number} size=100
   * @return {Promise}
   */
  piped ({ method = 'get', route, data = {}, by, size = 100 }) {
    const chunks = chunk(data[by], size).map(piece => {
      return Object.assign({}, data, { [by]: piece });
    });

    if (chunks.length === 0) {
      return this.request({ method, route, data });
    }

    const ignoreNotfound = error => (error.code === 'not-found' ? { error } : Promise.reject(error));
    const pipeline = chunks.map(data => this.request({ method, route, data }).catch(ignoreNotfound));

    return Promise.all(pipeline).then(results => {
      const successes = results.filter(res => !res.error);
      const nonArraySuccess = successes.filter(res => !Array.isArray(res))[0];
      if (successes.length === 0) return Promise.reject(results[0].error);
      else if (nonArraySuccess) return Promise.resolve(nonArraySuccess);
      else return Promise.resolve(successes.reduce((acc, cur) => acc.concat(cur)));
    });
  }

  /**
   * Issues an API request over xhr.
   *
   * @private
   * @param {String} method
   * @param {String} url
   * @param {Object} [data]
   * @return {Promise}
   */
  xhr ({ method, url, data }) {
    return new Promise((resolve, reject) => {
      let req = new XHR;
      const payload = qs.stringify(data, { encodeValuesOnly: true });
      const { timeout } = this;

      if (method === 'get') {
        url += `?${payload}`;
      }

      req.open(method, url);
      req.timeout = timeout;
      req.ontimeout = () => reject(errors('api-timeout'));
      req.onerror = () => reject(errors('api-error'));
      req.onprogress = NOOP;
      req.onload = function () {
        let res;
        try {
          if (this.responseText) res = JSON.parse(this.responseText);
        } catch (e) {
          debug(e, this.responseText);
          return reject(errors('api-error', {
            message: `There was a problem parsing the API response with: ${this.responseText}`
          }));
        }

        if (res && res.error) {
          reject(errors('api-error', res.error));
        } else {
          resolve(res);
        }
      };

      // XDR requests will abort if too many are sent simultaneously
      setTimeout(() => {
        if (method === 'post') {
          // XDR cannot set Content-type
          if (req.setRequestHeader) {
            req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
          }
          req.send(payload);
        } else {
          req.send();
        }
      }, 0);
    });
  }

  /**
   * Issues an API request over jsonp.
   *
   * @private
   * @param {String} method
   * @param {String} url
   * @param {Object} [data]
   */
  jsonp ({ method, url, data }) {
    const { timeout } = this;

    if (method === 'post') data._method = method;
    url += `?${qs.stringify(data, { encodeValuesOnly: true })}`;

    return new Promise((resolve, reject) => {
      jsonp(url, { prefix: '__rjs', timeout }, function (err, res) {
        if (err) return reject(err);
        if (res.error) {
          reject(errors('api-error', res.error));
        } else {
          resolve(res);
        }
      });
    });
  }
}
<commit_msg>Simplifies Request.piped control flows - Adds documentation to Request<commit_after>import chunk from 'lodash.chunk';
import deepAssign from '../util/deep-assign';
import errors from './errors';
import jsonp from 'jsonp';
import Promise from 'promise';
import qs from 'qs';

const debug = require('debug')('recurly:Request');

/**
 * XHR/XDM Reference, according to browser environment
 */
const XHR = (() => {
  const XHR = window.XMLHttpRequest;
  const XDM = window.XDomainRequest;
  if (XHR && 'withCredentials' in new XHR) return XHR;
  if (XDM) return XDM;
})();

const NOOP = () => {};

/**
 * @typedef {RequestOptions}
 *
 * @property {String}   route          API route (e.g.: '/plans/my-plan')
 * @property {Object}   data           Request payload
 * @property {Function} [done]         Response callback. Signature: done(err, res)
 * @property {Boolean}  [cached=false] Whether to cache the request
 */

/**
 * Constructs and issues API requests according to
 * the Recurly /js/v1 API contract
 *
 * @param {Object} options
 * @param {Recurly} options.recurly Recurly instance
 */
export class Request {
  constructor (options) {
    this.recurly = options.recurly;
    this.cache = {};
  }

  /**
   * @type {Number} default is 60 seconds
   */
  get timeout () {
    return this.recurly.config.timeout || 60000;
  }

  /**
   * @type {String}
   */
  get key () {
    return this.recurly.config.publicKey;
  }

  /**
   * @type {String}
   */
  get version () {
    return this.recurly.isParent ? this.recurly.version : this.recurly.config.parentVersion;
  }

  /**
   * @type {String}
   */
  get deviceId () {
    return this.recurly.deviceId;
  }

  /**
   * @type {String}
   */
  get instanceId () {
    return this.recurly.id;
  }

  /**
   * @type {String}
   */
  get sessionId () {
    return this.recurly.sessionId;
  }

  /**
   * @type {Boolean}
   */
  get shouldUseXHR () {
    return !!this.recurly.config.cors;
  }

  /**
   * @type {Boolean}
   */
  get isConfigured () {
    return !!this.recurly.configured;
  }

  /**
   * Performs a GET request
   *
   * @public
   * @param {RequestOptions} options
   *
   * @return {Promise} if `done` is not passed in options
   * @return {undefined} if `done` is passed in options -- the callback will be used instead
   */
  get ({ route, data, done, cached }) {
    return this.requestWithCallback({ method: 'get', route, data, done, cached });
  }

  /**
   * Performs a POST request
   *
   * @public
   * @param {RequestOptions} options
   *
   * @return {Promise} if `done` is not passed in options
   * @return {undefined} if `done` is passed in options -- the callback will be used instead
   */
  post ({ route, data, done, cached }) {
    return this.requestWithCallback({ method: 'post', route, data, done, cached });
  }

  /**
   * Constructs a request with an optional callback resolve handler
   *
   * @private
   * @param {RequestOptions} options
   * @param {String}         options.method 'get' or 'post'
   *
   * @return {Promise} if `done` is not passed in options
   * @return {undefined} if `done` is passed in options -- the callback will be used instead
   */
  requestWithCallback ({ method, route, data, done, cached = false }) {
    const request = this[cached ? 'cached' : 'request']({ method, route, data });

    if (done) {
      return request.done(
        res => done(null, res),
        err => done(err, null)
      );
    }

    return request;
  }

  /**
   * Issues an API request
   *
   * @public
   * @param {String} method
   * @param {String} route
   * @param {Object} [data]
   * @throws {Error} If `configure` has not been called.
   *
   * @return {Promise}
   */
  request ({ method, route, data = {} }) {
    debug('request', method, route, data);

    if (!this.isConfigured) {
      return Promise.reject(errors('not-configured'));
    }

    const { version, key, deviceId, sessionId, instanceId } = this;
    const url = this.recurly.url(route);

    data = deepAssign({}, data, { version, key, deviceId, sessionId, instanceId });

    if (this.shouldUseXHR) {
      return this.xhr({ method, url, data });
    } else {
      return this.jsonp({ method, url, data });
    }
  }

  /**
   * Performs an API request with caching
   */
  cached ({ method, route, data }) {
    let { cache } = this;
    const slug = `${method}-${route}-${JSON.stringify(data)}`;
    if (cache[slug]) return Promise.resolve(cache[slug]);
    return this.request({ method, route, data }).then(res => cache[slug] = res);
  }

  /**
   * Performs an API request that queues until it is able to be sent
   */
  queued (...args) {
    if (this.isConfigured) return this.request(...args);
    return new Promise((resolve, reject) => {
      this.recurly.once('configured', () => this.request(...args).then(resolve, reject));
    });
  }

  /**
   * Pipelines a request across several requests
   *
   * Chunks are made of `data[by]` in `size`. If any request errors,
   * the entire pipeline errors
   *
   * - Ignores individual 404s
   * - Rejects if all responses are 404s
   * - Immediately rejects other errors
   * - Collects and concats array responses
   * - Resolves non-array responses with the first response
   *
   * @param {String} method
   * @param {String} route
   * @param {Object} data
   * @param {Array} data.by must be an Array
   * @param {String} by
   * @param {Number} size=100
   * @return {Promise}
   */
  piped ({ method = 'get', route, data = {}, by, size = 100 }) {
    const chunks = chunk(data[by], size).map(piece => {
      return Object.assign({}, data, { [by]: piece });
    });

    if (chunks.length === 0) {
      return this.request({ method, route, data });
    }

    const ignoreNotfound = error => (error.code === 'not-found' ? { error } : Promise.reject(error));
    const pipeline = chunks.map(data => this.request({ method, route, data }).catch(ignoreNotfound));

    return Promise.all(pipeline).then(results => {
      const successes = results.filter(res => !res.error);

      if (successes.length === 0) {
        return Promise.reject(results[0].error);
      }

      // Determines if any single success response is not an Array.
      // If so, we resolve with that single response
      const nonArraySuccess = successes.filter(res => !Array.isArray(res))[0];
      if (nonArraySuccess) return Promise.resolve(nonArraySuccess);

      // If we are left entirely with successful Array responses, resolve
      // with a concatenated Array of them all
      return Promise.resolve(successes.reduce((acc, cur) => acc.concat(cur)));
    });
  }

  /**
   * Issues an API request over xhr.
   *
   * @private
   * @param {String} method
   * @param {String} url
   * @param {Object} [data]
   * @return {Promise}
   */
  xhr ({ method, url, data }) {
    return new Promise((resolve, reject) => {
      let req = new XHR;
      const payload = qs.stringify(data, { encodeValuesOnly: true });
      const { timeout } = this;

      if (method === 'get') {
        url += `?${payload}`;
      }

      req.open(method, url);
      req.timeout = timeout;
      req.ontimeout = () => reject(errors('api-timeout'));
      req.onerror = () => reject(errors('api-error'));
      req.onprogress = NOOP;
      req.onload = function () {
        let res;
        try {
          if (this.responseText) res = JSON.parse(this.responseText);
        } catch (e) {
          debug(e, this.responseText);
          return reject(errors('api-error', {
            message: `There was a problem parsing the API response with: ${this.responseText}`
          }));
        }

        if (res && res.error) {
          reject(errors('api-error', res.error));
        } else {
          resolve(res);
        }
      };

      // XDR requests will abort if too many are sent simultaneously
      setTimeout(() => {
        if (method === 'post') {
          // XDR cannot set Content-type
          if (req.setRequestHeader) {
            req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
          }
          req.send(payload);
        } else {
          req.send();
        }
      }, 0);
    });
  }

  /**
   * Issues an API request over jsonp.
   *
   * @private
   * @param {String} method
   * @param {String} url
   * @param {Object} [data]
   */
  jsonp ({ method, url, data }) {
    const { timeout } = this;

    if (method === 'post') data._method = method;
    url += `?${qs.stringify(data, { encodeValuesOnly: true })}`;

    return new Promise((resolve, reject) => {
      jsonp(url, { prefix: '__rjs', timeout }, function (err, res) {
        if (err) return reject(err);
        if (res.error) {
          reject(errors('api-error', res.error));
        } else {
          resolve(res);
        }
      });
    });
  }
}
<|endoftext|>


<commit_before>import React from 'react';

const App = () =>
Hello

export default App
<commit_msg>[NEW] React Component<commit_after>import React from 'react';

class App extends React.Component {
  render() {
    return
Hello World

  }
}

export default App
<|endoftext|>


<commit_before>/*
 * theme.js: Methods for loading the site's theme.
 *
 * (C) 2011, Nodejitsu Inc.
 *
 */

var smith = require('../blacksmith'),
    findit  = require('findit'),
    path    = require('path'),
    mkdirp  = require('mkdirp'),
    fs      = require('fs'),
    fs2     = require('../fs2'),
    helpers = require('../helpers');

var theme = exports;

theme.weld = function (dom, data) {

  // There is no real "weld" step for templates.
  return data;

};

theme.generate = function (output, files) {

  // Write theme files that *aren't* used as templates to disk.
  Object.keys(files).forEach(function (filePath) {

    // This list of template files gets filtered from the write step.
    // TODO: Find a way to generate this list programmatically.
    var templates = ['index.html', 'error.html', 'article.html'];

    if (templates.indexOf(path.basename(filePath)) !== -1) {
      return;
    }

    var newPath = smith.src + "/../public/" + filePath;

    smith.log.info("Writing " + newPath);

    fs2.writeFile(newPath, files[filePath], function (){ });
  });
};
<commit_msg>[fix] No more printed relative paths<commit_after>/*
 * theme.js: Methods for loading the site's theme.
 *
 * (C) 2011, Nodejitsu Inc.
 *
 */

var smith = require('../blacksmith'),
    findit  = require('findit'),
    path    = require('path'),
    mkdirp  = require('mkdirp'),
    fs      = require('fs'),
    fs2     = require('../fs2'),
    helpers = require('../helpers');

var theme = exports;

theme.weld = function (dom, data) {

  // There is no real "weld" step for templates.
  return data;

};

theme.generate = function (output, files) {

  // Write theme files that *aren't* used as templates to disk.
  Object.keys(files).forEach(function (filePath) {

    // This list of template files gets filtered from the write step.
    // TODO: Find a way to generate this list programmatically.
    var templates = ['index.html', 'error.html', 'article.html'];

    if (templates.indexOf(path.basename(filePath)) !== -1) {
      return;
    }

    var newPath = path.resolve(smith.src + "/../public/" + filePath);

    smith.log.debug("Writing " + newPath);

    fs2.writeFile(newPath, files[filePath], function (){ });
  });
};
<|endoftext|>