<commit_before>// =SECTION Setup

function visible() {
  Array.prototype.slice.call(arguments).forEach(function (s) {
    var el = $(s);
    var status;
    if (! el.length) {
      status = "does not exist";
    } else if (el.is(":visible")) {
      status = "visible";
    } else {
      status = "hidden";
    }
    print(s, ":", status);
  });
}

Test.require("ui", "chat", "util", "session", "jquery", "storage", "peers", "cursor", "windowing");
// => Loaded modules: ...

Test.normalStartup();

// => ...

Test.newPeer();

// => ...

visible("#towtruck-chat-notifier");

// => #towtruck-chat-notifier : visible

$("#towtruck-chat-notifier .towtruck-dismiss").click();

Test.incoming({
  type: "chat",
  clientId: "faker",
  text: "Test",
  messageId: "test-message"
});

// => ...

visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : visible
#towtruck-chat : hidden
*/

$("#towtruck-chat-button").click();
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : visible
*/

$("#towtruck-chat .towtruck-close").click();
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : hidden
*/

$("#towtruck-chat-button").click();

Test.incoming({
  type: "chat",
  clientId: "faker",
  text: "Test 2",
  messageId: "test-message"
});
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : visible
*/

$("#towtruck-chat .towtruck-close").click();
Test.incoming({
  type: "bye",
  clientId: "faker"
});

// =>

visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : visible
#towtruck-chat : hidden
*/

// Now we'll bring back the user:
Test.newPeer({url: "http://example.com/foo"});

// => ...

visible(".towtruck-follow");

/* =>
.towtruck-follow : visible
*/

$(".towtruck-nudge").click();

/* =>
send: url-change-nudge
  clientId: "me",
  to: "faker",
  url: "..."

 */

// =SECTION Helpers

