<commit_before>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint devel: true*/
/* global module, test, asyncTest, ok, _playground, expect, start, equal */

define(function () {
    "use strict";

    module("low-level");

    /**
     * Validates that the returned result-value is a success code
     *
     * @private
     * @param {Error=} err
     */
    var _validateNotifierResult = function (err) {
        // Acceptable success codes are: undefined, or an object with a
        // number property that has the value 0
        var testPasses = (err === undefined);
        if (!testPasses) {
            testPasses = (err.number !== undefined && err.number === 0);
        }

        ok(testPasses, "result is success");

        if (!testPasses) {
            console.log("validateNotifierResult failed for err:");
            console.log(err);
        }
    };

    /**
     * Validates that the returned result-value is an error object containing
     * number and message properties. Other properties are possible too.
     *
     * @private
     * @param {Error=} err
     * @param {number=} expectedError
     */
    var _validateNotifierResultError = function (err, expectedError) {

        var testPasses = false;
        do {
            if (err === undefined) {
                break;
            }
            if (!(err instanceof Error)) {
                break;
            }
            if (err.number === undefined) {
                break;
            }
            if (err.message === undefined) {
                break;
            }

            testPasses = true;
        } while (false);

        if (!testPasses) {
            console.log("Invalid form of an error resultValue:");
            console.log(err);
        } else {
            // test the returned error code
            // REVISIT: Replace the hard coded number with an enumeration
            // when that becomes available
            testPasses = (err.number === expectedError);

            if (!testPasses) {
                console.log("resultValue did not have the expected error code:" +
                    expectedError + " Actual error is: " + err.number);
            }
        }
        ok(testPasses, "error resultValue validation");
    };

    test("_playground object exists", function () {
        expect(1);
        ok(!!_playground, "_playground object exists");
    });

    test("_playground.ps property exists", function () {
        expect(1);
        ok(!!_playground.ps, "_playground.ps property exists");
    });

    test("_playground.ps.descriptor property exists", function () {
        expect(1);
        ok(!!_playground.ps.descriptor, "_playground.ps.descriptor property exists");
    });

    test("_playground.ps.ui property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui, "_playground.ps.ui property exists");
    });

    test("_playground._debug property exists", function () {
        expect(1);
        ok(!!_playground._debug, "_playground.debug property exists");
    });

    // Basic test for obtaining a value with no arguments
    asyncTest("_playground.ps.ui.getScaleFactor property", function () {
        expect(2);

        _playground.ps.ui.getScaleFactor(function (err, scaleFactor) {
            _validateNotifierResult(err);

            var scaleFactorValidation = (scaleFactor === 1 || scaleFactor === 2);
            ok(scaleFactorValidation, "scale factor validation");

            if (!scaleFactorValidation) {
                console.log("scale factor not the expected value. returned value is: " + scaleFactor);
            }

            start();
        });
    });

    // Negative test: Invoke a host method without providing a notifier
    test("_playground.ps.ui.getScaleFactor property (negative)", function () {
        expect(1);

        var result;
        try {
            _playground.ps.ui.getScaleFactor("some value");
        } catch (err) {
            result = err;
        }

        ok(result !== undefined, "missing notifier exception");
    });

    // Negative test. Invoke a host method with an incorrect number/typs of arguments
    asyncTest("_playground._debug.forcePlayArgumentFailure", function () {
        expect(1);

        _playground._debug.forcePlayArgumentFailure(function (err) {
            _validateNotifierResultError(err, _playground.errorCodes.ARGUMENT_ERROR);

            start();
        });
    });

    // _playground.ps.ui.setPointerPropagationMode
    test("_playground.ps.ui.setPointerPropagationMode property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui.setPointerPropagationMode,
            "_playground.ps.ui.setPointerPropagationMode property exists");
    });

    // _playground.ps.ui.getPointerPropagationMode
    test("_playground.ps.ui.getPointerPropagationMode property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui.getPointerPropagationMode,
            "_playground.ps.ui.getPointerPropagationMode property exists");
    });

    asyncTest("_playground.ps.ui.getPointerPropagationMode property", function () {
        expect(2);

        _playground.ps.ui.getPointerPropagationMode(function (err, mode) {
            _validateNotifierResult(err);

            var modeValidation = (mode === _playground.ps.ui.pointerPropagationMode.ALPHA_PROPAGATE ||
                mode === _playground.ps.ui.pointerPropagationMode.NEVER_PROPAGATE ||
                mode === _playground.ps.ui.pointerPropagationMode.ALWAYS_PROPAGATE);

            ok(modeValidation, "mode factor validation");
            if (!modeValidation) {
                console.log("mode not the expected value. returned value is: " + mode);
            }

            start();
        });
    });

    asyncTest("_playground.ps.ui.setPointerPropagationMode property", function () {
        expect(1);

        var options = {
            defaultMode: _playground.ps.ui.pointerPropagationMode.ALPHA_PROPAGATE
        };

        _playground.ps.ui.setPointerPropagationMode(options, function (err) {
            _validateNotifierResult(err);

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.get", function () {
        expect(5);

        var reference = {
            "ref": "$capp",
            "enum": "$Ordn",
            "value": "$Trgt"
        };

        _playground.ps.descriptor.get(reference, function (err, descriptor) {
            _validateNotifierResult(err);
            ok(!err, "Call succeeded");

            equal(typeof descriptor, "object", "Result is a descriptor");
            ok(descriptor.hasOwnProperty("hostName"), "Descriptor has the hostName property");
            equal(descriptor.hostName.indexOf("Adobe Photoshop"), 0, "hostName property starts with 'Adobe Photoshop'");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.play", function () {
        expect(4);

        _playground.ps.descriptor.play("jsonAction", {}, {}, function (err, descriptor) {
            _validateNotifierResult(err);
            ok(!err, "Call succeeded");

            equal(typeof descriptor, "object", "Result is a descriptor");
            equal(Object.keys(descriptor), 0, "Result object is empty");

            start();
        });
    });

});
<commit_msg>Argument and semantic failure tests for get and play<commit_after>/*
 * Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

/*jslint devel: true*/
/* global module, test, asyncTest, ok, _playground, expect, start, equal */

define(function () {
    "use strict";

    module("low-level");

    /**
     * Validates that the returned result-value is a success code
     *
     * @private
     * @param {Error=} err
     */
    var _validateNotifierResult = function (err) {
        // Acceptable success codes are: undefined, or an object with a
        // number property that has the value 0
        var testPasses = (err === undefined);
        if (!testPasses) {
            testPasses = (err.number !== undefined && err.number === 0);
        }

        ok(testPasses, "result is success");

        if (!testPasses) {
            console.log("validateNotifierResult failed for err:");
            console.log(err);
        }
    };

    /**
     * Validates that the returned result-value is an error object containing
     * number and message properties. Other properties are possible too.
     *
     * @private
     * @param {Error=} err
     * @param {number=} expectedError
     */
    var _validateNotifierResultError = function (err, expectedError) {

        var testPasses = false;
        do {
            if (err === undefined) {
                break;
            }
            if (!(err instanceof Error)) {
                break;
            }
            if (err.number === undefined) {
                break;
            }
            if (err.message === undefined) {
                break;
            }

            testPasses = true;
        } while (false);

        if (!testPasses) {
            console.log("Invalid form of an error resultValue:");
            console.log(err);
        } else {
            // test the returned error code
            // REVISIT: Replace the hard coded number with an enumeration
            // when that becomes available
            testPasses = (err.number === expectedError);

            if (!testPasses) {
                console.log("resultValue did not have the expected error code:" +
                    expectedError + " Actual error is: " + err.number);
            }
        }
        ok(testPasses, "error resultValue validation");
    };

    test("_playground object exists", function () {
        expect(1);
        ok(!!_playground, "_playground object exists");
    });

    test("_playground.ps property exists", function () {
        expect(1);
        ok(!!_playground.ps, "_playground.ps property exists");
    });

    test("_playground.ps.descriptor property exists", function () {
        expect(1);
        ok(!!_playground.ps.descriptor, "_playground.ps.descriptor property exists");
    });

    test("_playground.ps.ui property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui, "_playground.ps.ui property exists");
    });

    test("_playground._debug property exists", function () {
        expect(1);
        ok(!!_playground._debug, "_playground.debug property exists");
    });

    // Basic test for obtaining a value with no arguments
    asyncTest("_playground.ps.ui.getScaleFactor property", function () {
        expect(2);

        _playground.ps.ui.getScaleFactor(function (err, scaleFactor) {
            _validateNotifierResult(err);

            var scaleFactorValidation = (scaleFactor === 1 || scaleFactor === 2);
            ok(scaleFactorValidation, "scale factor validation");

            if (!scaleFactorValidation) {
                console.log("scale factor not the expected value. returned value is: " + scaleFactor);
            }

            start();
        });
    });

    // Negative test: Invoke a host method without providing a notifier
    test("_playground.ps.ui.getScaleFactor property (negative)", function () {
        expect(1);

        var result;
        try {
            _playground.ps.ui.getScaleFactor("some value");
        } catch (err) {
            result = err;
        }

        ok(result !== undefined, "missing notifier exception");
    });

    // Negative test. Invoke a host method with an incorrect number/typs of arguments
    asyncTest("_playground._debug.forcePlayArgumentFailure", function () {
        expect(1);

        _playground._debug.forcePlayArgumentFailure(function (err) {
            _validateNotifierResultError(err, _playground.errorCodes.ARGUMENT_ERROR);

            start();
        });
    });

    // _playground.ps.ui.setPointerPropagationMode
    test("_playground.ps.ui.setPointerPropagationMode property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui.setPointerPropagationMode,
            "_playground.ps.ui.setPointerPropagationMode property exists");
    });

    // _playground.ps.ui.getPointerPropagationMode
    test("_playground.ps.ui.getPointerPropagationMode property exists", function () {
        expect(1);
        ok(!!_playground.ps.ui.getPointerPropagationMode,
            "_playground.ps.ui.getPointerPropagationMode property exists");
    });

    asyncTest("_playground.ps.ui.getPointerPropagationMode property", function () {
        expect(2);

        _playground.ps.ui.getPointerPropagationMode(function (err, mode) {
            _validateNotifierResult(err);

            var modeValidation = (mode === _playground.ps.ui.pointerPropagationMode.ALPHA_PROPAGATE ||
                mode === _playground.ps.ui.pointerPropagationMode.NEVER_PROPAGATE ||
                mode === _playground.ps.ui.pointerPropagationMode.ALWAYS_PROPAGATE);

            ok(modeValidation, "mode factor validation");
            if (!modeValidation) {
                console.log("mode not the expected value. returned value is: " + mode);
            }

            start();
        });
    });

    asyncTest("_playground.ps.ui.setPointerPropagationMode property", function () {
        expect(1);

        var options = {
            defaultMode: _playground.ps.ui.pointerPropagationMode.ALPHA_PROPAGATE
        };

        _playground.ps.ui.setPointerPropagationMode(options, function (err) {
            _validateNotifierResult(err);

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.get: success", function () {
        expect(5);

        var reference = {
            "ref": "$capp",
            "enum": "$Ordn",
            "value": "$Trgt"
        };

        _playground.ps.descriptor.get(reference, function (err, descriptor) {
            _validateNotifierResult(err);
            ok(!err, "Call succeeded");

            equal(typeof descriptor, "object", "Result is a descriptor");
            ok(descriptor.hasOwnProperty("hostName"), "Descriptor has the hostName property");
            equal(descriptor.hostName.indexOf("Adobe Photoshop"), 0, "hostName property starts with 'Adobe Photoshop'");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.get: semantic failure", function () {
        expect(2);

        var reference = {
            "ref": "xxx-ref-does-not-exist-xxx",
            "enum": "$Ordn",
            "value": "$Trgt"
        };

        _playground.ps.descriptor.get(reference, function (err, descriptor) {
            _validateNotifierResultError(err, _playground.errorCodes.UNKNOWN_ERROR);
            ok(!descriptor, "Call failed");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.get: argument failure", function () {
        expect(2);

        _playground.ps.descriptor.get("xxx-ref-does-not-exist-xxx", function (err, descriptor) {
            _validateNotifierResultError(err, _playground.errorCodes.ARGUMENT_ERROR);
            ok(!descriptor, "Call failed");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.play: success", function () {
        expect(4);

        _playground.ps.descriptor.play("jsonAction", {}, {}, function (err, descriptor) {
            _validateNotifierResult(err);
            ok(!err, "Call succeeded");

            equal(typeof descriptor, "object", "Result is a descriptor");
            equal(Object.keys(descriptor), 0, "Result object is empty");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.play: argument failure", function () {
        expect(2);

        _playground.ps.descriptor.play("jsonAction", undefined, undefined, function (err, descriptor) {
            _validateNotifierResultError(err, _playground.errorCodes.ARGUMENT_ERROR);
            ok(!descriptor, "Call failed");

            start();
        });
    });

    asyncTest("_playground.ps.descriptor.play: semantic failure", function () {
        expect(2);

        _playground.ps.descriptor.play("xxx-ref-does-not-exist-xxx", {}, {}, function (err, descriptor) {
            _validateNotifierResultError(err, _playground.errorCodes.UNKNOWN_ERROR);
            ok(!descriptor, "Call failed");

            start();
        });
    });

});
<|endoftext|>


<commit_before>'use strict';

/**
 * Written by Alex, based on Carl's homepage test, to test the 'Help' page
 * rendered by help.js.
 *
 * Includes check for 1+ help section, for current 4 section titles,
 * and for functionality of .slide-display slider.
 */

jest.autoMockOff();

// Fixes https://github.com/facebook/jest/issues/78
jest.dontMock('react');
jest.dontMock('underscore');

describe('Testing statics.js (to increase coverage)', function() {
    var React, Statics, testItem, TestUtils, page, data, _, banners, Wrapper, helpEntries;

    beforeAll(function() {
        React = require('react');
        Statics = require('../../data/statics');
    });

    it('Has the required keys & values', function() {

        expect('homeDescription' in Statics).toBe(true);
        expect(Statics.homeDescription.length).toBeGreaterThan(0);
        expect('homeLinks' in Statics).toBe(true);
        expect(Statics.homeLinks.length).toBeGreaterThan(0);
        expect('gettingStarted' in Statics).toBe(true);
        expect(Statics.gettingStarted.length).toBeGreaterThan(0);
        expect('metadataStructure1' in Statics).toBe(true);
        expect(Statics.metadataStructure1.length).toBeGreaterThan(0);
        expect('metadataStructure2' in Statics).toBe(true);
        expect(Statics.metadataStructure2.length).toBeGreaterThan(0);
        expect('submissionXLS' in Statics).toBe(true);
        expect(Statics.submissionXLS.length).toBeGreaterThan(0);
        expect('aboutDCIC' in Statics).toBe(true);
        expect(Statics.aboutDCIC.length).toBeGreaterThan(0);
        expect('aboutAcknowledgement' in Statics).toBe(true);
        expect(Statics.aboutAcknowledgement.length).toBeGreaterThan(0);
        expect('aboutFunding' in Statics).toBe(true);
        expect(Statics.aboutFunding.length).toBeGreaterThan(0);

    });

});
<commit_msg>Making statics-test.js comment accurate<commit_after>'use strict';

/**
 * Written by Alex, based on Carl's homepage test, to test the content
 * in statics.js to ensure variables required exist and have text/html
 * content.
 */

jest.autoMockOff();

// Fixes https://github.com/facebook/jest/issues/78
jest.dontMock('react');
jest.dontMock('underscore');

describe('Testing statics.js (to increase coverage)', function() {
    var React, Statics, testItem, TestUtils, page, data, _, banners, Wrapper, helpEntries;

    beforeAll(function() {
        React = require('react');
        Statics = require('../../data/statics');
    });

    it('Has the required keys & values', function() {

        expect('homeDescription' in Statics).toBe(true);
        expect(Statics.homeDescription.length).toBeGreaterThan(0);
        expect('homeLinks' in Statics).toBe(true);
        expect(Statics.homeLinks.length).toBeGreaterThan(0);
        expect('gettingStarted' in Statics).toBe(true);
        expect(Statics.gettingStarted.length).toBeGreaterThan(0);
        expect('metadataStructure1' in Statics).toBe(true);
        expect(Statics.metadataStructure1.length).toBeGreaterThan(0);
        expect('metadataStructure2' in Statics).toBe(true);
        expect(Statics.metadataStructure2.length).toBeGreaterThan(0);
        expect('submissionXLS' in Statics).toBe(true);
        expect(Statics.submissionXLS.length).toBeGreaterThan(0);
        expect('aboutDCIC' in Statics).toBe(true);
        expect(Statics.aboutDCIC.length).toBeGreaterThan(0);
        expect('aboutAcknowledgement' in Statics).toBe(true);
        expect(Statics.aboutAcknowledgement.length).toBeGreaterThan(0);
        expect('aboutFunding' in Statics).toBe(true);
        expect(Statics.aboutFunding.length).toBeGreaterThan(0);

    });

});
<|endoftext|>


<commit_before>/*
 * timeago: a jQuery plugin, version: 0.7.2 (2009-07-30)
 * @requires jQuery v1.2.3 or later
 *
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2008-2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
 */
(function($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) return inWords(timestamp);
    else if (typeof timestamp == "string") return inWords($.timeago.parse(timestamp));
    else return inWords($.timeago.datetime(timestamp));
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowFuture: false,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        ago: null, // DEPRECATED, use suffixAgo
        fromNow: null, // DEPRECATED, use suffixFromNow
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years"
      }
    },
    inWords: function(distanceMillis) {
      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo || $l.ago;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow || $l.fromNow;
        }
        distanceMillis = Math.abs(distanceMillis);
      }

      var seconds = distanceMillis / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 48 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.floor(days)) ||
        days < 60 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
        years < 2 && substitute($l.year, 1) ||
        substitute($l.years, Math.floor(years));

      return $.trim([prefix, words, suffix].join(" "));
    },
    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      return new Date(s);
    },
    datetime: function(elem) {
      var iso8601 = $(elem).is('time') ? $(elem).attr('datetime') : $(elem).attr('title');
      return $t.parse(iso8601);
    }
  });

  $.fn.timeago = function() {
    var self = this;
    self.each(refresh);

    var $s = $t.settings;
    if ($s.refreshMillis > 0) {
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
    }
    return self;
  };

  function refresh() {
    var data = prepareData(this);
    if (!isNaN(data.datetime)) {
      $(this).text(inWords(data.datetime));
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (element.data("timeago") === undefined) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if (text.length > 0) element.attr("title", text);
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  function substitute(stringOrFunction, value) {
    var string = $.isFunction(stringOrFunction) ? stringOrFunction(value) : stringOrFunction;
    return string.replace(/%d/i, value);
  }

  // fix for IE6 suckage
  document.createElement('abbr');
  document.createElement('time');
})(jQuery);
<commit_msg>Version bump to 0.8.0<commit_after>/*
 * timeago: a jQuery plugin, version: 0.8.0 (2009-10-25)
 * @requires jQuery v1.2.3 or later
 *
 * Timeago is a jQuery plugin that makes it easy to support automatically
 * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
 *
 * For usage and examples, visit:
 * http://timeago.yarp.com/
 *
 * Licensed under the MIT:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2008-2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
 */
(function($) {
  $.timeago = function(timestamp) {
    if (timestamp instanceof Date) return inWords(timestamp);
    else if (typeof timestamp == "string") return inWords($.timeago.parse(timestamp));
    else return inWords($.timeago.datetime(timestamp));
  };
  var $t = $.timeago;

  $.extend($.timeago, {
    settings: {
      refreshMillis: 60000,
      allowFuture: false,
      strings: {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: "ago",
        suffixFromNow: "from now",
        ago: null, // DEPRECATED, use suffixAgo
        fromNow: null, // DEPRECATED, use suffixFromNow
        seconds: "less than a minute",
        minute: "about a minute",
        minutes: "%d minutes",
        hour: "about an hour",
        hours: "about %d hours",
        day: "a day",
        days: "%d days",
        month: "about a month",
        months: "%d months",
        year: "about a year",
        years: "%d years"
      }
    },
    inWords: function(distanceMillis) {
      var $l = this.settings.strings;
      var prefix = $l.prefixAgo;
      var suffix = $l.suffixAgo || $l.ago;
      if (this.settings.allowFuture) {
        if (distanceMillis < 0) {
          prefix = $l.prefixFromNow;
          suffix = $l.suffixFromNow || $l.fromNow;
        }
        distanceMillis = Math.abs(distanceMillis);
      }

      var seconds = distanceMillis / 1000;
      var minutes = seconds / 60;
      var hours = minutes / 60;
      var days = hours / 24;
      var years = days / 365;

      var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 48 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.floor(days)) ||
        days < 60 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.floor(days / 30)) ||
        years < 2 && substitute($l.year, 1) ||
        substitute($l.years, Math.floor(years));

      return $.trim([prefix, words, suffix].join(" "));
    },
    parse: function(iso8601) {
      var s = $.trim(iso8601);
      s = s.replace(/-/,"/").replace(/-/,"/");
      s = s.replace(/T/," ").replace(/Z/," UTC");
      s = s.replace(/([\+-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
      return new Date(s);
    },
    datetime: function(elem) {
      var iso8601 = $(elem).is('time') ? $(elem).attr('datetime') : $(elem).attr('title');
      return $t.parse(iso8601);
    }
  });

  $.fn.timeago = function() {
    var self = this;
    self.each(refresh);

    var $s = $t.settings;
    if ($s.refreshMillis > 0) {
      setInterval(function() { self.each(refresh); }, $s.refreshMillis);
    }
    return self;
  };

  function refresh() {
    var data = prepareData(this);
    if (!isNaN(data.datetime)) {
      $(this).text(inWords(data.datetime));
    }
    return this;
  }

  function prepareData(element) {
    element = $(element);
    if (element.data("timeago") === undefined) {
      element.data("timeago", { datetime: $t.datetime(element) });
      var text = $.trim(element.text());
      if (text.length > 0) element.attr("title", text);
    }
    return element.data("timeago");
  }

  function inWords(date) {
    return $t.inWords(distance(date));
  }

  function distance(date) {
    return (new Date().getTime() - date.getTime());
  }

  function substitute(stringOrFunction, value) {
    var string = $.isFunction(stringOrFunction) ? stringOrFunction(value) : stringOrFunction;
    return string.replace(/%d/i, value);
  }

  // fix for IE6 suckage
  document.createElement('abbr');
  document.createElement('time');
})(jQuery);
<|endoftext|>


<commit_before>import React from 'react';
import AddCommentBox from './add-comment-box';
import Comment from './comment';

export default ({comments}) => (

{comments.length} {(comments.length === 1) ? 'Comment' : 'Comments'}

      <AddCommentBox />

· · ·

    {comments.map((comment) =>
      <Comment key={comment.id} comment={comment} />
    )}

);
<commit_msg>Transfering props<commit_after>import React from 'react';
import AddCommentBox from './add-comment-box';
import Comment from './comment';

export default ({user, comments}) => (

{comments.length} {(comments.length === 1) ? 'Comment' : 'Comments'}

      <AddCommentBox user={user} />

· · ·

    {comments.map((comment) =>
      <Comment key={comment.id} comment={comment} />
    )}

);
<|endoftext|>


<commit_before>(function(ng, $, page) {
  "use strict";

  /**
   * Angular service for handling server interactions with samples.
   */
  var SampleService = (function() {
    var post, get;
    var location;
    var scope;

    /**
     * Internal function to reload the datatable.
     * @private
     */
    function _reloadTable() {
      // Need to keep the associated projects
      page.ajaxParam = {date: {}, associated: page.ajaxParam.associated};
      // Clear the search field if needed.
      oTable_samplesTable.search("");
      oTable_samplesTable.ajax.reload();
    }

    /**
     * Get the current state of the datatable filters.
     * @returns {{}}
     * @private
     */
    function _getFilterState() {
      var filters = {};
      Object.assign(filters, page.ajaxParam);
      var searchTerm = document.querySelector(".dataTables_filter input").value;
      filters.search = searchTerm;
      return filters;
    }

    function SampleService($http, $window, $rootScope) {
      post = $http.post;
      get = $http.get;
      location = $window.location;
      scope = $rootScope;

      /**
       * Clear specific filters applied to the samples table.
       */
      scope.$on('FILTER_CLEARED', function(event, args) {
        if (args.type === "file") {
          delete page.ajaxParam.sampleNames;
        } else {
          delete page.ajaxParam[args.type]
        }
        oTable_samplesTable.ajax.reload();
      });

      /**
       * Clear all filters that are active on the project samples table.
       */
      scope.$on("CLEAR_FILTERS", _reloadTable);

      /**
       * Filter the project samples table based on the filter modal.
       */
      scope.$on("FILTER_TABLE", function(event, args) {
        // Setting the page object, since dandelion datatables establishes the custom
        // params dynamically.  We add to them in the sample-ajax-params.js file.
        ng.extend(page.ajaxParam, args.filter);
        // Reload the tables (which will call for these custom params).
        oTable_samplesTable.ajax.reload();
      });
    }

    /**
     * Show all success and error messages for samples being copied.
     * @param result
     */
    function showCopyRemoveErrors(result) {
      if (result.message) {
        notifications.show({type: "success", msg: result.message});
      }
      if (result.warnings) {
        result.warnings.forEach(function(warning) {
          notifications.show({type: "warning", msg: warning});
        })
      }
    }

    /**
     * Copy or move the samples.
     * @param params
     * @returns {*}
     * @private
     */
    function _copyMoveSamples(params) {
      return post(page.urls.samples.copy, params)
        .success(function(result) {
          showCopyRemoveErrors(result);
        });
    }

    /**
     * Merge samples
     * @param {object} data of sample ids and where to merge them.
     * @returns {*}
     */
    SampleService.prototype.merge = function(data) {
      var params = {
        sampleIds: data.ids,
        mergeSampleId: data.mergeSampleId,
        newName: data.newName
      };
      return post(page.urls.samples.merge, params)
        .success(function(result) {
          notifications.show({type: result.result, msg: result.message});
        });
    };

    /**
     * Copy samples to another project
     * @param params
     * @returns {*}
     */
    SampleService.prototype.copy = function(params) {
      return _copyMoveSamples(params);
    };

    /**
     * Move samples to another project
     * @param params
     * @returns {*}
     */
    SampleService.prototype.move = function(params) {
      params.remove = true;
      return _copyMoveSamples(params);
    };

    /**
     * Remove samples from this project.
     * @param ids
     * @returns {*}
     */
    SampleService.prototype.remove = function(ids) {
      return post(page.urls.samples.remove, {sampleIds: ids})
        .success(function(data) {
          notifications.show({type: data.result, msg: data.message})
        });
    };

    /**
     * Download a samples by sample id.
     * @param ids
     */
    SampleService.prototype.download = function(ids) {
      var url = page.urls.samples.download + "?" + $.param({ids: ids});
      // Need an iframe so it does not load into the current window.
      var iframe = document.querySelector("#download-iframe");
      if (iframe === null) {
        iframe = document.createElement("iframe");
        iframe.setAttribute("id", "download-iframe");
        iframe.style.display = "none";
      }
      iframe.setAttribute('src', url);
      document.body.appendChild(iframe);
    };

    /**
     * Load the NCBI export page based on selected samples.
     * @param ids
     */
    SampleService.prototype.ncbiExport = function(ids) {
      location.href = page.urls.samples.ncbi + "?" + $.param({ids: ids});
    };

    /**
     * Filter the datatable based on a set of sample names.
     * @param {array} sampleNames
     */
    SampleService.prototype.filterBySampleNames = function(sampleNames) {
      // Store the sample names so datatables can add them to the url
      // This is done in sample-ajax-params.js
      page.ajaxParam.sampleNames = sampleNames;
      oTable_samplesTable.ajax.reload(function() {
        // Need to see if anything is missing
        post(page.urls.fileMissingSamples, {
          sampleNames: sampleNames,
          projects: page.ajaxParam.associated
        }).then(function(response) {
          if (response.data.missingNames) {
            var msg = "" + response.data.message + "
";
            response.data.missingNames.forEach(function(name) {
              msg += "
" + name + "
"
            });
            msg += "
";
            notifications.show({type: "warning", msg: msg, timeout: false});
          } else {
            notifications.show({type: "success", msg: response.data.success})
          }
        });
        scope.$broadcast("FILE_FILTER");
      });
    };

    /**
     * Export the datatable to a file.
     * @param {string} type either 'xlsx' or 'csv'
     */
    SampleService.prototype.exportToFile = function(type) {
      // Need the parameters from the datatable so that we know the correct filters.
      var tableParams = oTable_samplesTable.ajax.params();
      tableParams.dtf = type; // This is an important parameter name for exporting the correct format.

      // Need an iframe here since it is a document download not just an ajax request.
      var iframe = document.createElement("iframe");
      iframe.style.display = "none";
      iframe.src = page.urls.samples.export + "?" + $.param(tableParams);
      document.body.appendChild(iframe);
    };

    /**
     * Update the table to display any associated projects.
     * @param {array} projectsIds - list of project ids for associated projects.
     */
    SampleService.prototype.updateAssociatedProjects = function(projectsIds) {
      page.ajaxParam.associated = projectsIds;
      oTable_samplesTable.ajax.reload();
    };

    /**
     * Get all the ids for the samples currently displayed in the datatable.
     * @returns {*}
     */
    SampleService.prototype.getAllIds = function() {
      return post(page.urls.samples.sampleIds, _getFilterState())
        .success(function(result) {
          return result;
        });
    };

    return SampleService;
  }());

  ng.module("irida.projects.samples.service", ["irida.cart"])
    .service('SampleService', ["$http", "$window", "$rootScope", SampleService])
  ;
}(window.angular, window.jQuery, window.PAGE));
<commit_msg>Fixed response call<commit_after>(function(ng, $, page) {
  "use strict";

  /**
   * Angular service for handling server interactions with samples.
   */
  var SampleService = (function() {
    var post, get;
    var location;
    var scope;

    /**
     * Internal function to reload the datatable.
     * @private
     */
    function _reloadTable() {
      // Need to keep the associated projects
      page.ajaxParam = {date: {}, associated: page.ajaxParam.associated};
      // Clear the search field if needed.
      oTable_samplesTable.search("");
      oTable_samplesTable.ajax.reload();
    }

    /**
     * Get the current state of the datatable filters.
     * @returns {{}}
     * @private
     */
    function _getFilterState() {
      var filters = {};
      Object.assign(filters, page.ajaxParam);
      var searchTerm = document.querySelector(".dataTables_filter input").value;
      filters.search = searchTerm;
      return filters;
    }

    function SampleService($http, $window, $rootScope) {
      post = $http.post;
      get = $http.get;
      location = $window.location;
      scope = $rootScope;

      /**
       * Clear specific filters applied to the samples table.
       */
      scope.$on('FILTER_CLEARED', function(event, args) {
        if (args.type === "file") {
          delete page.ajaxParam.sampleNames;
        } else {
          delete page.ajaxParam[args.type]
        }
        oTable_samplesTable.ajax.reload();
      });

      /**
       * Clear all filters that are active on the project samples table.
       */
      scope.$on("CLEAR_FILTERS", _reloadTable);

      /**
       * Filter the project samples table based on the filter modal.
       */
      scope.$on("FILTER_TABLE", function(event, args) {
        // Setting the page object, since dandelion datatables establishes the custom
        // params dynamically.  We add to them in the sample-ajax-params.js file.
        ng.extend(page.ajaxParam, args.filter);
        // Reload the tables (which will call for these custom params).
        oTable_samplesTable.ajax.reload();
      });
    }

    /**
     * Show all success and error messages for samples being copied.
     * @param result
     */
    function showCopyRemoveErrors(result) {
      if (result.message) {
        notifications.show({type: "success", msg: result.message});
      }
      if (result.warnings) {
        result.warnings.forEach(function(warning) {
          notifications.show({type: "warning", msg: warning});
        })
      }
    }

    /**
     * Copy or move the samples.
     * @param params
     * @returns {*}
     * @private
     */
    function _copyMoveSamples(params) {
      return post(page.urls.samples.copy, params)
        .success(function(result) {
          showCopyRemoveErrors(result);
        });
    }

    /**
     * Merge samples
     * @param {object} data of sample ids and where to merge them.
     * @returns {*}
     */
    SampleService.prototype.merge = function(data) {
      var params = {
        sampleIds: data.ids,
        mergeSampleId: data.mergeSampleId,
        newName: data.newName
      };
      return post(page.urls.samples.merge, params)
        .success(function(result) {
          notifications.show({type: result.result, msg: result.message});
        });
    };

    /**
     * Copy samples to another project
     * @param params
     * @returns {*}
     */
    SampleService.prototype.copy = function(params) {
      return _copyMoveSamples(params);
    };

    /**
     * Move samples to another project
     * @param params
     * @returns {*}
     */
    SampleService.prototype.move = function(params) {
      params.remove = true;
      return _copyMoveSamples(params);
    };

    /**
     * Remove samples from this project.
     * @param ids
     * @returns {*}
     */
    SampleService.prototype.remove = function(ids) {
      return post(page.urls.samples.remove, {sampleIds: ids})
        .success(function(data) {
          notifications.show({type: data.result, msg: data.message})
        });
    };

    /**
     * Download a samples by sample id.
     * @param ids
     */
    SampleService.prototype.download = function(ids) {
      var url = page.urls.samples.download + "?" + $.param({ids: ids});
      // Need an iframe so it does not load into the current window.
      var iframe = document.querySelector("#download-iframe");
      if (iframe === null) {
        iframe = document.createElement("iframe");
        iframe.setAttribute("id", "download-iframe");
        iframe.style.display = "none";
      }
      iframe.setAttribute('src', url);
      document.body.appendChild(iframe);
    };

    /**
     * Load the NCBI export page based on selected samples.
     * @param ids
     */
    SampleService.prototype.ncbiExport = function(ids) {
      location.href = page.urls.samples.ncbi + "?" + $.param({ids: ids});
    };

    /**
     * Filter the datatable based on a set of sample names.
     * @param {array} sampleNames
     */
    SampleService.prototype.filterBySampleNames = function(sampleNames) {
      // Store the sample names so datatables can add them to the url
      // This is done in sample-ajax-params.js
      page.ajaxParam.sampleNames = sampleNames;
      oTable_samplesTable.ajax.reload(function() {
        // Need to see if anything is missing
        post(page.urls.fileMissingSamples, {
          sampleNames: sampleNames,
          projects: page.ajaxParam.associated
        }).then(function(response) {
          if (response.data.missingNames) {
            var msg = "" + response.data.message + "
";
            response.data.missingNames.forEach(function(name) {
              msg += "
" + name + "
"
            });
            msg += "
";
            notifications.show({type: "warning", msg: msg, timeout: false});
          } else {
            notifications.show({type: "success", msg: response.data.success})
          }
        });
        scope.$broadcast("FILE_FILTER");
      });
    };

    /**
     * Export the datatable to a file.
     * @param {string} type either 'xlsx' or 'csv'
     */
    SampleService.prototype.exportToFile = function(type) {
      // Need the parameters from the datatable so that we know the correct filters.
      var tableParams = oTable_samplesTable.ajax.params();
      tableParams.dtf = type; // This is an important parameter name for exporting the correct format.

      // Need an iframe here since it is a document download not just an ajax request.
      var iframe = document.createElement("iframe");
      iframe.style.display = "none";
      iframe.src = page.urls.samples.export + "?" + $.param(tableParams);
      document.body.appendChild(iframe);
    };

    /**
     * Update the table to display any associated projects.
     * @param {array} projectsIds - list of project ids for associated projects.
     */
    SampleService.prototype.updateAssociatedProjects = function(projectsIds) {
      page.ajaxParam.associated = projectsIds;
      oTable_samplesTable.ajax.reload();
    };

    /**
     * Get all the ids for the samples currently displayed in the datatable.
     * @returns {*}
     */
    SampleService.prototype.getAllIds = function() {
      return post(page.urls.samples.sampleIds, _getFilterState())
        .then(function(result) {
          return result.data;
        });
    };

    return SampleService;
  }());

  ng.module("irida.projects.samples.service", ["irida.cart"])
    .service('SampleService', ["$http", "$window", "$rootScope", SampleService])
  ;
}(window.angular, window.jQuery, window.PAGE));
<|endoftext|>


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

foam.CLASS({
  package: 'foam.u2.view',
  name: 'TableCellPropertyRefinement',

  refines: 'foam.core.Property',

  properties: [
    {
      name: 'tableHeaderFormatter',
      value: function(axiom) {
        this.add(axiom.label);
      }
    },
    {
      name: 'tableCellFormatter',
      value: function(value, obj, axiom) {
        this.add(value);
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Action',

  properties: [
    {
      name: 'tableCellFormatter',
      value: function(_, obj, axiom) {
        this.
          startContext({ data: obj }).
          add(axiom).
          endContext();
      }
    },
    {
      name: 'tableHeaderFormatter',
      value: function(axiom) {
        this.add(axiom.label);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'TableView',
  extends: 'foam.u2.Element',

  implements: [ 'foam.mlang.Expressions' ],

  exports: [
    'columns',
    'selection',
    'hoverSelection'
  ],

  imports: [
    'editRecord?',
    'selection? as importSelection'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function CSS() {/*
        ^ th {
          text-align: left;
          white-space: nowrap;
        }

        ^row:hover {
          background: #eee;
        }

        ^selected {
          background: #eee;
          outline: 1px solid #f00;
        }
    */}
    })
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'orderedDAO',
      expression: function(data, order) {
        return data ? data.orderBy(order) : foam.dao.NullDAO.create();
      }
    },
    {
      name: 'order'
    },
    {
      name: 'columns_',
      expression: function(columns, data, of) {
        var of = this.of || ( data && data.of);
        if ( ! of ) return [];

        return columns.map(function(p) {
          return typeof p == 'string' ?
            of.getAxiomByName(p) :
            p ;
        });
      }
    },
    {
      name: 'columns',
      expression: function(data, of) {
        var of = this.of || ( data && data.of);
        if ( ! of ) return [];

        var tableColumns = of.getAxiomByName('tableColumns');

        if ( tableColumns ) return tableColumns.columns;

        return of.getAxiomsByClass(foam.core.Property).
            filter(function(p) { return ! p.hidden; }).
            map(foam.core.Property.NAME.f);
      }
    },
    'selection',
    'hoverSelection'
  ],

  methods: [
    function sortBy(column) {
      this.order = this.order === column ?
        this.DESC(column) :
        column;
    },

    function initE() {
      var view = this;

      this.
        addClass(this.myClass()).
        setNodeName('table').
        start('thead').
        add(this.slot(function(columns_) {
          return this.E('tr').
            forEach(columns_, function(column) {
              this.
                start('th').
                on('click', function(e) { view.sortBy(column); }).
                call(column.tableHeaderFormatter, [column]).
                add(' ', this.slot(function(order) {
                  return column === order ? this.Entity.create({ name: '#9651' }) :
                      (view.Desc.isInstance(order) && order.arg1 === column) ? this.Entity.create({ name: '#9661' }) :
                      ''
                }, view.order$)).
                end();
            });
        })).
        add(this.slot(function(columns_) {
          return this.
            E('tbody').
            select(this.orderedDAO$proxy, function(obj) {
              return this.
                E('tr').
                start('tr').
                on('mouseover', function() { view.hoverSelection = obj; }).
                on('click', function() {
                  view.selection = obj;
                  if ( view.importSelection$ ) view.importSelection = obj;
                  if ( view.editRecord$ ) view.editRecord(obj);
                }).
                addClass(this.slot(function(selection) {
                  if ( obj === selection ) return view.myClass('selected');
                  return '';
                }, view.selection$)).
                addClass(view.myClass('row')).
                forEach(columns_, function(column) {
                  this.
                    start('td').
                    call(column.tableCellFormatter, [
                      column.f ? column.f(obj) : null, obj, column
                    ]).
                    end();
                });
            });
        }));
    }
  ]
});
<commit_msg>Fix indentation.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'TableCellPropertyRefinement',

  refines: 'foam.core.Property',

  properties: [
    {
      name: 'tableHeaderFormatter',
      value: function(axiom) {
        this.add(axiom.label);
      }
    },
    {
      name: 'tableCellFormatter',
      value: function(value, obj, axiom) {
        this.add(value);
      }
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Action',

  properties: [
    {
      name: 'tableCellFormatter',
      value: function(_, obj, axiom) {
        this.
          startContext({ data: obj }).
          add(axiom).
          endContext();
      }
    },
    {
      name: 'tableHeaderFormatter',
      value: function(axiom) {
        this.add(axiom.label);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'TableView',
  extends: 'foam.u2.Element',

  implements: [ 'foam.mlang.Expressions' ],

  exports: [
    'columns',
    'selection',
    'hoverSelection'
  ],

  imports: [
    'editRecord?',
    'selection? as importSelection'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function CSS() {/*
        ^ th {
          text-align: left;
          white-space: nowrap;
        }

        ^row:hover {
          background: #eee;
        }

        ^selected {
          background: #eee;
          outline: 1px solid #f00;
        }
    */}
    })
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'orderedDAO',
      expression: function(data, order) {
        return data ? data.orderBy(order) : foam.dao.NullDAO.create();
      }
    },
    {
      name: 'order'
    },
    {
      name: 'columns_',
      expression: function(columns, data, of) {
        var of = this.of || ( data && data.of);
        if ( ! of ) return [];

        return columns.map(function(p) {
          return typeof p == 'string' ?
            of.getAxiomByName(p) :
            p ;
        });
      }
    },
    {
      name: 'columns',
      expression: function(data, of) {
        var of = this.of || ( data && data.of);
        if ( ! of ) return [];

        var tableColumns = of.getAxiomByName('tableColumns');

        if ( tableColumns ) return tableColumns.columns;

        return of.getAxiomsByClass(foam.core.Property).
            filter(function(p) { return ! p.hidden; }).
            map(foam.core.Property.NAME.f);
      }
    },
    'selection',
    'hoverSelection'
  ],

  methods: [
    function sortBy(column) {
      this.order = this.order === column ?
        this.DESC(column) :
        column;
    },

    function initE() {
      var view = this;

      this.
        addClass(this.myClass()).
        setNodeName('table').
        start('thead').
          add(this.slot(function(columns_) {
            return this.E('tr').
              forEach(columns_, function(column) {
                this.start('th').
                  on('click', function(e) { view.sortBy(column); }).
                  call(column.tableHeaderFormatter, [column]).
                  add(' ', this.slot(function(order) {
                    return column === order ? this.Entity.create({ name: '#9651' }) :
                        (view.Desc.isInstance(order) && order.arg1 === column) ? this.Entity.create({ name: '#9661' }) :
                        ''
                  }, view.order$)).
                end();
              });
          })).
          add(this.slot(function(columns_) {
            return this.
              E('tbody').
              select(this.orderedDAO$proxy, function(obj) {
                return this.
                  E('tr').
                    start('tr').
                      on('mouseover', function() { view.hoverSelection = obj; }).
                      on('click', function() {
                        view.selection = obj;
                        if ( view.importSelection$ ) view.importSelection = obj;
                        if ( view.editRecord$ ) view.editRecord(obj);
                      }).
                      addClass(this.slot(function(selection) {
                        if ( obj === selection ) return view.myClass('selected');
                        return '';
                      }, view.selection$)).
                      addClass(view.myClass('row')).
                      forEach(columns_, function(column) {
                        this.
                          start('td').
                          call(column.tableCellFormatter, [
                            column.f ? column.f(obj) : null, obj, column
                          ]).
                          end();
                      });
              });
          }));
    }
  ]
});
<|endoftext|>


<commit_before>function showKandidaat(str) {
	var xhttp;
	document.getElementById("spinner").innerHTML = "";
	if(str.length === 0) { // kui vastus tyhi
		document.getElementById("kandB").innerHTML = "";
		return;
	}
	xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
		if(xhttp.readyState === 4 && xhttp.status === 200) {
			var data_json = jQuery.parseJSON(xhttp.responseText);
			document.getElementById("kandB").innerHTML = "<button class=\"btn btn-default\" id=\"haal\" type=\"submit\">Hääleta</button>
Kandidaat: "+data_json.Nimi+", "+data_json.Erakond+", "+data_json.Piirkond+"
";

		} else {
			document.getElementById("kandB").innerHTML = "";
		}
	};
	xhttp.open("GET", "ajaxResponse?q="+str,true);
	xhttp.send();
}

function pollKandidaat() {
	var box = document.getElementById("ajutine");

	$.ajax({
		type: "GET",
		url: "http://valimised16.cs.ut.ee/index.php/sait/pollResponse",
		async: true,
		cache: false,
		timeout:50000,

		success: function(data) {
			box.innerHTML = data;
			setTimeout(pollKandidaat,5000);
		},
		error: function(textStatus, errorThrown) {
			box.innerHTML = textStatus + " " + errorThrown;
			setTimeout(pollKandidaat,15000);
		}
	});
}

function pollVotes() {
	if(window.location.pathname != "/index.php/sait/tulemused") {
		return;
	}
	$.ajax({
		type: "GET",
		url: "http://valimised16.cs.ut.ee/index.php/sait/getVotesLive",
		async: true,
		timeout: 50000,

		success: function(data) {
			var data_json = jQuery.parseJSON(data);
			$.each(data_json, function(k, v) {
				document.getElementById(k).innerHTML = v;
			});
		}
	});
}

$(document).ready(function() {

	// long polling
	pollKandidaat();
	pollVotes();

	// haale sisestamine
	var request;

	$("#kandB").on('click','#haal', function() {

		if(request) {
			request.abort();
		}

		var $input = $("#data");

		$input.prop("disabled", true);
		$("#haal").prop("disabled",true);
		var spinner = document.getElementById("spinner");
		spinner.innerHTML = "";
		request = $.ajax({
			url: "haal",
			cache: false,
			type: "post",
			data: {'haaletus' : $input.val()},
			success: function(resp) {
				spinner.innerHTML = "Hääl kinnitatud." + resp;
			},
			complete: function() {
			},
			error: function(jq,status,message) {
				spinner.innerHTML = jq + " " + status + " " + message;
			}
		});
		request.always(function() {
			//$input.prop("disabled",false);
		});
		event.preventDefault();
	});
});
<commit_msg>koikide haalte uuendamine reaalajas, long polling<commit_after>function showKandidaat(str) {
	var xhttp;
	document.getElementById("spinner").innerHTML = "";
	if(str.length === 0) { // kui vastus tyhi
		document.getElementById("kandB").innerHTML = "";
		return;
	}
	xhttp = new XMLHttpRequest();
	xhttp.onreadystatechange = function() {
		if(xhttp.readyState === 4 && xhttp.status === 200) {
			var data_json = jQuery.parseJSON(xhttp.responseText);
			document.getElementById("kandB").innerHTML = "<button class=\"btn btn-default\" id=\"haal\" type=\"submit\">Hääleta</button>
Kandidaat: "+data_json.Nimi+", "+data_json.Erakond+", "+data_json.Piirkond+"
";

		} else {
			document.getElementById("kandB").innerHTML = "";
		}
	};
	xhttp.open("GET", "ajaxResponse?q="+str,true);
	xhttp.send();
}

function pollKandidaat() {
	var box = document.getElementById("ajutine");

	$.ajax({
		type: "GET",
		url: "http://valimised16.cs.ut.ee/index.php/sait/pollResponse",
		async: true,
		cache: false,
		timeout:50000,

		success: function(data) {
			box.innerHTML = data;
			setTimeout(pollKandidaat,5000);
		},
		error: function(textStatus, errorThrown) {
			box.innerHTML = textStatus + " " + errorThrown;
			setTimeout(pollKandidaat,15000);
		}
	});
}

function pollVotes() {
	if(window.location.pathname != "/index.php/sait/tulemused") {
		return;
	}
	$.ajax({
		type: "GET",
		url: "http://valimised16.cs.ut.ee/index.php/sait/getVotesLive",
		async: true,
		timeout: 50000,

		success: function(data) {
			var data_json = jQuery.parseJSON(data);
			$.each(data_json, function(k, v) {
				document.getElementById(k).innerHTML = v;
			});

			setTimeout(pollVotes,5000);
		},
		error: function(textStatus, errorThrown) {
			setTimeout(pollVotes,10000);
		}
	});
}

$(document).ready(function() {

	// long polling
	pollKandidaat();
	pollVotes();

	// haale sisestamine
	var request;

	$("#kandB").on('click','#haal', function() {

		if(request) {
			request.abort();
		}

		var $input = $("#data");

		$input.prop("disabled", true);
		$("#haal").prop("disabled",true);
		var spinner = document.getElementById("spinner");
		spinner.innerHTML = "";
		request = $.ajax({
			url: "haal",
			cache: false,
			type: "post",
			data: {'haaletus' : $input.val()},
			success: function(resp) {
				spinner.innerHTML = "Hääl kinnitatud." + resp;
			},
			complete: function() {
			},
			error: function(jq,status,message) {
				spinner.innerHTML = jq + " " + status + " " + message;
			}
		});
		request.always(function() {
			//$input.prop("disabled",false);
		});
		event.preventDefault();
	});
});
<|endoftext|>


<commit_before>/*global Mousetrap:true */
import loadScript from 'discourse/lib/load-script';
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";

// Our head can be a static string or a function that returns a string
// based on input (like for numbered lists).
function getHead(head, prev) {
  if (typeof head === "string") {
    return [head, head.length];
  } else {
    return getHead(head(prev));
  }
}

const _createCallbacks = [];

function Toolbar() {
  this.shortcuts = {};

  this.groups = [
    {group: 'fontStyles', buttons: []},
    {group: 'insertions', buttons: []},
    {group: 'extras', buttons: [], lastGroup: true}
  ];

  this.addButton({
    id: 'bold',
    group: 'fontStyles',
    shortcut: 'B',
    perform: e => e.applySurround('**', '**', 'bold_text')
  });

  this.addButton({
    id: 'italic',
    group: 'fontStyles',
    shortcut: 'I',
    perform: e => e.applySurround('*', '*', 'italic_text')
  });

  this.addButton({id: 'link', group: 'insertions', shortcut: 'K', action: 'showLinkModal'});

  this.addButton({
    id: 'quote',
    group: 'insertions',
    icon: 'quote-right',
    shortcut: 'Shift+9',
    perform: e => e.applySurround('> ', '', 'code_text')
  });

  this.addButton({
    id: 'code',
    group: 'insertions',
    shortcut: 'Shift+C',
    perform(e) {
      if (e.selected.value.indexOf("\n") !== -1) {
        e.applySurround('    ', '', 'code_text');
      } else {
        e.applySurround('`', '`', 'code_text');
      }
    },
  });

  this.addButton({
    id: 'bullet',
    group: 'extras',
    icon: 'list-ul',
    shortcut: 'Shift+8',
    title: 'composer.ulist_title',
    perform: e => e.applyList('* ', 'list_item')
  });

  this.addButton({
    id: 'list',
    group: 'extras',
    icon: 'list-ol',
    shortcut: 'Shift+7',
    title: 'composer.olist_title',
    perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item')
  });

  this.addButton({
    id: 'heading',
    group: 'extras',
    icon: 'font',
    shortcut: 'Alt+1',
    perform: e => e.applyList('## ', 'heading_text')
  });

  this.addButton({
    id: 'rule',
    group: 'extras',
    icon: 'minus',
    shortcut: 'Alt+R',
    title: 'composer.hr_title',
    perform: e => e.addText("\n\n----------\n")
  });
};

Toolbar.prototype.addButton = function(button) {
  const g = this.groups.findProperty('group', button.group);
  if (!g) {
    throw `Couldn't find toolbar group ${button.group}`;
  }

  const createdButton = {
    id: button.id,
    className: button.className || button.id,
    icon: button.icon || button.id,
    action: button.action || 'toolbarButton',
    perform: button.perform || Ember.K
  };

  if (button.sendAction) {
    createdButton.sendAction = button.sendAction;
  }

  const title = I18n.t(button.title || `composer.${button.id}_title`);
  if (button.shortcut) {
    const mac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
    const mod = mac ? 'Meta' : 'Ctrl';
    createdButton.title = `${title} (${mod}+${button.shortcut})`;

    // Mac users are used to glyphs for shortcut keys
    if (mac) {
      createdButton.title = createdButton.title.replace('Shift', "\u21E7")
                                               .replace('Meta', "\u2318")
                                               .replace('Alt', "\u2325")
                                               .replace(/\+/g, '');
    }

    this.shortcuts[`${mod}+${button.shortcut}`.toLowerCase()] = createdButton;
  } else {
    createdButton.title = title;
  }

  if (button.unshift) {
    g.buttons.unshift(createdButton);
  } else {
    g.buttons.push(createdButton);
  }
};

export function onToolbarCreate(func) {
  _createCallbacks.push(func);
};

export default Ember.Component.extend({
  classNames: ['d-editor'],
  ready: false,
  insertLinkHidden: true,
  link: '',
  lastSel: null,

  @computed('placeholder')
  placeholderTranslated(placeholder) {
    if (placeholder) return I18n.t(placeholder);
    return null;
  },

  @on('didInsertElement')
  _startUp() {
    this._applyEmojiAutocomplete();

    loadScript('defer/html-sanitizer-bundle').then(() => this.set('ready', true));

    const shortcuts = this.get('toolbar.shortcuts');
    Ember.keys(shortcuts).forEach(sc => {
      const button = shortcuts[sc];
      Mousetrap(this.$('.d-editor-input')[0]).bind(sc, () => {
        this.send(button.action, button);
      });
    });

    // disable clicking on links in the preview
    this.$('.d-editor-preview').on('click.preview', e => {
      e.preventDefault();
      return false;
    });

    this.appEvents.on('composer:insert-text', text => {
      this._addText(this._getSelected(), text);
    });
  },

  @on('willDestroyElement')
  _shutDown() {
    this.appEvents.off('composer:insert-text');

    Ember.keys(this.get('toolbar.shortcuts')).forEach(sc => {
      Mousetrap(this.$('.d-editor-input')[0]).unbind(sc);
    });
    this.$('.d-editor-preview').off('click.preview');
  },

  @computed
  toolbar() {
    const toolbar = new Toolbar();
    _createCallbacks.forEach(cb => cb(toolbar));
    this.sendAction('extraButtons', toolbar);
    return toolbar;
  },

  @computed('ready', 'value')
  preview(ready, value) {
    if (!ready) { return; }

    const markdownOptions = this.get('markdownOptions') || {};
    markdownOptions.sanitize = true;

    const text = Discourse.Dialect.cook(value || "", markdownOptions);
    Ember.run.scheduleOnce('afterRender', () => {
      if (this._state !== "inDOM") { return; }
      const $preview = this.$('.d-editor-preview');
      if ($preview.length === 0) return;

      this.sendAction('previewUpdated', $preview);
    });

    return text ? text : "";
  },

  _applyEmojiAutocomplete() {
    if (!this.siteSettings.enable_emoji) { return; }

    const container = this.container;
    const template = container.lookup('template:emoji-selector-autocomplete.raw');
    const self = this;

    this.$('.d-editor-input').autocomplete({
      template: template,
      key: ":",

      transformComplete(v) {
        if (v.code) {
          return `${v.code}:`;
        } else {
          showSelector({
            appendTo: self.$(),
            container,
            onSelect: title => self._addText(this._getSelected(), `${title}:`)
          });
          return "";
        }
      },

      dataSource(term) {
        return new Ember.RSVP.Promise(resolve => {
          const full = `:${term}`;
          term = term.toLowerCase();

          if (term === "") {
            return resolve(["smile", "smiley", "wink", "sunny", "blush"]);
          }

          if (Discourse.Emoji.translations[full]) {
            return resolve([Discourse.Emoji.translations[full]]);
          }

          const options = Discourse.Emoji.search(term, {maxResults: 5});

          return resolve(options);
        }).then(list => list.map(code => {
          return {code, src: Discourse.Emoji.urlFor(code)};
        })).then(list => {
          if (list.length) {
            list.push({ label: I18n.t("composer.more_emoji") });
          }
          return list;
        });
      }
    });
  },

  _getSelected() {
    if (!this.get('ready')) { return; }

    const textarea = this.$('textarea.d-editor-input')[0];
    const start = textarea.selectionStart;
    const end = textarea.selectionEnd;

    const value = textarea.value.substring(start, end);
    const pre = textarea.value.slice(0, start);
    const post = textarea.value.slice(end);

    return { start, end, value, pre, post };
  },

  _selectText(from, length) {
    Ember.run.scheduleOnce('afterRender', () => {
      const textarea = this.$('textarea.d-editor-input')[0];
      textarea.focus();
      textarea.selectionStart = from;
      textarea.selectionEnd = textarea.selectionStart + length;
    });
  },

  _applySurround(sel, head, tail, exampleKey) {
    const pre = sel.pre;
    const post = sel.post;

    const tlen = tail.length;
    if (sel.start === sel.end) {
      if (tlen === 0) { return; }

      const [hval, hlen] = getHead(head);
      const example = I18n.t(`composer.${exampleKey}`);
      this.set('value', `${pre}${hval}${example}${tail}${post}`);
      this._selectText(pre.length + hlen, example.length);
    } else {
      const lines = sel.value.split("\n");

      let [hval, hlen] = getHead(head);
      if (lines.length === 1 && pre.slice(-tlen) === tail && post.slice(0, hlen) === hval) {
        this.set('value', `${pre.slice(0, -hlen)}${sel.value}${post.slice(tlen)}`);
        this._selectText(sel.start - hlen, sel.value.length);
      } else {
        const contents = lines.map(l => {
          if (l.length === 0) { return l; }

          if (l.slice(0, hlen) === hval && tlen === 0 || l.slice(-tlen) === tail) {
            if (tlen === 0) {
              const result = l.slice(hlen);
              [hval, hlen] = getHead(head, hval);
              return result;
            } else if (l.slice(-tlen) === tail) {
              const result = l.slice(hlen, -tlen);
              [hval, hlen] = getHead(head, hval);
              return result;
            }
          }
          const result = `${hval}${l}${tail}`;
          [hval, hlen] = getHead(head, hval);
          return result;
        }).join("\n");

        this.set('value', `${pre}${contents}${post}`);
        if (lines.length === 1 && tlen > 0) {
          this._selectText(sel.start + hlen, contents.length - hlen - hlen);
        } else {
          this._selectText(sel.start, contents.length);
        }
      }
    }
  },

  _applyList(sel, head, exampleKey) {
    if (sel.value.indexOf("\n") !== -1) {
      this._applySurround(sel, head, '', exampleKey);
    } else {

      const [hval, hlen] = getHead(head);
      if (sel.start === sel.end) {
        sel.value = I18n.t(`composer.${exampleKey}`);
      }

      const trimmedPre = sel.pre.trim();
      const number = (sel.value.indexOf(hval) === 0) ? sel.value.slice(hlen) : `${hval}${sel.value}`;
      const preLines = trimmedPre.length ? `${trimmedPre}\n\n` : "";

      const trimmedPost = sel.post.trim();
      const post = trimmedPost.length ? `\n\n${trimmedPost}` : trimmedPost;

      this.set('value', `${preLines}${number}${post}`);
      this._selectText(preLines.length, number.length);
    }
  },

  _addText(sel, text) {
    const insert = `${sel.pre}${text}`;
    this.set('value', `${insert}${sel.post}`);
    this._selectText(insert.length, 0);
  },

  actions: {
    toolbarButton(button) {
      const selected = this._getSelected();
      const toolbarEvent = {
        selected,
        applySurround: (head, tail, exampleKey) => this._applySurround(selected, head, tail, exampleKey),
        applyList: (head, exampleKey) => this._applyList(selected, head, exampleKey),
        addText: text => this._addText(selected, text)
      };

      if (button.sendAction) {
        return this.sendAction(button.sendAction, toolbarEvent);
      } else {
        button.perform(toolbarEvent);
      }
    },

    showLinkModal() {
      this._lastSel = this._getSelected();
      this.set('insertLinkHidden', false);
    },

    insertLink() {
      const link = this.get('link');

      if (Ember.isEmpty(link)) { return; }
      const m = / "([^"]+)"/.exec(link);
      if (m && m.length === 2) {
        const description = m[1];
        const remaining = link.replace(m[0], '');
        this._addText(this._lastSel, `[${description}](${remaining})`);
      } else {
        const selectedValue = this._lastSel.value || link;
        this._addText(this._lastSel, `[${selectedValue}](${link})`);
      }

      this.set('link', '');
    },

    emoji() {
      showSelector({
        appendTo: this.$(),
        container: this.container,
        onSelect: title => this._addText(this._getSelected(), `:${title}:`)
      });
    }
  }

});
<commit_msg>FIX: `d-editor` wasn't properly debouncing updates<commit_after>/*global Mousetrap:true */
import loadScript from 'discourse/lib/load-script';
import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
import { showSelector } from "discourse/lib/emoji/emoji-toolbar";

// Our head can be a static string or a function that returns a string
// based on input (like for numbered lists).
function getHead(head, prev) {
  if (typeof head === "string") {
    return [head, head.length];
  } else {
    return getHead(head(prev));
  }
}

const _createCallbacks = [];

function Toolbar() {
  this.shortcuts = {};

  this.groups = [
    {group: 'fontStyles', buttons: []},
    {group: 'insertions', buttons: []},
    {group: 'extras', buttons: [], lastGroup: true}
  ];

  this.addButton({
    id: 'bold',
    group: 'fontStyles',
    shortcut: 'B',
    perform: e => e.applySurround('**', '**', 'bold_text')
  });

  this.addButton({
    id: 'italic',
    group: 'fontStyles',
    shortcut: 'I',
    perform: e => e.applySurround('*', '*', 'italic_text')
  });

  this.addButton({id: 'link', group: 'insertions', shortcut: 'K', action: 'showLinkModal'});

  this.addButton({
    id: 'quote',
    group: 'insertions',
    icon: 'quote-right',
    shortcut: 'Shift+9',
    perform: e => e.applySurround('> ', '', 'code_text')
  });

  this.addButton({
    id: 'code',
    group: 'insertions',
    shortcut: 'Shift+C',
    perform(e) {
      if (e.selected.value.indexOf("\n") !== -1) {
        e.applySurround('    ', '', 'code_text');
      } else {
        e.applySurround('`', '`', 'code_text');
      }
    },
  });

  this.addButton({
    id: 'bullet',
    group: 'extras',
    icon: 'list-ul',
    shortcut: 'Shift+8',
    title: 'composer.ulist_title',
    perform: e => e.applyList('* ', 'list_item')
  });

  this.addButton({
    id: 'list',
    group: 'extras',
    icon: 'list-ol',
    shortcut: 'Shift+7',
    title: 'composer.olist_title',
    perform: e => e.applyList(i => !i ? "1. " : `${parseInt(i) + 1}. `, 'list_item')
  });

  this.addButton({
    id: 'heading',
    group: 'extras',
    icon: 'font',
    shortcut: 'Alt+1',
    perform: e => e.applyList('## ', 'heading_text')
  });

  this.addButton({
    id: 'rule',
    group: 'extras',
    icon: 'minus',
    shortcut: 'Alt+R',
    title: 'composer.hr_title',
    perform: e => e.addText("\n\n----------\n")
  });
};

Toolbar.prototype.addButton = function(button) {
  const g = this.groups.findProperty('group', button.group);
  if (!g) {
    throw `Couldn't find toolbar group ${button.group}`;
  }

  const createdButton = {
    id: button.id,
    className: button.className || button.id,
    icon: button.icon || button.id,
    action: button.action || 'toolbarButton',
    perform: button.perform || Ember.K
  };

  if (button.sendAction) {
    createdButton.sendAction = button.sendAction;
  }

  const title = I18n.t(button.title || `composer.${button.id}_title`);
  if (button.shortcut) {
    const mac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
    const mod = mac ? 'Meta' : 'Ctrl';
    createdButton.title = `${title} (${mod}+${button.shortcut})`;

    // Mac users are used to glyphs for shortcut keys
    if (mac) {
      createdButton.title = createdButton.title.replace('Shift', "\u21E7")
                                               .replace('Meta', "\u2318")
                                               .replace('Alt', "\u2325")
                                               .replace(/\+/g, '');
    }

    this.shortcuts[`${mod}+${button.shortcut}`.toLowerCase()] = createdButton;
  } else {
    createdButton.title = title;
  }

  if (button.unshift) {
    g.buttons.unshift(createdButton);
  } else {
    g.buttons.push(createdButton);
  }
};

export function onToolbarCreate(func) {
  _createCallbacks.push(func);
};

export default Ember.Component.extend({
  classNames: ['d-editor'],
  ready: false,
  insertLinkHidden: true,
  link: '',
  lastSel: null,

  @computed('placeholder')
  placeholderTranslated(placeholder) {
    if (placeholder) return I18n.t(placeholder);
    return null;
  },

  @on('didInsertElement')
  _startUp() {
    this._applyEmojiAutocomplete();

    loadScript('defer/html-sanitizer-bundle').then(() => this.set('ready', true));

    const shortcuts = this.get('toolbar.shortcuts');
    Ember.keys(shortcuts).forEach(sc => {
      const button = shortcuts[sc];
      Mousetrap(this.$('.d-editor-input')[0]).bind(sc, () => {
        this.send(button.action, button);
      });
    });

    // disable clicking on links in the preview
    this.$('.d-editor-preview').on('click.preview', e => {
      e.preventDefault();
      return false;
    });

    this.appEvents.on('composer:insert-text', text => {
      this._addText(this._getSelected(), text);
    });
  },

  @on('willDestroyElement')
  _shutDown() {
    this.appEvents.off('composer:insert-text');

    Ember.keys(this.get('toolbar.shortcuts')).forEach(sc => {
      Mousetrap(this.$('.d-editor-input')[0]).unbind(sc);
    });
    this.$('.d-editor-preview').off('click.preview');
  },

  @computed
  toolbar() {
    const toolbar = new Toolbar();
    _createCallbacks.forEach(cb => cb(toolbar));
    this.sendAction('extraButtons', toolbar);
    return toolbar;
  },

  _updatePreview() {
    const value = this.get('value');
    const markdownOptions = this.get('markdownOptions') || {};
    markdownOptions.sanitize = true;

    this.set('preview', Discourse.Dialect.cook(value || "", markdownOptions));
    Ember.run.scheduleOnce('afterRender', () => {
      if (this._state !== "inDOM") { return; }
      const $preview = this.$('.d-editor-preview');
      if ($preview.length === 0) return;

      this.sendAction('previewUpdated', $preview);
    });
  },

  @observes('ready', 'value')
  _watchForChanges() {
    if (!this.get('ready')) { return; }
    Ember.run.debounce(this, this._updatePreview, 30);
  },

  _applyEmojiAutocomplete() {
    if (!this.siteSettings.enable_emoji) { return; }

    const container = this.container;
    const template = container.lookup('template:emoji-selector-autocomplete.raw');
    const self = this;

    this.$('.d-editor-input').autocomplete({
      template: template,
      key: ":",

      transformComplete(v) {
        if (v.code) {
          return `${v.code}:`;
        } else {
          showSelector({
            appendTo: self.$(),
            container,
            onSelect: title => self._addText(this._getSelected(), `${title}:`)
          });
          return "";
        }
      },

      dataSource(term) {
        return new Ember.RSVP.Promise(resolve => {
          const full = `:${term}`;
          term = term.toLowerCase();

          if (term === "") {
            return resolve(["smile", "smiley", "wink", "sunny", "blush"]);
          }

          if (Discourse.Emoji.translations[full]) {
            return resolve([Discourse.Emoji.translations[full]]);
          }

          const options = Discourse.Emoji.search(term, {maxResults: 5});

          return resolve(options);
        }).then(list => list.map(code => {
          return {code, src: Discourse.Emoji.urlFor(code)};
        })).then(list => {
          if (list.length) {
            list.push({ label: I18n.t("composer.more_emoji") });
          }
          return list;
        });
      }
    });
  },

  _getSelected() {
    if (!this.get('ready')) { return; }

    const textarea = this.$('textarea.d-editor-input')[0];
    const start = textarea.selectionStart;
    const end = textarea.selectionEnd;

    const value = textarea.value.substring(start, end);
    const pre = textarea.value.slice(0, start);
    const post = textarea.value.slice(end);

    return { start, end, value, pre, post };
  },

  _selectText(from, length) {
    Ember.run.scheduleOnce('afterRender', () => {
      const textarea = this.$('textarea.d-editor-input')[0];
      textarea.focus();
      textarea.selectionStart = from;
      textarea.selectionEnd = textarea.selectionStart + length;
    });
  },

  _applySurround(sel, head, tail, exampleKey) {
    const pre = sel.pre;
    const post = sel.post;

    const tlen = tail.length;
    if (sel.start === sel.end) {
      if (tlen === 0) { return; }

      const [hval, hlen] = getHead(head);
      const example = I18n.t(`composer.${exampleKey}`);
      this.set('value', `${pre}${hval}${example}${tail}${post}`);
      this._selectText(pre.length + hlen, example.length);
    } else {
      const lines = sel.value.split("\n");

      let [hval, hlen] = getHead(head);
      if (lines.length === 1 && pre.slice(-tlen) === tail && post.slice(0, hlen) === hval) {
        this.set('value', `${pre.slice(0, -hlen)}${sel.value}${post.slice(tlen)}`);
        this._selectText(sel.start - hlen, sel.value.length);
      } else {
        const contents = lines.map(l => {
          if (l.length === 0) { return l; }

          if (l.slice(0, hlen) === hval && tlen === 0 || l.slice(-tlen) === tail) {
            if (tlen === 0) {
              const result = l.slice(hlen);
              [hval, hlen] = getHead(head, hval);
              return result;
            } else if (l.slice(-tlen) === tail) {
              const result = l.slice(hlen, -tlen);
              [hval, hlen] = getHead(head, hval);
              return result;
            }
          }
          const result = `${hval}${l}${tail}`;
          [hval, hlen] = getHead(head, hval);
          return result;
        }).join("\n");

        this.set('value', `${pre}${contents}${post}`);
        if (lines.length === 1 && tlen > 0) {
          this._selectText(sel.start + hlen, contents.length - hlen - hlen);
        } else {
          this._selectText(sel.start, contents.length);
        }
      }
    }
  },

  _applyList(sel, head, exampleKey) {
    if (sel.value.indexOf("\n") !== -1) {
      this._applySurround(sel, head, '', exampleKey);
    } else {

      const [hval, hlen] = getHead(head);
      if (sel.start === sel.end) {
        sel.value = I18n.t(`composer.${exampleKey}`);
      }

      const trimmedPre = sel.pre.trim();
      const number = (sel.value.indexOf(hval) === 0) ? sel.value.slice(hlen) : `${hval}${sel.value}`;
      const preLines = trimmedPre.length ? `${trimmedPre}\n\n` : "";

      const trimmedPost = sel.post.trim();
      const post = trimmedPost.length ? `\n\n${trimmedPost}` : trimmedPost;

      this.set('value', `${preLines}${number}${post}`);
      this._selectText(preLines.length, number.length);
    }
  },

  _addText(sel, text) {
    const insert = `${sel.pre}${text}`;
    this.set('value', `${insert}${sel.post}`);
    this._selectText(insert.length, 0);
  },

  actions: {
    toolbarButton(button) {
      const selected = this._getSelected();
      const toolbarEvent = {
        selected,
        applySurround: (head, tail, exampleKey) => this._applySurround(selected, head, tail, exampleKey),
        applyList: (head, exampleKey) => this._applyList(selected, head, exampleKey),
        addText: text => this._addText(selected, text)
      };

      if (button.sendAction) {
        return this.sendAction(button.sendAction, toolbarEvent);
      } else {
        button.perform(toolbarEvent);
      }
    },

    showLinkModal() {
      this._lastSel = this._getSelected();
      this.set('insertLinkHidden', false);
    },

    insertLink() {
      const link = this.get('link');

      if (Ember.isEmpty(link)) { return; }
      const m = / "([^"]+)"/.exec(link);
      if (m && m.length === 2) {
        const description = m[1];
        const remaining = link.replace(m[0], '');
        this._addText(this._lastSel, `[${description}](${remaining})`);
      } else {
        const selectedValue = this._lastSel.value || link;
        this._addText(this._lastSel, `[${selectedValue}](${link})`);
      }

      this.set('link', '');
    },

    emoji() {
      showSelector({
        appendTo: this.$(),
        container: this.container,
        onSelect: title => this._addText(this._getSelected(), `:${title}:`)
      });
    }
  }

});
<|endoftext|>