Test.addControl('
Some perhaps helpful test buttons to trigger events (not all combos are valid):
');

Test.addControl($("<button>Faker Rejoin</button>").click(function () {
  Test.newPeer();
}));

Test.addControl($("<button>Join Random New Faker</button>").click(function () {
  var id = Math.floor(Math.random() * 1000);
  Test.newPeer({
    name: "Faker " + id,
    clientId: "random-" + id
  });
}));

Test.addControl($('<input placeholder="Change Faker URL">').keypress(function (ev) {
  if (ev.which == 13) {
    Test.newPeer({url: ev.target.value});
    ev.target.value = "";
  }
}));

Test.addControl($('<button>Faker Leave</button>').click(function () {
  Test.incoming({
    type: "bye",
    clientId: "faker"
  });
}));

Test.addControl($('<button>Faker decline</button>').click(function () {
  Test.incoming({
    type: "bye",
    clientId: "faker",
    reason: "declined-join"
  });
}));

Test.addControl($('<button>Nudge Me</button>').click(function () {
  Test.incoming({
    type: "url-change-nudge",
    clientId: "faker",
    url: peers.getPeer("faker").url,
    to: peers.Self.id
  });
}));

Test.addControl($('<button>Keyboard</button>').click(function () {
  Test.incoming({
    type: "keydown",
    clientId: "faker"
  });
}));
<commit_msg>Fix test so it doesn't fail due to animations<commit_after>// =SECTION Setup

function visible() {
  Array.prototype.slice.call(arguments).forEach(function (s) {
    var el = $(s);
    var status;
    if (! el.length) {
      status = "does not exist";
    } else if (el.is(":visible")) {
      status = "visible";
    } else {
      status = "hidden";
    }
    print(s, ":", status);
  });
}

Test.require("ui", "chat", "util", "session", "jquery", "storage", "peers", "cursor", "windowing");
// => Loaded modules: ...

Test.normalStartup();

// => ...

Test.newPeer();

// => ...

visible("#towtruck-chat-notifier");

// => #towtruck-chat-notifier : visible

$("#towtruck-chat-notifier .towtruck-dismiss").click();

Test.incoming({
  type: "chat",
  clientId: "faker",
  text: "Test",
  messageId: "test-message"
});

// => ...

visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : visible
#towtruck-chat : hidden
*/

$("#towtruck-chat-button").click();
// The animation makes this take a while:
// (We could check $("#towtruck-chat-notifier").queue().length though)
wait(function () {return ! $("#towtruck-chat-notifier").is(":visible");});
// =>
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : visible
*/

$("#towtruck-chat .towtruck-close").click();
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : hidden
*/

$("#towtruck-chat-button").click();

Test.incoming({
  type: "chat",
  clientId: "faker",
  text: "Test 2",
  messageId: "test-message"
});
visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : hidden
#towtruck-chat : visible
*/

$("#towtruck-chat .towtruck-close").click();
Test.incoming({
  type: "bye",
  clientId: "faker"
});

// =>

visible("#towtruck-chat-notifier", "#towtruck-chat");

/* =>
#towtruck-chat-notifier : visible
#towtruck-chat : hidden
*/

// Now we'll bring back the user:
Test.newPeer({url: "http://example.com/foo"});

// => ...

visible(".towtruck-follow");

/* =>
.towtruck-follow : visible
*/

$(".towtruck-nudge").click();

/* =>
send: url-change-nudge
  clientId: "me",
  to: "faker",
  url: "..."

 */

// =SECTION Helpers

Test.addControl('
Some perhaps helpful test buttons to trigger events (not all combos are valid):
');

Test.addControl($("<button>Faker Rejoin</button>").click(function () {
  Test.newPeer();
}));

Test.addControl($("<button>Join Random New Faker</button>").click(function () {
  var id = Math.floor(Math.random() * 1000);
  Test.newPeer({
    name: "Faker " + id,
    clientId: "random-" + id
  });
}));

Test.addControl($('<input placeholder="Change Faker URL">').keypress(function (ev) {
  if (ev.which == 13) {
    Test.newPeer({url: ev.target.value});
    ev.target.value = "";
  }
}));

Test.addControl($('<button>Faker Leave</button>').click(function () {
  Test.incoming({
    type: "bye",
    clientId: "faker"
  });
}));

Test.addControl($('<button>Faker decline</button>').click(function () {
  Test.incoming({
    type: "bye",
    clientId: "faker",
    reason: "declined-join"
  });
}));

Test.addControl($('<button>Nudge Me</button>').click(function () {
  Test.incoming({
    type: "url-change-nudge",
    clientId: "faker",
    url: peers.getPeer("faker").url,
    to: peers.Self.id
  });
}));

Test.addControl($('<button>Keyboard</button>').click(function () {
  Test.incoming({
    type: "keydown",
    clientId: "faker"
  });
}));
<|endoftext|>


<commit_before>/**
 * Created by lidavidm on 6/6/17.
 */

const CELL_SIZE = 1;
const PLAYER_COLORS = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF];

const DOCK_TURNS = 5;
const ATTACK_RADIUS = 5;

class FrameAnimation {
    constructor(frames, update, draw) {
        this.frames = frames;
        this.update = update;
        this.draw = draw;
    }
}

class HaliteVisualizer {
    constructor(replay) {
        this.replay = replay;
        this.frame = 0;
        this.substep = 0;
        // TODO: match aspect ratio of map
        this.application = new PIXI.Application(800, 800);

        this.scale = 800 / Math.max(replay.width, replay.height);

        this.planetContainer = new PIXI.Graphics();
        this.shipContainer = new PIXI.Graphics();
        this.application.stage.addChild(this.planetContainer);
        this.application.stage.addChild(this.shipContainer);

        this.timer = null;

        this.animationQueue = [];
    }

    get currentSubstep() {
        return this.replay.frames[this.frame][this.substep];
    }

    attach(containerEl) {
        $(containerEl).append(this.application.view);

        this.draw();
    }

    play() {
        if (this.timer) return;

        this.timer = window.setInterval(() => {
            for (let i = 0; i < 6; i++) {
                this.substep++;
                if (this.substep >= this.replay.frames[this.frame].length) {
                    this.substep = 0;
                    this.frame++;
                }

                if (this.frame >= this.replay.frames.length) {
                    this.pause();
                    this.frame = this.replay.frames.length - 1;
                    this.substep = this.replay.frames[this.frame].length - 1;
                    break;
                }

                this.update();
            }
        }, 1000/40);

        this.application.ticker.add(this.draw.bind(this));
    }

    pause() {
        if (!this.timer) return;

        window.clearInterval(this.timer);
        this.timer = null;
    }

    drawPlanet(planet) {
        let planetBase = this.replay.planets[planet.id];

        const side = CELL_SIZE * this.scale;

        for (let dx = -planetBase.r; dx <= planetBase.r; dx++) {
            for (let dy = -planetBase.r; dy <= planetBase.r; dy++) {
                if (dx*dx + dy*dy <= planetBase.r*planetBase.r) {
                    const x = side * (dx + planetBase.x);
                    const y = side * (dy + planetBase.y);

                    this.planetContainer.beginFill(0xFFFFFF);
                    this.planetContainer.drawRect(x, y, side, side);
                }
            }
        }

        if (planet.owner !== null) {
            this.planetContainer.beginFill(PLAYER_COLORS[planet.owner]);
            this.planetContainer.drawCircle(side * (planetBase.x + 0.5), side * (planetBase.y + 0.5), side * planetBase.r / 2);
        }
    }

    drawShip(ship) {
        const width = CELL_SIZE * this.scale;
        const height = CELL_SIZE * this.scale;

        const x = width * ship.x;
        const y = width * ship.y;

        this.shipContainer.lineStyle(0);
        this.shipContainer.beginFill(PLAYER_COLORS[ship.owner]);
        this.shipContainer.drawRect(x, y, width, height);
        this.shipContainer.endFill();

        if (ship.docking.status !== "undocked") {
            let progress = ship.docking.status === "docked" ? DOCK_TURNS : DOCK_TURNS - ship.docking.turns_left;

            const planetId = ship.docking.planet_id;
            const planetBase = this.replay.planets[planetId];

            const planetX = width * planetBase.x;
            const planetY = width * planetBase.y;

            const dx = planetX - x;
            const dy = planetY - y;

            this.shipContainer.beginFill(PLAYER_COLORS[ship.owner]);
            this.shipContainer.lineStyle(2, PLAYER_COLORS[ship.owner], 0.5);
            if (ship.docking.status === "undocking") {
                // TODO:
            }
            else {
                progress /= DOCK_TURNS;
                this.shipContainer.moveTo(x, y);
                this.shipContainer.lineTo(x + progress*dx, y + progress*dy);
            }
            this.shipContainer.endFill();
        }
    }

    update() {
        if (this.currentSubstep.events) {
            for (let event of this.currentSubstep.events) {
                if (event.event === "destroyed") {
                    this.animationQueue.push(new FrameAnimation(
                        48,
                        () => {

                        },
                        (frame) => {
                            const width = CELL_SIZE * this.scale;
                            const height = CELL_SIZE * this.scale;

                            const x = width * event.x;
                            const y = width * event.y;

                            this.shipContainer.beginFill(0xFFA500, frame / 24);
                            this.shipContainer.lineStyle(0);
                            this.shipContainer.drawRect(x, y, width, height);
                            this.shipContainer.endFill();
                        },
                    ));
                }
                else if (event.event === "attack") {
                    this.animationQueue.push(new FrameAnimation(
                        24,
                        () => {
                        },
                        (frame) => {
                            const side = CELL_SIZE * this.scale;

                            const x = side * (event.x + 0.5);
                            const y = side * (event.y + 0.5);

                            this.shipContainer.lineStyle(2, 0xFFFFFF, 0.5 * frame / 24);
                            this.shipContainer.drawCircle(x, y, side * ATTACK_RADIUS);
                            this.shipContainer.endFill();
                        },
                    ));
                }
                else {
                    console.log(event);
                }
            }
        }
    }

    draw() {
        this.planetContainer.clear();
        this.shipContainer.clear();

        for (let planet of this.currentSubstep.planets) {
            this.drawPlanet(planet);
        }

        for (let ship of this.currentSubstep.ships) {
            this.drawShip(ship);
        }

        this.animationQueue = this.animationQueue.filter((anim) => {
            anim.draw(anim.frames);
            anim.frames--;
            return anim.frames > 0;
        });
    }
}

class HaliteVisualizerControls {
    constructor(replay) {
        this.visualizer = new HaliteVisualizer(replay);
    }

    attach(el) {
        $(el).empty();

        this.visualizer.attach(el);

        this.visualizer.play();
    }
}

function setupUpload() {
    const dragPopup = $(`<div class="halite-dnd-popup">`).appendTo(document.body);
    dragPopup.append($("
Drop to visualize
"));

    const visualizerEl = $("
").appendTo(document.body);

    document.body.addEventListener("dragenter", (e) => {
        e.stopPropagation();
        e.preventDefault();

        dragPopup.removeClass("hidden");
    });
    document.body.addEventListener("dragover", (e) => {
        e.stopPropagation();
        e.preventDefault();
    });
    document.body.addEventListener("dragexit", (e) => {
        e.stopPropagation();
        e.preventDefault();
        dragPopup.addClass("hidden");
    });

    document.body.addEventListener("drop", (e) => {
        e.stopPropagation();
        e.preventDefault();
        dragPopup.addClass("hidden");

        let dt = e.dataTransfer;
        let files = dt.files;

        let reader = new FileReader();
        reader.onload = function(e) {
            const replay = msgpack.decode(new Uint8Array(e.target.result));

            console.log(replay);

            let visualizer = new HaliteVisualizerControls(replay);
            visualizer.attach(visualizerEl);
        };
        reader.readAsArrayBuffer(files[0]);
    });
}

function setup() {
    setupUpload();
}
<commit_msg>Make visualization easier to read<commit_after>/**
 * Created by lidavidm on 6/6/17.
 */

const CELL_SIZE = 1;
const PLAYER_COLORS = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF];

const DOCK_TURNS = 5;
const ATTACK_RADIUS = 5;

class FrameAnimation {
    constructor(frames, update, draw) {
        this.frames = frames;
        this.update = update;
        this.draw = draw;
    }
}

class HaliteVisualizer {
    constructor(replay) {
        this.replay = replay;
        this.frame = 0;
        this.substep = 0;
        // TODO: match aspect ratio of map
        this.application = new PIXI.Application(800, 800);

        this.scale = 800 / Math.max(replay.width, replay.height);

        this.planetContainer = new PIXI.Graphics();
        this.shipContainer = new PIXI.Graphics();
        this.application.stage.addChild(this.planetContainer);
        this.application.stage.addChild(this.shipContainer);

        this.timer = null;

        this.animationQueue = [];
    }

    get currentSubstep() {
        return this.replay.frames[this.frame][this.substep];
    }

    attach(containerEl) {
        $(containerEl).append(this.application.view);

        this.draw();
    }

    play() {
        if (this.timer) return;

        this.timer = window.setInterval(() => {
            for (let i = 0; i < 6; i++) {
                this.substep++;
                if (this.substep >= this.replay.frames[this.frame].length) {
                    this.substep = 0;
                    this.frame++;
                }

                if (this.frame >= this.replay.frames.length) {
                    this.pause();
                    this.frame = this.replay.frames.length - 1;
                    this.substep = this.replay.frames[this.frame].length - 1;
                    break;
                }

                this.update();
            }
        }, 1000/40);

        this.application.ticker.add(this.draw.bind(this));
    }

    pause() {
        if (!this.timer) return;

        window.clearInterval(this.timer);
        this.timer = null;
    }

    drawPlanet(planet) {
        let planetBase = this.replay.planets[planet.id];

        const side = CELL_SIZE * this.scale;

        this.planetContainer.lineStyle(0);
        for (let dx = -planetBase.r; dx <= planetBase.r; dx++) {
            for (let dy = -planetBase.r; dy <= planetBase.r; dy++) {
                if (dx*dx + dy*dy <= planetBase.r*planetBase.r) {
                    const x = side * (dx + planetBase.x);
                    const y = side * (dy + planetBase.y);
                    if (planet.owner !== null) {
                        this.planetContainer.beginFill(PLAYER_COLORS[planet.owner]);
                    }
                    else {
                        this.planetContainer.beginFill(0xA56729);
                    }
                    this.planetContainer.drawRect(x, y, side, side);
                    this.planetContainer.endFill();
                }
            }
        }

        const center_x = side * planetBase.x;
        const center_y = side * (planetBase.y + 0.5);

        const health_factor = planet.health / planetBase.health;
        const health_bar = health_factor * side * (planetBase.r - 1);
        this.planetContainer.beginFill(0xFF0000);
        this.planetContainer.lineStyle(2, 0x000000);
        this.planetContainer.drawRect(center_x, center_y - health_bar, side, 2 * health_bar);
        this.planetContainer.endFill();
    }

    drawShip(ship) {
        const side = CELL_SIZE * this.scale;

        const x = side * ship.x;
        const y = side * ship.y;

        this.shipContainer.lineStyle(1, 0x000000);
        this.shipContainer.beginFill(PLAYER_COLORS[ship.owner]);
        this.shipContainer.drawRect(x, y, side, side);
        this.shipContainer.endFill();

        if (ship.docking.status !== "undocked") {
            let progress = ship.docking.status === "docked" ? DOCK_TURNS : DOCK_TURNS - ship.docking.turns_left;

            const planetId = ship.docking.planet_id;
            const planetBase = this.replay.planets[planetId];

            const planetX = side * (planetBase.x + 0.5);
            const planetY = side * (planetBase.y + 0.5);

            const cx = x + 0.5*side;
            const cy = y + 0.5*side;

            const dx = planetX - cx;
            const dy = planetY - cy;

            this.shipContainer.beginFill(PLAYER_COLORS[ship.owner]);
            this.shipContainer.lineStyle(2, 0xFFFFFF, 1);
            if (ship.docking.status === "undocking") {
                // TODO:
            }
            else {
                progress /= DOCK_TURNS;
                this.shipContainer.moveTo(cx, cy);
                this.shipContainer.lineTo(cx + progress*dx, cy + progress*dy);
            }
            this.shipContainer.endFill();
        }
    }

    update() {
        if (this.currentSubstep.events) {
            for (let event of this.currentSubstep.events) {
                if (event.event === "destroyed") {
                    this.animationQueue.push(new FrameAnimation(
                        48,
                        () => {

                        },
                        (frame) => {
                            const width = CELL_SIZE * this.scale;
                            const height = CELL_SIZE * this.scale;

                            const x = width * event.x;
                            const y = width * event.y;

                            this.shipContainer.beginFill(0xFFA500, frame / 24);
                            this.shipContainer.lineStyle(0);
                            this.shipContainer.drawRect(x, y, width, height);
                            this.shipContainer.endFill();
                        },
                    ));
                }
                else if (event.event === "attack") {
                    this.animationQueue.push(new FrameAnimation(
                        24,
                        () => {
                        },
                        (frame) => {
                            const side = CELL_SIZE * this.scale;

                            const x = side * (event.x + 0.5);
                            const y = side * (event.y + 0.5);

                            this.shipContainer.lineStyle(2, 0xFFFFFF, 0.5 * frame / 24);
                            this.shipContainer.drawCircle(x, y, side * ATTACK_RADIUS);
                            this.shipContainer.endFill();
                        },
                    ));
                }
                else {
                    console.log(event);
                }
            }
        }
    }

    draw() {
        this.planetContainer.clear();
        this.shipContainer.clear();

        for (let planet of this.currentSubstep.planets) {
            this.drawPlanet(planet);
        }

        for (let ship of this.currentSubstep.ships) {
            this.drawShip(ship);
        }

        this.animationQueue = this.animationQueue.filter((anim) => {
            anim.draw(anim.frames);
            anim.frames--;
            return anim.frames > 0;
        });
    }
}

class HaliteVisualizerControls {
    constructor(replay) {
        this.visualizer = new HaliteVisualizer(replay);
    }

    attach(el) {
        $(el).empty();

        this.visualizer.attach(el);

        this.visualizer.play();
    }
}

function setupUpload() {
    const dragPopup = $(`<div class="halite-dnd-popup">`).appendTo(document.body);
    dragPopup.append($("
Drop to visualize
"));

    const visualizerEl = $("
").appendTo(document.body);

    document.body.addEventListener("dragenter", (e) => {
        e.stopPropagation();
        e.preventDefault();

        dragPopup.removeClass("hidden");
    });
    document.body.addEventListener("dragover", (e) => {
        e.stopPropagation();
        e.preventDefault();
    });
    document.body.addEventListener("dragexit", (e) => {
        e.stopPropagation();
        e.preventDefault();
        dragPopup.addClass("hidden");
    });

    document.body.addEventListener("drop", (e) => {
        e.stopPropagation();
        e.preventDefault();
        dragPopup.addClass("hidden");

        let dt = e.dataTransfer;
        let files = dt.files;

        let reader = new FileReader();
        reader.onload = function(e) {
            const replay = msgpack.decode(new Uint8Array(e.target.result));

            console.log(replay);

            let visualizer = new HaliteVisualizerControls(replay);
            visualizer.attach(visualizerEl);
        };
        reader.readAsArrayBuffer(files[0]);
    });
}

function setup() {
    setupUpload();
}
<|endoftext|>


<commit_before>/**
  @module ember-time-tools
*/
import Ember from 'ember';
import layout from '../templates/components/tt-calendar-month';
import eventTemplate from '../templates/components/tt-calendar-month-event';

const computed = Ember.computed;

var Day = Ember.Object.extend({
  date: null,
  month: null,
  year: null,
  inMonth: false
});

/**
  @class CalendarMonthComponent
  @namespace Calendar
*/
export default Ember.Component.extend({
  layout,

  /**
    This is the template to use for the events displayed in the calendar.

    @property eventTemplate
    @type {String}
  */
  eventTemplate: eventTemplate,

  /**
    @property classNames
    @type {Array}
    @default `['tt-calendar-month', 'tt-calendar-container']`
    @private
  */
  classNames: ['tt-calendar-month', 'tt-calendar-container'],

  /**
    These are the names for each day (column) displayed in the header.

    @property dayNames
    @type {Array}
    @default `['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']`
  */
  dayNames: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],

  /**
    @property monthNames
    @type {Array}
    @default `['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']`
  */
  monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],

  /**
    An array of event models.  Each event should have at least `start`, `end`
    and `description` properties.

      ```
      var event = {
        description: 'Event Title'
        start: timestamp,
        end: timestamp
      }
      ```

    The timestamp can be an integer (milliseconds since 1/1/1970) or a Date object.

    @property events
    @type {Array}
  */
  events: null,

  /**
    @property sortProperties
    @type {Array}
    @default `['start:asc', 'end:asc']`
    @private
  */
  sortProperties: ['start:asc', 'end:asc'],

  /**
    Sorts the `events` array.

    @property sortedEvents
    @type {Array}
    @private
  */
  sortedEvents: computed.sort('events', 'sortProperties'),

  /**
    The selected model/event.

    @property selected
    @type {Object}
  */
  selected: null,

  /**
    A `Date()` object representing the currently selected date.

    @property selectedDate
    @type {Object}
  */
  selectedDate: null,

  /**
    @method init
    @private
  */
  init: function () {
    this.setToday();
    return this._super();
  },

  /**
    The year of the `selectedDate`.

    @property year
    @type {Number}
    @private
  */
  year: computed( 'selectedDate', function() {
    var date = this.get('selectedDate') || new Date();
    return date.getFullYear();
  }),

  /**
    The month of the `selectedDate`.

    @property month
    @type {Number}
    @private
  */
  month: computed( 'selectedDate', function() {
    var date = this.get('selectedDate') || new Date();
    return date.getMonth();
  }),

  /**
    The month name string.. ie. 'January'.

    @property monthName
    @type {String}
    @private
  */
  monthName: computed( 'month', 'monthNames', function () {
    var { month, monthNames } = this.getProperties([ 'month', 'monthNames' ]);
    return monthNames[ month ];
  }),

  /**
    The number of the day ofthe week that the calendar will start on.
    Day numbers start at 0 = monday.

    @property startDay
    @type {Number}
    @private
  */
  startDay: computed( 'year', 'month', function () {
    var month = this.get('month'),
      year = this.get('year'),
      newCal = new Date(year, month, 1),
      startDay = newCal.getDay() - 1;

    if (startDay < 0) {
      startDay = 6;
    }

    return startDay;
  }),

  /**
    @property weeks
    @type {Array}
    @private
  */
  weeks: computed( 'month', 'year', 'startDay', function () {

    const rows = 6;
    const days = 7;
    var out = Ember.A();
    var { month, year, startDay } = this.getProperties([ 'month', 'year', 'startDay' ]);
    var lastMonth = this.getLastMonth( month );
    var daily = 0;
    var dailyNextMonth = 1;
    var daysInLastMonth = this.getDaysInMonth({ year:year, month: lastMonth });
    var daysInMonth = this.getDaysInMonth({ year:year, month:month });
    var outOfMonthDayObject = function (d) {
      var y = year;
      if ( 0 === daily ) {
        return {
          date: daysInLastMonth - (startDay - d)+1,
          month: (function (month) {
            var newMonth = month - 1;
            if (newMonth < 0) {
              newMonth += 12;
              y--;
            }
            return newMonth;
          })(month),
          year: y
        };
      } else {
        return {
          date: dailyNextMonth++,
          month: (function (month) {
            var newMonth = month + 1;
            if (newMonth > 11) {
              newMonth -= 12;
              y++;
            }
            return newMonth;
          })(month),
          year: y
        };
      }
    };

    for ( var w = 0; w < rows; w++ ) {
      var daysArray = Ember.A();

      for (var d = 0; d < days; d++) {
        // start the ball rolling when `d` is equal to `startDay`
        if ((d === startDay) && (0 === daily)) {
          daily = 1;
        }

        if ((daily > 0) && daily <= daysInMonth) {
          daysArray.pushObject(Day.create({
            date:daily++,
            month: month,
            year: year,
            inMonth: true
          }));
        } else {
          daysArray.pushObject( Day.create( outOfMonthDayObject( d ) ) );
        }
      }

      out.pushObject( daysArray );
    }
    return out;
  }),

  /**
    Pass in a month number and this function will return the previous month.

    @method getLastMonth
    @param {Integer} month starting at 0 = January
    @private
    @return {Integer}
  */
  getLastMonth(month) {
    var lastMonth = month - 1;
    if (lastMonth < 0) {
      lastMonth += 12;
    }
    return lastMonth;
  },

  /**
    Get the number of days in the month.

    The `date` object passed in should be a POJO with `month` and `year` properties.

      ```
      {
        year: 2015,
        month: 11 // starting from 0, 11 = December.
      }
      ```

    @method getDaysInMonth
    @param {Object} date
    @private
    @return {Integer} The number of days in the month
  */
  getDaysInMonth(date) {
    const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    var year = parseInt(date.year),
      month = parseInt(date.month);

    // test for leapyear when february is selected
    if (1 === month) {
      return ((0 === year % 4) && (0 !== (year % 100))) || (0 === year % 400) ? 29 : 28;
    } else {
      return daysInMonth[month];
    }
  },

  /**
    Sets the `selected` property to `model`.

    This function is called when an event is clicked.

    @method select
    @param {Object} model
    @private
  */
  select( model ) {
    this.set( 'selected', model );
  },

  /**
    This is an empty method, it does nothing, just returns false.  It is meant
    only as a stub, to be extended, or passed in as a closure action via the
    template.

    The function will be called when an event is double-clicked.

    @method dblClick
    @private
    @return {Boolean} false
  */
  dblClick() {
    return false;
  },

  /**
    @method setToday
    @private
    @param {Object} date (optional) a `Date` object.
  */
  setToday: function (date) {
    if (!date) {
      date = new Date();
    }
    this.set('selectedDate', date);
  },

  /**
    @method _nextMonth
    @private
  */
  _nextMonth() {
    var date = this.get('selectedDate');
    if ( !date ) {
      date = new Date();
    }
    date.setMonth(date.getMonth() + 1);
    this.set('selectedDate', new Date(date));
  },

  /**
    @method _prevMonth
    @private
  */
  _prevMonth: function () {
    var date = this.get('selectedDate');
    if ( !date ) {
      date = new Date();
    }
    date.setMonth(date.getMonth() - 1);
    this.setToday(new Date(date));
  },

  actions: {
    /**
      ACTION

      @method nextMonth
    */
    nextMonth() {
      this._nextMonth();
    },

    /**
      ACTION

      @method prevMonth
    */
    prevMonth() {
      this._prevMonth();
    },

    /**
      ACTION

      @method today
    */
    today: function () {
      this.setToday();
    }
  }
});
<commit_msg>update calendar-month component<commit_after>/**
  @module ember-time-tools
*/
import Ember from 'ember';
import layout from '../templates/components/tt-calendar-month';
import eventTemplate from '../templates/components/tt-calendar-month-event';

const computed = Ember.computed;

var Day = Ember.Object.extend({
  date: null,
  month: null,
  year: null,
  inMonth: false
});

/**
  @class CalendarMonthComponent
  @namespace Calendar
*/
export default Ember.Component.extend({
  layout,

  /**
    This is the template to use for the events displayed in the calendar.

    @property eventTemplate
    @type {String}
  */
  eventTemplate: eventTemplate,

  /**
    @property classNames
    @type {Array}
    @default `['tt-calendar-month', 'tt-calendar-container']`
    @private
  */
  classNames: ['tt-calendar-month', 'tt-calendar-container'],

  /**
    These are the names for each day (column) displayed in the header.

    @property dayNames
    @type {Array}
    @default `['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']`
  */
  dayNames: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],

  /**
    @property monthNames
    @type {Array}
    @default `['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']`
  */
  monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],

  /**
    An array of event models.  Each event should have at least `start`, `end`
    and `description` properties.

      ```
      var event = {
        description: 'Event Title'
        start: timestamp,
        end: timestamp
      }
      ```

    The timestamp can be an integer (milliseconds since 1/1/1970) or a Date object.

    @property events
    @type {Array}
  */
  events: null,

  /**
    @property sortProperties
    @type {Array}
    @default `['start:asc', 'end:asc']`
    @private
  */
  sortProperties: ['start:asc', 'end:asc'],

  /**
    Sorts the `events` array.

    @property sortedEvents
    @type {Array}
    @private
  */
  sortedEvents: computed.sort('events', 'sortProperties'),

  /**
    The selected model/event.

    @property selected
    @type {Object}
  */
  selected: null,

  /**
    A `Date()` object representing the currently selected date.

    @property selectedDate
    @type {Object}
  */
  selectedDate: null,

  /**
    @method init
    @private
  */
  init: function () {
    this.setToday();
    return this._super();
  },

  /**
    The year of the `selectedDate`.

    @property year
    @type {Number}
    @private
  */
  year: computed( 'selectedDate', function() {
    var date = this.get('selectedDate') || new Date();
    return date.getFullYear();
  }),

  /**
    The month of the `selectedDate`.

    @property month
    @type {Number}
    @private
  */
  month: computed( 'selectedDate', function() {
    var date = this.get('selectedDate') || new Date();
    return date.getMonth();
  }),

  /**
    The month name string.. ie. 'January'.

    @property monthName
    @type {String}
    @private
  */
  monthName: computed( 'month', 'monthNames', function () {
    var { month, monthNames } = this.getProperties([ 'month', 'monthNames' ]);
    return monthNames[ month ];
  }),

  /**
    The number of the day ofthe week that the calendar will start on.
    Day numbers start at 0 = monday.

    @property startDay
    @type {Number}
    @private
  */
  startDay: computed( 'year', 'month', function () {
    var month = this.get('month'),
      year = this.get('year'),
      newCal = new Date(year, month, 1),
      startDay = newCal.getDay() - 1;

    if (startDay < 0) {
      startDay = 6;
    }

    return startDay;
  }),

  /**
    @property weeks
    @type {Array}
    @private
  */
  weeks: computed( 'month', 'year', 'startDay', function () {

    const rows = 6;
    const days = 7;
    var out = Ember.A();
    var { month, year, startDay } = this.getProperties([ 'month', 'year', 'startDay' ]);
    var lastMonth = this.getLastMonth( month );
    var daily = 0;
    var dailyNextMonth = 1;
    var daysInLastMonth = this.getDaysInMonth({ year:year, month: lastMonth });
    var daysInMonth = this.getDaysInMonth({ year:year, month:month });
    var outOfMonthDayObject = function (d) {
      var y = year;
      if ( 0 === daily ) {
        return {
          date: daysInLastMonth - (startDay - d)+1,
          month: (function (month) {
            var newMonth = month - 1;
            if (newMonth < 0) {
              newMonth += 12;
              y--;
            }
            return newMonth;
          })(month),
          year: y
        };
      } else {
        return {
          date: dailyNextMonth++,
          month: (function (month) {
            var newMonth = month + 1;
            if (newMonth > 11) {
              newMonth -= 12;
              y++;
            }
            return newMonth;
          })(month),
          year: y
        };
      }
    };

    for ( var w = 0; w < rows; w++ ) {
      var daysArray = Ember.A();

      for (var d = 0; d < days; d++) {
        // start the ball rolling when `d` is equal to `startDay`
        if ((d === startDay) && (0 === daily)) {
          daily = 1;
        }

        if ((daily > 0) && daily <= daysInMonth) {
          daysArray.pushObject(Day.create({
            date:daily++,
            month: month,
            year: year,
            inMonth: true
          }));
        } else {
          daysArray.pushObject( Day.create( outOfMonthDayObject( d ) ) );
        }
      }

      out.pushObject( daysArray );
    }
    return out;
  }),

  /**
    Pass in a month number and this function will return the previous month.

    @method getLastMonth
    @param {Integer} month starting at 0 = January
    @private
    @return {Integer}
  */
  getLastMonth(month) {
    var lastMonth = month - 1;
    if (lastMonth < 0) {
      lastMonth += 12;
    }
    return lastMonth;
  },

  /**
    Get the number of days in the month.

    The `date` object passed in should be a POJO with `month` and `year` properties.

      ```
      {
        year: 2015,
        month: 11 // starting from 0, 11 = December.
      }
      ```

    @method getDaysInMonth
    @param {Object} date
    @private
    @return {Integer} The number of days in the month
  */
  getDaysInMonth(date) {
    const daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    var year = parseInt(date.year),
      month = parseInt(date.month);

    // test for leapyear when february is selected
    if (1 === month) {
      return ((0 === year % 4) && (0 !== (year % 100))) || (0 === year % 400) ? 29 : 28;
    } else {
      return daysInMonth[month];
    }
  },

  /**
    Sets the `selected` property to `model`.

    This function is called when an event is clicked.

    @method select
    @param {Object} model
    @private
  */
  select( model ) {
    this.set( 'selected', model );
  },

  /**
    This is an empty method, it does nothing, just returns false.  It is meant
    only as a stub, to be extended, or passed in as a closure action via the
    template.

    The function will be called when an event is double-clicked.

    @method dblClick
    @private
    @return {Boolean} false
  */
  dblClick() {
    return false;
  },

  /**
    @method setToday
    @private
    @param {Object} date (optional) a `Date` object.
  */
  setToday: function (date) {
    if (!date) {
      date = new Date();
    }
    this.set('selectedDate', date);
  },

  /**
    @method _nextMonth
    @private
  */
  _nextMonth() {
    var date = this.get('selectedDate');
    if ( !date ) {
      date = new Date();
    }
    date.setDate(1);
    date.setMonth(date.getMonth() + 1);
    this.set('selectedDate', new Date(date));
  },

  /**
    @method _prevMonth
    @private
  */
  _prevMonth: function () {
    var date = this.get('selectedDate');
    if ( !date ) {
      date = new Date();
    }
    date.setDate(1);
    date.setMonth(date.getMonth() - 1);
    this.setToday(new Date(date));
  },

  actions: {
    /**
      ACTION

      @method nextMonth
    */
    nextMonth() {
      this._nextMonth();
    },

    /**
      ACTION

      @method prevMonth
    */
    prevMonth() {
      this._prevMonth();
    },

    /**
      ACTION

      @method today
    */
    today: function () {
      this.setToday();
    }
  }
});
<|endoftext|>


<commit_before>/**
  @module ember-flexberry
*/

import Ember from 'ember';
import EditFormController from './edit-form';

/**
  Controller for the Detail's Edit Forms.
  It lets to interact between agregator's and details's forms.

  This class re-exports to the application as `/controllers/detail-edit-form`.
  So, you can inherit from `./detail-edit-form`, even if file `app/controllers/detail-edit-form.js` is not presented in the application.

  Example:
  ```javascript
  // app/controllers/order.js
  import DetailEditFormController from './detail-edit-form';
  export default DetailEditFormController.extend({
  });
  ```

  @class DetailEditFormController
  @extends EditFormController
*/
export default EditFormController.extend({
  /**
    A logic value showing if current route has parent one.

    @property _hasParentRoute
    @type Boolean
    @private
    @readOnly
  */
  _hasParentRoute: Ember.computed('modelCurrentAgregatorPathes', function() {
    let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
    let modelAgregatorRoutes = flexberryDetailInteractionService.get('modelCurrentAgregatorPathes');
    return flexberryDetailInteractionService.hasValues(this.get('modelCurrentAgregatorPathes')) ||
      flexberryDetailInteractionService.hasValues(modelAgregatorRoutes);
  }),

  /**
    Flag to enable return to agregator's path if possible.
    It overrides base default value.

    @property returnToAgregatorRoute
    @type Boolean
    @default true
  */
  returnToAgregatorRoute: true,

  /**
    Paths to return to after leaving current route.

    @property modelCurrentAgregatorPathes
    @type Array
  */
  modelCurrentAgregatorPathes: undefined,

  /**
    Current detail's agregators.

    @property modelCurrentAgregators
    @type Array
  */
  modelCurrentAgregators: undefined,

  /**
    Flag: indicates whether to save current model before going to the detail's route.
    This flag is set to `true` when this form is opened from agregator's form.

    @property saveBeforeRouteLeave
    @type Boolean
    @default false
  */
  saveBeforeRouteLeave: false,

  actions: {
    /**
      Handler for button 'Save' click.
      If return path is determined, no rollback happens and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.save
    */
    save() {
      this._saveInternalLogic();
      this._super.apply(this, arguments);
    },

    /**
      Handler for button 'Save and close' click.
      If return path is determined, no rollback happens and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.saveAndClose
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after save.
    */
    saveAndClose(skipTransition) {
      this._saveInternalLogic();
      this._super.apply(this, arguments);
    },

    /**
      Handler for button 'Delete' click.
      If return path is determined and current model is saved, record marks as deleted and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.delete
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after delete.
    */
    delete(skipTransition) {
      if (this.get('model').get('id') && this.get('_hasParentRoute') && !this.get('saveBeforeRouteLeave')) {
        if (confirm('Are you sure you want to delete that record?')) {
          this.get('model').deleteRecord();
          this.transitionToParentRoute(skipTransition, false);
        }
      } else {
        this.delete(skipTransition);
      }
    },

    /**
      Handler for button 'Close' click.
      If return path is determined, an error is thrown because this action should not be triggered.
      Otherwise base logic is executed.

      @method actions.close
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped.
    */
    close(skipTransition) {
      this.close(skipTransition);
    },
  },

  /**
    Сlose edit form and transition to parent route.

    @method close
    @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped.
  */
  close(skipTransition) {
    this._setFlexberryDetailInteractionSettings();
    if (!this.get('_hasParentRoute')) {
      this._super.apply(this, arguments);
      return;
    }

    if (this.get('saveBeforeRouteLeave')) {
      this._super.apply(this, [skipTransition, true]);
      return;
    }

    // If 'saveBeforeRouteLeave' == false & 'close' button has been pressed,
    // before transition to parent route we should validate model and then upload files
    // (if some file components are present on current detail rout),
    // because after transition, all components correspondent to current detail route (including file components)
    // gonna be destroyed, and files won't be uploaded at all.
    let model = this.get('model');
    model.validate().then(() => model.beforeSave({ softSave: true })).then(() => {
      this._super.apply(this, [skipTransition, false]);
    }, (reason) => {
      this.rejectError(reason);
    });
  },

  /**
    Save object.

    @method save
    @param {Boolean} close If `true`, then save and close.
    @param {Boolean} skipTransition If `true`, then transition after save process will be skipped.
    @return {Promise}
  */
  save(close, skipTransition) {
    this._saveInternalLogic();
    return this._super(...arguments);
  },

  /**
    Delete object, if successful transition to parent route.

    @method delete
    @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after delete.
    @return {Promise}
  */
  delete(skipTransition) {
    this._setFlexberryDetailInteractionSettings();
    return this._super(...arguments);
  },

  /**
    Method to transit to parent's route (previous route).
    If `modelAgregatorRoute` is set, transition to defined path and set flag 'modelNoRollBack' to `true` on controller to prevent rollback of model.
    Then if `parentRoute` is set, transition to defined path.
    Otherwise transition to corresponding list.

    @method transitionToParentRoute
    @param {Boolean} skipTransition If `true`, then transition will be skipped.
    @param {Boolean} rollBackModel Flag: indicates whether to set flag to roll back model after route leave (if `true`) or not (if `false`).
  */
  transitionToParentRoute(skipTransition, rollBackModel) {
    if (this.get('_hasParentRoute')) {
      if (!rollBackModel) {
        this.set('modelNoRollBack', true);
      }

      let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
      let modelAgregatorRoutes = this.get('modelCurrentAgregatorPathes') ? this.get('modelCurrentAgregatorPathes') :
        flexberryDetailInteractionService.get('modelCurrentAgregatorPathes');
      let modelAgregatorRoute = modelAgregatorRoutes.pop();
      let modelCurrentAgregators = this.get('modelCurrentAgregators') ? this.get('modelCurrentAgregators') :
        flexberryDetailInteractionService.get('modelCurrentAgregators');
      let modelCurrentAgregator = modelCurrentAgregators.pop();

      flexberryDetailInteractionService.set('modelCurrentAgregatorPathes', modelAgregatorRoutes);
      flexberryDetailInteractionService.set('modelCurrentAgregators', modelCurrentAgregators);
      flexberryDetailInteractionService.set('modelLastUpdatedDetail', this.get('model'));
      if (modelCurrentAgregator) {
        let store = Ember.getOwner(this).lookup('service:store');
        let agregatorIsOfflineModel = modelCurrentAgregator && store.get('offlineModels') &&
          store.get(`offlineModels.${modelCurrentAgregator.constructor.modelName}`);

        if (this.get('offlineGlobals.isOnline') && !agregatorIsOfflineModel) {
          flexberryDetailInteractionService.set('modelCurrentNotSaved', modelCurrentAgregator);
        }
      }

      if (!skipTransition) {
        if (modelAgregatorRoute.indexOf('/new') > 0 && modelCurrentAgregator.get('id')) {
          modelAgregatorRoute = modelAgregatorRoute.slice(1, -4);
        }

        this.transitionToRoute(modelAgregatorRoute, modelCurrentAgregator);
      }
    } else {
      this._super.apply(this, arguments);
    }
  },

  _saveInternalLogic() {
    if (this.get('_hasParentRoute') && !this.get('saveBeforeRouteLeave')) {
      throw new Error('\'Save\' operation is not accessible due to current settings.');
    }

    this._setFlexberryDetailInteractionSettings();
  },

  _setFlexberryDetailInteractionSettings() {
    let modelAgregatorRoutes = this.get('modelCurrentAgregatorPathes');
    let modelCurrentAgregators = this.get('modelCurrentAgregators');
    let saveBeforeRouteLeave = this.get('saveBeforeRouteLeave');
    let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
    flexberryDetailInteractionService.set('modelCurrentAgregatorPathes', modelAgregatorRoutes);
    flexberryDetailInteractionService.set('modelCurrentAgregators', modelCurrentAgregators);
    flexberryDetailInteractionService.set('saveBeforeRouteLeave', saveBeforeRouteLeave);
  }
});
<commit_msg>Fix detail-edit-form controller add missing rollBackModel param<commit_after>/**
  @module ember-flexberry
*/

import Ember from 'ember';
import EditFormController from './edit-form';

/**
  Controller for the Detail's Edit Forms.
  It lets to interact between agregator's and details's forms.

  This class re-exports to the application as `/controllers/detail-edit-form`.
  So, you can inherit from `./detail-edit-form`, even if file `app/controllers/detail-edit-form.js` is not presented in the application.

  Example:
  ```javascript
  // app/controllers/order.js
  import DetailEditFormController from './detail-edit-form';
  export default DetailEditFormController.extend({
  });
  ```

  @class DetailEditFormController
  @extends EditFormController
*/
export default EditFormController.extend({
  /**
    A logic value showing if current route has parent one.

    @property _hasParentRoute
    @type Boolean
    @private
    @readOnly
  */
  _hasParentRoute: Ember.computed('modelCurrentAgregatorPathes', function() {
    let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
    let modelAgregatorRoutes = flexberryDetailInteractionService.get('modelCurrentAgregatorPathes');
    return flexberryDetailInteractionService.hasValues(this.get('modelCurrentAgregatorPathes')) ||
      flexberryDetailInteractionService.hasValues(modelAgregatorRoutes);
  }),

  /**
    Flag to enable return to agregator's path if possible.
    It overrides base default value.

    @property returnToAgregatorRoute
    @type Boolean
    @default true
  */
  returnToAgregatorRoute: true,

  /**
    Paths to return to after leaving current route.

    @property modelCurrentAgregatorPathes
    @type Array
  */
  modelCurrentAgregatorPathes: undefined,

  /**
    Current detail's agregators.

    @property modelCurrentAgregators
    @type Array
  */
  modelCurrentAgregators: undefined,

  /**
    Flag: indicates whether to save current model before going to the detail's route.
    This flag is set to `true` when this form is opened from agregator's form.

    @property saveBeforeRouteLeave
    @type Boolean
    @default false
  */
  saveBeforeRouteLeave: false,

  actions: {
    /**
      Handler for button 'Save' click.
      If return path is determined, no rollback happens and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.save
    */
    save() {
      this._saveInternalLogic();
      this._super.apply(this, arguments);
    },

    /**
      Handler for button 'Save and close' click.
      If return path is determined, no rollback happens and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.saveAndClose
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after save.
    */
    saveAndClose(skipTransition) {
      this._saveInternalLogic();
      this._super.apply(this, arguments);
    },

    /**
      Handler for button 'Delete' click.
      If return path is determined and current model is saved, record marks as deleted and user is redirected to agregator's form.
      Otherwise base logic is executed.

      @method actions.delete
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after delete.
    */
    delete(skipTransition) {
      if (this.get('model').get('id') && this.get('_hasParentRoute') && !this.get('saveBeforeRouteLeave')) {
        if (confirm('Are you sure you want to delete that record?')) {
          this.get('model').deleteRecord();
          this.transitionToParentRoute(skipTransition, false);
        }
      } else {
        this.delete(skipTransition);
      }
    },

    /**
      Handler for button 'Close' click.
      If return path is determined, an error is thrown because this action should not be triggered.
      Otherwise base logic is executed.

      @method actions.close
      @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped.
      @param {Boolean} rollBackModel Flag: indicates whether to set flag to roll back model after route leave (if `true`) or not (if `false`).
    */
    close(skipTransition, rollBackModel) {
      this.close(skipTransition, rollBackModel);
    },
  },

  /**
    Сlose edit form and transition to parent route.

    @method close
    @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped.
    @param {Boolean} rollBackModel Flag: indicates whether to set flag to roll back model after route leave (if `true`) or not (if `false`).
  */
  close(skipTransition, rollBackModel) {
    this._setFlexberryDetailInteractionSettings();
    if (!this.get('_hasParentRoute')) {
      this._super.apply(this, arguments);
      return;
    }

    if (this.get('saveBeforeRouteLeave')) {
      this._super.apply(this, [skipTransition, true]);
      return;
    }

    // If 'saveBeforeRouteLeave' == false & 'close' button has been pressed,
    // before transition to parent route we should validate model and then upload files
    // (if some file components are present on current detail rout),
    // because after transition, all components correspondent to current detail route (including file components)
    // gonna be destroyed, and files won't be uploaded at all.
    let model = this.get('model');
    model.validate().then(() => model.beforeSave({ softSave: true })).then(() => {
      this._super.apply(this, [skipTransition, false]);
    }, (reason) => {
      this.rejectError(reason);
    });
  },

  /**
    Save object.

    @method save
    @param {Boolean} close If `true`, then save and close.
    @param {Boolean} skipTransition If `true`, then transition after save process will be skipped.
    @return {Promise}
  */
  save(close, skipTransition) {
    this._saveInternalLogic();
    return this._super(...arguments);
  },

  /**
    Delete object, if successful transition to parent route.

    @method delete
    @param {Boolean} skipTransition If `true`, then transition during close form process will be skipped after delete.
    @return {Promise}
  */
  delete(skipTransition) {
    this._setFlexberryDetailInteractionSettings();
    return this._super(...arguments);
  },

  /**
    Method to transit to parent's route (previous route).
    If `modelAgregatorRoute` is set, transition to defined path and set flag 'modelNoRollBack' to `true` on controller to prevent rollback of model.
    Then if `parentRoute` is set, transition to defined path.
    Otherwise transition to corresponding list.

    @method transitionToParentRoute
    @param {Boolean} skipTransition If `true`, then transition will be skipped.
    @param {Boolean} rollBackModel Flag: indicates whether to set flag to roll back model after route leave (if `true`) or not (if `false`).
  */
  transitionToParentRoute(skipTransition, rollBackModel) {
    if (this.get('_hasParentRoute')) {
      if (!rollBackModel) {
        this.set('modelNoRollBack', true);
      }

      let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
      let modelAgregatorRoutes = this.get('modelCurrentAgregatorPathes') ? this.get('modelCurrentAgregatorPathes') :
        flexberryDetailInteractionService.get('modelCurrentAgregatorPathes');
      let modelAgregatorRoute = modelAgregatorRoutes.pop();
      let modelCurrentAgregators = this.get('modelCurrentAgregators') ? this.get('modelCurrentAgregators') :
        flexberryDetailInteractionService.get('modelCurrentAgregators');
      let modelCurrentAgregator = modelCurrentAgregators.pop();

      flexberryDetailInteractionService.set('modelCurrentAgregatorPathes', modelAgregatorRoutes);
      flexberryDetailInteractionService.set('modelCurrentAgregators', modelCurrentAgregators);
      flexberryDetailInteractionService.set('modelLastUpdatedDetail', this.get('model'));
      if (modelCurrentAgregator) {
        let store = Ember.getOwner(this).lookup('service:store');
        let agregatorIsOfflineModel = modelCurrentAgregator && store.get('offlineModels') &&
          store.get(`offlineModels.${modelCurrentAgregator.constructor.modelName}`);

        if (this.get('offlineGlobals.isOnline') && !agregatorIsOfflineModel) {
          flexberryDetailInteractionService.set('modelCurrentNotSaved', modelCurrentAgregator);
        }
      }

      if (!skipTransition) {
        if (modelAgregatorRoute.indexOf('/new') > 0 && modelCurrentAgregator.get('id')) {
          modelAgregatorRoute = modelAgregatorRoute.slice(1, -4);
        }

        this.transitionToRoute(modelAgregatorRoute, modelCurrentAgregator);
      }
    } else {
      this._super.apply(this, arguments);
    }
  },

  _saveInternalLogic() {
    if (this.get('_hasParentRoute') && !this.get('saveBeforeRouteLeave')) {
      throw new Error('\'Save\' operation is not accessible due to current settings.');
    }

    this._setFlexberryDetailInteractionSettings();
  },

  _setFlexberryDetailInteractionSettings() {
    let modelAgregatorRoutes = this.get('modelCurrentAgregatorPathes');
    let modelCurrentAgregators = this.get('modelCurrentAgregators');
    let saveBeforeRouteLeave = this.get('saveBeforeRouteLeave');
    let flexberryDetailInteractionService = this.get('_flexberryDetailInteractionService');
    flexberryDetailInteractionService.set('modelCurrentAgregatorPathes', modelAgregatorRoutes);
    flexberryDetailInteractionService.set('modelCurrentAgregators', modelCurrentAgregators);
    flexberryDetailInteractionService.set('saveBeforeRouteLeave', saveBeforeRouteLeave);
  }
});
<|endoftext|>


<commit_before>/* global moment */
import Ember from 'ember';

const { Helper, isBlank } = Ember;

export default Helper.extend({
  compute([value, days, format]){
    console.log(days);
    if (isBlank(value)) {
      return null;
    }

    if (isBlank(format)) {
      return moment(value).subtract(days, 'days').format();
    } else {
      return moment(value).subtract(days, 'days').format(format);
    }
  }
});
<commit_msg>Removed console.log<commit_after>/* global moment */
import Ember from 'ember';

const { Helper, isBlank } = Ember;

export default Helper.extend({
  compute([value, days, format]){
    if (isBlank(value)) {
      return null;
    }

    if (isBlank(format)) {
      return moment(value).subtract(days, 'days').format();
    } else {
      return moment(value).subtract(days, 'days').format(format);
    }
  }
});
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
import { all } from 'rsvp';

export default class CourseVisualizationsRoute extends Route.extend(AuthenticatedRouteMixin) {
  @service store;

  titleToken = 'general.coursesAndSessions';

  /**
   * Prefetch related data to limit network requests
   */
  afterModel(model) {
    const courses = [model.get('id')];
    const course = model.get('id');
    const sessions = model.hasMany('sessions').ids();
    const existingSessionsInStore = this.store.peekAll('session');
    const existingSessionIds = existingSessionsInStore.mapBy('id');
    const unloadedSessions = sessions.filter(id => !existingSessionIds.includes(id));

    //if we have already loaded all of these sessions we can just proceed normally
    if (unloadedSessions.length === 0) {
      return;
    }

    const promises = [
      this.store.query('session', { filters: { course } }),
      this.store.query('offering', { filters: { courses } }),
      this.store.query('ilm-session', { filters: { courses } }),
      this.store.query('objective', { filters: { courses } }),
    ];
    const maximumSessionLoad = 100;
    if (sessions.length < maximumSessionLoad) {
      promises.pushObject(this.store.query('objective', { filters: { sessions } }));
      promises.pushObject(this.store.query('session-type', { filters: { sessions } }));
      promises.pushObject(this.store.query('term', { filters: { sessions } }));
    } else {
      for (let i = 0; i < sessions.length; i += maximumSessionLoad) {
        const slice = sessions.slice(i, i + maximumSessionLoad);
        promises.pushObject(this.store.query('objective', { filters: { sessions: slice } }));
        promises.pushObject(this.store.query('session-type', { filters: { sessions: slice } }));
        promises.pushObject(this.store.query('term', { filters: { sessions: slice } }));
      }
    }

    return all(promises);
  }
}
<commit_msg>preload session/course-objectives instead of legacy objectives.<commit_after>import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
import { all } from 'rsvp';

export default class CourseVisualizationsRoute extends Route.extend(AuthenticatedRouteMixin) {
  @service store;

  titleToken = 'general.coursesAndSessions';

  /**
   * Prefetch related data to limit network requests
   */
  afterModel(model) {
    const courses = [model.get('id')];
    const course = model.get('id');
    const sessions = model.hasMany('sessions').ids();
    const existingSessionsInStore = this.store.peekAll('session');
    const existingSessionIds = existingSessionsInStore.mapBy('id');
    const unloadedSessions = sessions.filter(id => !existingSessionIds.includes(id));

    //if we have already loaded all of these sessions we can just proceed normally
    if (unloadedSessions.length === 0) {
      return;
    }

    const promises = [
      this.store.query('session', { filters: { course } }),
      this.store.query('offering', { filters: { courses } }),
      this.store.query('ilm-session', { filters: { courses } }),
      this.store.query('course-objective', { filters: { courses } }),
    ];
    const maximumSessionLoad = 100;
    if (sessions.length < maximumSessionLoad) {
      promises.pushObject(this.store.query('session-objective', { filters: { sessions } }));
      promises.pushObject(this.store.query('session-type', { filters: { sessions } }));
      promises.pushObject(this.store.query('term', { filters: { sessions } }));
    } else {
      for (let i = 0; i < sessions.length; i += maximumSessionLoad) {
        const slice = sessions.slice(i, i + maximumSessionLoad);
        promises.pushObject(this.store.query('session-objective', { filters: { sessions: slice } }));
        promises.pushObject(this.store.query('session-type', { filters: { sessions: slice } }));
        promises.pushObject(this.store.query('term', { filters: { sessions: slice } }));
      }
    }

    return all(promises);
  }
}
<|endoftext|>


<commit_before>/**************************************
* Anagram plugin for Pokémon Showdown *
* By: jd                              *
**************************************/

var fs = require('fs');
var request = require('request');
var anagramWords = ['pokemon'];
try {
	anagramWords = fs.readFileSync('config/wordlist.txt','utf8').split('\n');
} catch (e) {
	request('http://pastebin.com/raw.php?i=5yQ8MLG2', function callback(error, response, body) {
	    if (!error && response.statusCode === 200) {
	    	fs.writeFileSync('config/wordlist.txt', body, 'utf8');
	    }
	});
}

exports.commands = {
	anagram: function(target, room, user) {
		if (!user.can('broadcast', null, room) || !this.canTalk()) return this.sendReply('/anagram - Access denied.');
		if (!target && !room.anagram) return this.sendReply("Usage: /anagram [normal/pokemon]");
		if (!this.canBroadcast()) return;
		if (room.anagram) return this.sendReplyBox("Word: " + room.anagram.scrambledWord);
		if (!room.anagram) room.anagram = {};

		target = toId(target);
		var theme = '';

		switch (target) {

			case 'pokemon':
				theme = 'Pokemon';
				var pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
				room.anagram.word = pokemon.name;
				while (pokemon.id.indexOf('mega') > -1 || pokemon.tier === 'CAP') {
					pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
					room.anagram.word = pokemon.name;
				}
				break;

			case 'info':
			case 'credits':
				this.sendReplyBox("Anagram plugin by jd");
				delete room.anagram;
				break;

			case 'end':
				if (!user.can('broadcast', null, room)) return this.sendReply('/endanagram - Access denied.');
				if (!room.anagram) return this.sendReply('There is no anagram running in here.');
				delete room.anagram;
				room.add('|raw|
The anagram game was ended by ' + Tools.escapeHTML(user.name) + '
');
				break;

			default:
			case 'normal':
				theme = 'Normal';
				room.anagram.word = anagramWords.sample().trim();
				while (room.anagram.word.length < 4 || room.anagram.word.length > 8) room.anagram.word = anagramWords.sample().trim();
				break;
		}

		room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));
		while (room.anagram.scrambledWord === toId(room.anagram.word)) room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));

		room.chat = function (user, message, connection) {
			message = CommandParser.parse(message, this, user, connection);
			if (message) {
				this.add('|c|' + user.getIdentity(this.id) + '|' + message, true);
				if (room.anagram && toId(message) === toId(room.anagram.word)) {
					this.add('|raw|
' + Tools.escapeHTML(user.name) + ' got the word! It was ' + room.anagram.word + '
');
					delete room.anagram;
					delete room.chat;
				}
			}
		this.update();
		};
		return this.add('|raw|
' + Tools.escapeHTML(user.name) + ' has started an anagram. Letters: ' + room.anagram.scrambledWord + ' Theme: ' + theme + '
');
	}
};<commit_msg>Use this.add(); not room.add() in anagrams end<commit_after>/**************************************
* Anagram plugin for Pokémon Showdown *
* By: jd                              *
**************************************/

var fs = require('fs');
var request = require('request');
var anagramWords = ['pokemon'];
try {
	anagramWords = fs.readFileSync('config/wordlist.txt','utf8').split('\n');
} catch (e) {
	request('http://pastebin.com/raw.php?i=5yQ8MLG2', function callback(error, response, body) {
	    if (!error && response.statusCode === 200) {
	    	fs.writeFileSync('config/wordlist.txt', body, 'utf8');
	    }
	});
}

exports.commands = {
	anagram: function(target, room, user) {
		if (!user.can('broadcast', null, room) || !this.canTalk()) return this.sendReply('/anagram - Access denied.');
		if (!target && !room.anagram) return this.sendReply("Usage: /anagram [normal/pokemon]");
		if (!this.canBroadcast()) return;
		if (room.anagram) return this.sendReplyBox("Word: " + room.anagram.scrambledWord);
		if (!room.anagram) room.anagram = {};

		target = toId(target);
		var theme = '';

		switch (target) {

			case 'pokemon':
				theme = 'Pokemon';
				var pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
				room.anagram.word = pokemon.name;
				while (pokemon.id.indexOf('mega') > -1 || pokemon.tier === 'CAP') {
					pokemon = Tools.dataSearch(Object.keys(Tools.data.Pokedex).sample().trim())[0];
					room.anagram.word = pokemon.name;
				}
				break;

			case 'info':
			case 'credits':
				this.sendReplyBox("Anagram plugin by jd");
				delete room.anagram;
				break;

			case 'end':
				if (!user.can('broadcast', null, room)) return this.sendReply('/endanagram - Access denied.');
				if (!room.anagram) return this.sendReply('There is no anagram running in here.');
				delete room.anagram;
				this.add('|raw|
The anagram game was ended by ' + Tools.escapeHTML(user.name) + '
');
				break;

			default:
			case 'normal':
				theme = 'Normal';
				room.anagram.word = anagramWords.sample().trim();
				while (room.anagram.word.length < 4 || room.anagram.word.length > 8) room.anagram.word = anagramWords.sample().trim();
				break;
		}

		room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));
		while (room.anagram.scrambledWord === toId(room.anagram.word)) room.anagram.scrambledWord = toId(room.anagram.word.split('').sort(function(){return 0.5-Math.random();}).join(''));

		room.chat = function (user, message, connection) {
			message = CommandParser.parse(message, this, user, connection);
			if (message) {
				this.add('|c|' + user.getIdentity(this.id) + '|' + message, true);
				if (room.anagram && toId(message) === toId(room.anagram.word)) {
					this.add('|raw|
' + Tools.escapeHTML(user.name) + ' got the word! It was ' + room.anagram.word + '
');
					delete room.anagram;
					delete room.chat;
				}
			}
		this.update();
		};
		return this.add('|raw|
' + Tools.escapeHTML(user.name) + ' has started an anagram. Letters: ' + room.anagram.scrambledWord + ' Theme: ' + theme + '
');
	}
};<|endoftext|>


<commit_before>
/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement () {
    $('

').appendTo('body')
    return $('

').appendTo('body')
}

function fillArray(value, len) {
  if (len === 0) return [];
  var a = [value];
  while (a.length * 2 <= len) a = a.concat(a);
  if (a.length < len) a = a.concat(a.slice(0, len - a.length));
  return a;
}

var getInstructFeedback = function() {
  return '

' + feedback_instruct_text + '

'
}
/* ************************************ */
/* Define experimental variables */
/* ************************************ */
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0    //ms
var instructTimeThresh = 3   ///in seconds

var path = 'static/experiments/ravens/images/'
var prefix = '
<img src = "'
var bottom_id = '" id="bottom_img'
var postfix = '"
'
var top_img = ['top_1.jpg','top_2.jpg', 'top_3.jpg', 'top_4.jpg', 'top_5.jpg', 'top_6.jpg', 'top_7.jpg', 'top_8.jpg', 'top_9.jpg', 'top_10.jpg', 'top_11.jpg', 'top_12.jpg', 'top_13.jpg', 'top_14.jpg', 'top_15.jpg', 'top_16.jpg', 'top_17.jpg', 'top_18.jpg']
var bottom_img = ['bottom_1.jpg','bottom_2.jpg', 'bottom_3.jpg', 'bottom_4.jpg', 'bottom_5.jpg', 'bottom_6.jpg', 'bottom_7.jpg', 'bottom_8.jpg', 'bottom_9.jpg', 'bottom_10.jpg', 'bottom_11.jpg', 'bottom_12.jpg', 'bottom_13.jpg', 'bottom_14.jpg', 'bottom_15.jpg', 'bottom_16.jpg', 'bottom_17.jpg', 'bottom_18.jpg']

var all_pages = []

for (var i = 0; i < top_img.length; i++) {
  var page = []
  page.push(prefix+path+top_img[i]+postfix+prefix+path+bottom_img[i]+bottom_id+postfix)
  all_pages.push(page)
}

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */

/* define static blocks */

var welcome_block = {
  type: 'text',
  text: '

Welcome to this survey.

 Press enter to begin.

',
  cont_key: [13],
  data: {exp_id: "ravens"}
};

var feedback_instruct_text = 'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  cont_key: [13],
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  pages: [
    '

This is a test of observation and clear thinking with 18 problems. The top part of each problem is a pattern with one part cut out of it. Your task is to look at the pattern, think of what the missing part must look like to complete the pattern correctly, both along the rows and the columns, and then find the right piece out of the eight shown. Only one of the answer choices is perfectly correct.

The following page will have an example trial.

',
    '

Look at the top part (the pattern) of this sample problem. Notice that going across the rows, the number of horizontal lines is equal. Going down the columns, the number of squares is equal.
<img src = "static/experiments/ravens/images/practice/sample_matrix_top.jpg"

Look at the solution of this sample problem. The best completion of the missing cell is the alternative "E)" which is selected below.

<img src = "static/experiments/ravens/images/practice/sample_matrix_bottom.jpg" id="bottom_img"

<img src = "static/experiments/ravens/images/practice/Opt_E_selected.png"

',
    '
You will now complete two practice trials with feedback. The test trials will not include feedback.

'
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000,
  data: {exp_id : "ravens"}
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
    timeline: instruction_trials,
  /* This function defines stopping criteria */
    loop_function: function(data){
    for(i=0;i<data.length;i++){
      if((data[i].trial_type=='poldrack-instructions') && (data[i].rt!=-1)){
        rt=data[i].rt
        sumInstructTime=sumInstructTime+rt
      }
    }
    if(sumInstructTime<=instructTimeThresh*1000){
      feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if(sumInstructTime>instructTimeThresh*1000){
      feedback_instruct_text = 'Done with instructions. Press enter to continue.'
      return false
    }
    }
}

/////////////////////
// ADD PRACTICE BLOCK
/////////////////////

var getPracticeInstruct = function() {
  return '

' + practice_instruct_text_1+ '

'
}

var practice_instruct_text_1 = 'Starting with first example trial.  Press  Enter  to continue.'
var practice_instruct_block_1 = {
  type: 'poldrack-text',
  cont_key: [13],
  text: getPracticeIntruct,
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var practice_trials_1 = []
var practice_block_1 = {
  type: "poldrack-survey-multi-choice",
  exp_id: "ravens",
  horizontal: true,
  preamble: '',
  pages: ,
  options: ,
  scale: ,
  show_clickable_nav: true,
  allow_backward: true,
  required: ,
};
practice_trials_1.push(practice_instruct_block_1)
practice_trials_1.push(practice_block_1)

var practice_node_1 = {
    timeline: practice_trials_1,
  /* This function defines stopping criteria */
    loop_function: function(data){

    for(i=0;i<data.length;i++){
      if((data[i].trial_type=='poldrack-survey-multi-choice') && (data[i].score_response!=-1)){
        rt=data[i].rt
        sumInstructTime=sumInstructTime+rt
      }
    }
    if(sumInstructTime<=instructTimeThresh*1000){
      feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if(sumInstructTime>instructTimeThresh*1000){
      feedback_instruct_text = 'Done with instructions. Press enter to continue.'
      return false
    }
    }
}

var start_test_block = {
  type: 'poldrack-text',
  cont_key: [13],
  text: '

You are now ready to begin the test trials.

',
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};

var opts = ["A", "B", "C", "D", "E", "F", "G", "H"]

var all_options = fillArray([opts], 18)

var score_scale = {"A":1, "B":2, "C":3, "D":4, "E":5, "F":6, "G":7, "H":8}

/////////////////////
// ADD correct/incorrect
/////////////////////

var survey_block = {
  type: "poldrack-survey-multi-choice",
  exp_id: "ravens",
  horizontal: true,
  preamble: '',
  pages: all_pages,
  options: all_options,
  scale: score_scale,
  show_clickable_nav: true,
  allow_backward: true,
  required: fillArray([true], 18),
};

var end_block = {
  type: 'text',
  text: '

Congratulations for completing this task!

Press enter to continue.

',
  cont_key: [13],
  data: {exp_id: "ravens"}
};

//Set up experiment
var ravens_experiment = []
ravens_experiment.push(welcome_block);
ravens_experiment.push(instruction_node);
ravens_experiment.push(practice_block);
ravens_experiment.push(start_test_block);
ravens_experiment.push(survey_block);
ravens_experiment.push(end_block);
<commit_msg>Fixed ravens to work with new scoring coding.<commit_after>
/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement () {
    $('

').appendTo('body')
    return $('

').appendTo('body')
}

function fillArray(value, len) {
  if (len === 0) return [];
  var a = [value];
  while (a.length * 2 <= len) a = a.concat(a);
  if (a.length < len) a = a.concat(a.slice(0, len - a.length));
  return a;
}

var getInstructFeedback = function() {
  return '

' + feedback_instruct_text + '

'
}
/* ************************************ */
/* Define experimental variables */
/* ************************************ */
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0    //ms
var instructTimeThresh = 3   ///in seconds

var path = 'static/experiments/ravens/images/'
var prefix = '
<img src = "'
var bottom_id = '" id="bottom_img'
var postfix = '"
'
var top_img = ['top_1.jpg','top_2.jpg', 'top_3.jpg', 'top_4.jpg', 'top_5.jpg', 'top_6.jpg', 'top_7.jpg', 'top_8.jpg', 'top_9.jpg', 'top_10.jpg', 'top_11.jpg', 'top_12.jpg', 'top_13.jpg', 'top_14.jpg', 'top_15.jpg', 'top_16.jpg', 'top_17.jpg', 'top_18.jpg']
var bottom_img = ['bottom_1.jpg','bottom_2.jpg', 'bottom_3.jpg', 'bottom_4.jpg', 'bottom_5.jpg', 'bottom_6.jpg', 'bottom_7.jpg', 'bottom_8.jpg', 'bottom_9.jpg', 'bottom_10.jpg', 'bottom_11.jpg', 'bottom_12.jpg', 'bottom_13.jpg', 'bottom_14.jpg', 'bottom_15.jpg', 'bottom_16.jpg', 'bottom_17.jpg', 'bottom_18.jpg']

var all_pages = []

for (var i = 0; i < top_img.length; i++) {
  var page = []
  page.push(prefix+path+top_img[i]+postfix+prefix+path+bottom_img[i]+bottom_id+postfix)
  all_pages.push(page)
}

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */

/* define static blocks */

var welcome_block = {
  type: 'text',
  text: '

Welcome to this survey.

 Press enter to begin.

',
  cont_key: [13],
  data: {exp_id: "ravens"}
};

var feedback_instruct_text = 'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  cont_key: [13],
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  pages: [
    '

This is a test of observation and clear thinking with 18 problems. The top part of each problem is a pattern with one part cut out of it. Your task is to look at the pattern, think of what the missing part must look like to complete the pattern correctly, both along the rows and the columns, and then find the right piece out of the eight shown. Only one of the answer choices is perfectly correct.

The following page will have an example trial.

',
    '

Look at the top part (the pattern) of this sample problem. Notice that going across the rows, the number of horizontal lines is equal. Going down the columns, the number of squares is equal.
<img src = "static/experiments/ravens/images/practice/sample_matrix_top.jpg"

Look at the solution of this sample problem. The best completion of the missing cell is the alternative "E)" which is selected below.

<img src = "static/experiments/ravens/images/practice/sample_matrix_bottom.jpg" id="bottom_img"

<img src = "static/experiments/ravens/images/practice/Opt_E_selected.png"

',
    '
You will now complete two practice trials with feedback. The test trials will not include feedback.

'
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000,
  data: {exp_id : "ravens"}
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
    timeline: instruction_trials,
  /* This function defines stopping criteria */
    loop_function: function(data){
    for(i=0;i<data.length;i++){
      if((data[i].trial_type=='poldrack-instructions') && (data[i].rt!=-1)){
        rt=data[i].rt
        sumInstructTime=sumInstructTime+rt
      }
    }
    if(sumInstructTime<=instructTimeThresh*1000){
      feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if(sumInstructTime>instructTimeThresh*1000){
      feedback_instruct_text = 'Done with instructions. Press enter to continue.'
      return false
    }
    }
}

/////////////////////
// ADD PRACTICE BLOCK
/////////////////////

var getPracticeInstruct = function() {
  return '

' + practice_instruct_text_1+ '

'
}

var practice_instruct_text_1 = 'Starting with first example trial.  Press  Enter  to continue.'
var practice_instruct_block_1 = {
  type: 'poldrack-text',
  cont_key: [13],
  text: getPracticeIntruct,
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};

var practice_trials_1 = []
var practice_block_1 = {
  type: "poldrack-survey-multi-choice",
  exp_id: "ravens",
  horizontal: true,
  preamble: '',
  pages: ,
  options: ,
  scale: ,
  show_clickable_nav: true,
  allow_backward: true,
  required: ,
};
practice_trials_1.push(practice_instruct_block_1)
practice_trials_1.push(practice_block_1)

var practice_node_1 = {
    timeline: practice_trials_1,
  /* This function defines stopping criteria */
    loop_function: function(data){

    for(i=0;i<data.length;i++){
      if((data[i].trial_type=='poldrack-survey-multi-choice') && (data[i].score_response!=-1)){
        rt=data[i].rt
        sumInstructTime=sumInstructTime+rt
      }
    }
    if(sumInstructTime<=instructTimeThresh*1000){
      feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
      return true
    } else if(sumInstructTime>instructTimeThresh*1000){
      feedback_instruct_text = 'Done with instructions. Press enter to continue.'
      return false
    }
    }
}

var start_test_block = {
  type: 'poldrack-text',
  cont_key: [13],
  text: '

You are now ready to begin the test trials.

',
  timing_post_trial: 0,
  timing_response: 60000,
  data: {exp_id: "ravens"}
};

var opts = ["A", "B", "C", "D", "E", "F", "G", "H"]

var all_options = fillArray([opts], 18)

var scale_q1 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q2 = {"A":0, "B":0, "C":0, "D":0, "E":1, "F":0, "G":0, "H":0}
var scale_q3 = {"A":0, "B":0, "C":0, "D":0, "E":0, "F":0, "G":1, "H":0}
var scale_q4 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q5 = {"A":0, "B":0, "C":1, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q6 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q7 = {"A":0, "B":0, "C":0, "D":0, "E":1, "F":0, "G":0, "H":0}
var scale_q8 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q9 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q10 = {"A":0, "B":0, "C":0, "D":0, "E":1, "F":0, "G":0, "H":0}
var scale_q11 = {"A":1, "B":0, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q12 = {"A":0, "B":0, "C":0, "D":0, "E":1, "F":0, "G":0, "H":0}
var scale_q13 = {"A":1, "B":0, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q14 = {"A":0, "B":0, "C":1, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q15 = {"A":0, "B":1, "C":0, "D":0, "E":0, "F":0, "G":0, "H":0}
var scale_q16 = {"A":0, "B":0, "C":0, "D":0, "E":1, "F":0, "G":0, "H":0}
var scale_q17 = {"A":0, "B":0, "C":0, "D":0, "E":0, "F":1, "G":0, "H":0}
var scale_q18 = {"A":0, "B":0, "C":0, "D":1, "E":0, "F":0, "G":0, "H":0}

var score_scale = [[scale_q1],[scale_q2],[scale_q3],[scale_q4],[scale_q5],[scale_q6],[scale_q7],[scale_q8],[scale_q9],[scale_q10],[scale_q11],[scale_q12],[scale_q13],[scale_q14],[scale_q15],[scale_q16],[scale_q17],[scale_q18]]

/////////////////////
// ADD correct/incorrect
/////////////////////

var survey_block = {
  type: "poldrack-survey-multi-choice",
  exp_id: "ravens",
  horizontal: true,
  preamble: '',
  pages: all_pages,
  options: all_options,
  scale: score_scale,
  show_clickable_nav: true,
  allow_backward: true,
  required: fillArray([true], 18),
};

var end_block = {
  type: 'text',
  text: '

Congratulations for completing this task!

Press enter to continue.

',
  cont_key: [13],
  data: {exp_id: "ravens"}
};

//Set up experiment
var ravens_experiment = []
ravens_experiment.push(welcome_block);
ravens_experiment.push(instruction_node);
ravens_experiment.push(practice_block);
ravens_experiment.push(start_test_block);
ravens_experiment.push(survey_block);
ravens_experiment.push(end_block);
<|endoftext|>


<commit_before>var fs = require('fs')
var path = require('path')
var spawn = require('child_process').spawn
var utils = require('../utils')

/* Needed paths for the plugin */
var paths = {}
var es6 = false
var blacklist = ['Resources', 'build', 'README', 'LICENSE']

exports.cliVersion = ">=3.x"
exports.version = "1.0"
exports.init = function (logger, config, cli) {
    /* Handle brutal stops */
    process.on('SIGINT', function () {
        paths.toProject && utils.cleanSync(paths.toProject)
        process.exit(2)
    })
    process.on('exit', function () {
        paths.toProject && utils.cleanSync(paths.toProject)
    })

    cli.on('build.pre.construct', executeSeq(logger, [
        prepare
    ]))

    cli.on('build.android.startEmulator', executeSeq(logger, [
        copyResources
    ]))

    cli.on('build.ios.copyResource', executeSeq(logger, [
        copyResources
    ]))

    cli.on('build.pre.compile', executeSeq(logger, [
        copyProject,
        transcompile,
        requirePolyfill,
        copyPolyfill
    ]))

    cli.on('build.post.compile', executeSeq(logger, [
        postCompile
    ]))
}

function executeSeq(logger, tasks) {
    var current = 0
    var errored = false

    return function task(data, terminate) {
        tasks[current](logger, data, function next(err, type) {
            if (err) {
                if (errored) { return }
                errored = true
                logger[type || 'error'](err)
                if (type && type !== 'error') {
                    return terminate()
                }
                return terminate(type && type !== 'error' ? undefined : "Unable to use ES6")
            }
            if (++current >= tasks.length) { return terminate() }
            task(data, terminate)
        })
    }
}

function copyResources (logger, data, next) {
    logger.info("Copying back resources into project folder")
    utils.cp(paths.toResources, paths.fromResources, next)
}

function prepare(logger, data, next) {
    var propES6 = data.cli.tiapp.properties.es6 && data.cli.tiapp.properties.es6.value
    var optiES6 = data.cli.argv.$_.indexOf('--es6') !== -1
    es6 = propES6 || optiES6;
    logger.info(es6 ? "Setup project for ES6 transpiling" : "Es6 not desired on this project")
    if (!es6) { return next() }
    paths.fromProject = data.cli.argv['project-dir']
    paths.toProject = path.join(paths.fromProject, '.project')
    paths.fromSources = path.join(paths.fromProject, 'app')
    paths.toSources = path.join(paths.toProject, 'app')
    paths.fromResources = path.join(paths.fromProject, 'Resources')
    paths.toResources = path.join(paths.toProject, 'Resources')
    data.cli.argv.$_.push('--project-dir', paths.toProject)
    data.cli.argv['project-dir'] = paths.toProject
    utils.clean(paths.toProject, next)
}

function copyProject (logger, data, next) {
    /* Ensure the user wants to build for es6 */
    if (!es6) { return next('ES6 not desired for this build.', 'info') }

    logger.info("Preparing project for ES6.")
    fs.mkdir(paths.toProject, function (e) {
        if (e) { return next(e) }
        fs.readdir(paths.fromProject, function (e, files) {
            var n = files.length
            if (n === 0) { return next() }
            var after = function (e) {
                if (e) { return next(e) }
                if (--n === 0) { return next() }
            }
            files.forEach(function (f) {
                if (blacklist.indexOf(f) !== -1 || f.match(/^\..*/)) { return after() }
                utils.cp(path.join(paths.fromProject, f), path.join(paths.toProject, f), after)
            })
        })
    })
}

function transcompile (logger, data, next) {
    /* Execute Babel on the copied sources */
    logger.info("Transpiling with Babel")
    var babelBin = path.join(__dirname, '..', '..', '..', '..', 'node_modules', 'babel', 'bin', 'babel')
    var babel = spawn('node', [babelBin, paths.toSources, '--out-dir', paths.toSources])
    babel.on('exit', next)
    babel.stdout.on('data' , function (d) { logger.info(d)  })
    babel.stderr.on('data' , function (d) { logger.error(d)  })
    babel.stdout.on('error', function (e) { logger.error(e) })
}

function requirePolyfill (logger, data, next) {
    logger.info("Adding Polyfill features")
    /* Now, we've to read alloy.js and manually add a require to polyfill */
    var alloy = path.join(paths.toSources, 'alloy.js')
    fs.readFile(alloy, function (e, content) {
        if(e) { return next(e) }
        fs.writeFile(alloy, 'require("babel/polyfill");\n' + content.toString(), next)
    })
}

function copyPolyfill (logger, data, next) {
    /* Finally, create if it does not exist a lib/babel folder, and copy polyfill.js in it */
    logger.info("Copying polyfill library")
    var from = path.join(path.dirname(__dirname), 'lib', 'polyfill.js')
    var to = paths.toSources
    fs.mkdir(to, function (e) {
        if (e && e.code !== 'EEXIST') { return next(e) }
        to = path.join(to, 'lib')
        fs.mkdir(to, function (e) {
            if (e && e.code !== 'EEXIST') { return next(e) }
            to = path.join(to, 'babel')
            fs.mkdir(to, function (e) {
                if (e && e.code !== 'EEXIST') { return next(e) }
                to = path.join(to, 'polyfill.js')
                utils.cp(from, to, next)
            })
        })
    })
}

function postCompile (logger, data, next) {
    logger.info("Cleaning ES6 artifacts")
    utils.clean(paths.toProject, next)
}
<commit_msg>Patch missing line when es6 not desired<commit_after>var fs = require('fs')
var path = require('path')
var spawn = require('child_process').spawn
var utils = require('../utils')

/* Needed paths for the plugin */
var paths = {}
var es6 = false
var blacklist = ['Resources', 'build', 'README', 'LICENSE']

exports.cliVersion = ">=3.x"
exports.version = "1.0"
exports.init = function (logger, config, cli) {
    /* Handle brutal stops */
    process.on('SIGINT', function () {
        paths.toProject && utils.cleanSync(paths.toProject)
        process.exit(2)
    })
    process.on('exit', function () {
        paths.toProject && utils.cleanSync(paths.toProject)
    })

    cli.on('build.pre.construct', executeSeq(logger, [
        prepare
    ]))

    cli.on('build.android.startEmulator', executeSeq(logger, [
        copyResources
    ]))

    cli.on('build.ios.copyResource', executeSeq(logger, [
        copyResources
    ]))

    cli.on('build.pre.compile', executeSeq(logger, [
        copyProject,
        transcompile,
        requirePolyfill,
        copyPolyfill
    ]))

    cli.on('build.post.compile', executeSeq(logger, [
        postCompile
    ]))
}

function executeSeq(logger, tasks) {
    var current = 0
    var errored = false

    return function task(data, terminate) {
        tasks[current](logger, data, function next(err, type) {
            if (err) {
                if (errored) { return }
                errored = true
                logger[type || 'error'](err)
                if (type && type !== 'error') {
                    return terminate()
                }
                return terminate(type && type !== 'error' ? undefined : "Unable to use ES6")
            }
            if (++current >= tasks.length) { return terminate() }
            task(data, terminate)
        })
    }
}

function copyResources (logger, data, next) {
    if (!es6) { return next() }
    logger.info("Copying back resources into project folder")
    utils.cp(paths.toResources, paths.fromResources, next)
}

function prepare(logger, data, next) {
    var propES6 = data.cli.tiapp.properties.es6 && data.cli.tiapp.properties.es6.value
    var optiES6 = data.cli.argv.$_.indexOf('--es6') !== -1
    es6 = propES6 || optiES6;
    logger.info(es6 ? "Setup project for ES6 transpiling" : "Es6 not desired on this project")
    if (!es6) { return next() }
    paths.fromProject = data.cli.argv['project-dir']
    paths.toProject = path.join(paths.fromProject, '.project')
    paths.fromSources = path.join(paths.fromProject, 'app')
    paths.toSources = path.join(paths.toProject, 'app')
    paths.fromResources = path.join(paths.fromProject, 'Resources')
    paths.toResources = path.join(paths.toProject, 'Resources')
    data.cli.argv.$_.push('--project-dir', paths.toProject)
    data.cli.argv['project-dir'] = paths.toProject
    utils.clean(paths.toProject, next)
}

function copyProject (logger, data, next) {
    /* Ensure the user wants to build for es6 */
    if (!es6) { return next('ES6 not desired for this build.', 'info') }

    logger.info("Preparing project for ES6.")
    fs.mkdir(paths.toProject, function (e) {
        if (e) { return next(e) }
        fs.readdir(paths.fromProject, function (e, files) {
            var n = files.length
            if (n === 0) { return next() }
            var after = function (e) {
                if (e) { return next(e) }
                if (--n === 0) { return next() }
            }
            files.forEach(function (f) {
                if (blacklist.indexOf(f) !== -1 || f.match(/^\..*/)) { return after() }
                utils.cp(path.join(paths.fromProject, f), path.join(paths.toProject, f), after)
            })
        })
    })
}

function transcompile (logger, data, next) {
    /* Execute Babel on the copied sources */
    logger.info("Transpiling with Babel")
    var babelBin = path.join(__dirname, '..', '..', '..', '..', 'node_modules', 'babel', 'bin', 'babel')
    var babel = spawn('node', [babelBin, paths.toSources, '--out-dir', paths.toSources])
    babel.on('exit', next)
    babel.stdout.on('data' , function (d) { logger.info(d)  })
    babel.stderr.on('data' , function (d) { logger.error(d)  })
    babel.stdout.on('error', function (e) { logger.error(e) })
}

function requirePolyfill (logger, data, next) {
    logger.info("Adding Polyfill features")
    /* Now, we've to read alloy.js and manually add a require to polyfill */
    var alloy = path.join(paths.toSources, 'alloy.js')
    fs.readFile(alloy, function (e, content) {
        if(e) { return next(e) }
        fs.writeFile(alloy, 'require("babel/polyfill");\n' + content.toString(), next)
    })
}

function copyPolyfill (logger, data, next) {
    /* Finally, create if it does not exist a lib/babel folder, and copy polyfill.js in it */
    logger.info("Copying polyfill library")
    var from = path.join(path.dirname(__dirname), 'lib', 'polyfill.js')
    var to = paths.toSources
    fs.mkdir(to, function (e) {
        if (e && e.code !== 'EEXIST') { return next(e) }
        to = path.join(to, 'lib')
        fs.mkdir(to, function (e) {
            if (e && e.code !== 'EEXIST') { return next(e) }
            to = path.join(to, 'babel')
            fs.mkdir(to, function (e) {
                if (e && e.code !== 'EEXIST') { return next(e) }
                to = path.join(to, 'polyfill.js')
                utils.cp(from, to, next)
            })
        })
    })
}

function postCompile (logger, data, next) {
    logger.info("Cleaning ES6 artifacts")
    utils.clean(paths.toProject, next)
}
<|endoftext|>