<commit_before>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */
/**
 * @fileoverview
 * @author Francois-Xavier Aeberhard <fx@red-agent.com>
 */
YUI.add('wegas-mcq-tabview', function(Y) {
    "use strict";
    var CONTENTBOX = 'contentBox',
        Wegas = Y.Wegas,
        MCQTabView;
    /**
     * @name Y.Wegas.MCQTabView
     * @extends Y.Widget
     * @borrows Y.WidgetChild, Y.Wegas.Widget, Y.Wegas.Editable
     * @class Class to view and reply to questions and choices.
     * @constructor
     * @description  Display and allow to reply at questions and choice sent
     *  to the current player
     */
    MCQTabView = Y.Base.create("wegas-mcqtabview", Y.Widget, [Y.WidgetChild, Wegas.Widget, Wegas.Editable], {
        /** @lends Y.Wegas.MCQTabView# */
        // *** Lifecycle Methods *** //
        CONTENT_TEMPLATE: null,
        /**
         * @function
         * @private
         * @description Set variable with initials values.
         */
        initializer: function() {
            /**
             * datasource from Y.Wegas.Facade.Variable
             */
            this.dataSource = Wegas.Facade.Variable;
            this.tabView = new Y.TabView();
            /**
             * Reference to each used functions
             */
            this.handlers = {};
            this.isRemovingTabs = false;
        },
        /**
         * @function
         * @private
         * @description Render the TabView widget in the content box.
         */
        renderUI: function() {
            var cb = this.get(CONTENTBOX);
            this.tabView.render(cb);
            this.tabView.get("boundingBox").addClass("horizontal-tabview");
            cb.append("

");
        },
        /**
         * @function
         * @private
         * @description bind function to events.
         * When submit button is clicked, send the selected choice
         * When datasource is updated, do syncUI;
         */
        bindUI: function() {
            this.tabView.after("selectionChange", this.onTabSelected, this);
            this.handlers.response = this.dataSource.after("update", this.syncUI, this);
            /*
             // This code does not refresh the display when a question list is to be replaced by another list:
             this.handlers.response = this.dataSource.after("updatedInstance", function(e) {
             // Easy test to possibly avoid entering the loop:
             if (e.entity.get("@class") !== "QuestionInstance") return;
             var questions = this.get("variable.evaluated");
             // Check for null and undefined:
             if (questions == null) return;
             if (!Y.Lang.isArray(questions)) {
             questions = [questions];
             }
             for (var i=0; i<questions.length; i++) {
             var q = questions[i];
             if (q.getInstance().get("id") === e.entity.get("id")) {
             this.syncUI();
             return;
             }
             }
             }, this);
             */
        },
        updateTab: function(tab, question) {
            var questionInstance = question.getInstance(),
                choiceDescriptor,
                label, cbxType = question.get("cbx"),
                validatedCbx = (cbxType ? questionInstance.get('validated') : false),
                nbReplies = questionInstance.get("replies").length,
                highlightUnanswered = (this.get("highlightUnanswered") && (cbxType ? !validatedCbx : (nbReplies === 0)));

            if (cbxType) {
                if (validatedCbx)
                    label = ""; // Dummy status string
            } else if (nbReplies > 0) {
                if (question.get("allowMultipleReplies")) {
                    label = questionInstance.get("replies").length + "x";
                } else { // Find the last selected replies
                    choiceDescriptor = questionInstance.get("replies")[questionInstance.get("replies").length - 1 ].getChoiceDescriptor();
                    label = choiceDescriptor.get("title") || "";
                    label = (label.length >= 15) ? label.substr(0, 15) + "..." : label;
                }
            }

            if (Y.Lang.isNull(label)) {
                label = (!question.get("allowMultipleReplies")) ? Y.Wegas.I18n.t('mcq.unanswered') : Y.Wegas.I18n.t('mcq.notDone');
            }

            label = '

'
                + (question.get("title") || question.get("label") || "undefined") + "
"
                + '
' + label + "
"
                + '
';

            tab.set("label", label);
        },
        createTab: function(question) {
            var tab = new Y.Wegas.Tab();
            tab.loaded = false;
            tab.cQuestion = question;
            tab.set("content", "

");
            this.updateTab(tab, question);
            return tab;
        },
        updateTabs: function(questions) {
            var question, questionInstance, oldTab, newTab, tabs, toRemove, index, queue,
                oldIndex;
            tabs = this.tabView._items;
            toRemove = tabs.slice(0);

            if (!Y.Lang.isArray(questions)) {
                queue = [questions];
            } else {
                queue = questions;
            }

            index = 0;
            while (question = queue.shift()) {
                if (question instanceof Wegas.persistence.QuestionDescriptor) {
                    oldTab = Y.Array.find(tabs, function(item) {
                        return item.cQuestion.get("id") === question.get("id");
                    });
                    questionInstance = question.getInstance();

                    if (questionInstance.get("active")) {
                        if (oldTab) {
                            // Simple Update
                            this.updateTab(oldTab, question);
                            oldIndex = toRemove.indexOf(oldTab);
                            if (oldIndex >= 0) {
                                toRemove.splice(oldIndex, 1);
                            }
                        } else {
                            // Just activated
                            newTab = this.createTab(question);
                            this.tabView.add(newTab, index);
                        }
                        index++;
                    }
                } else if (question instanceof Wegas.persistence.ListDescriptor) {
                    queue = queue.concat(question.get("items"));
                }
            }

            /*
             * Remvoe tabs which are not to be no longer displayed
             */
            while (question = toRemove.shift()) {
                question.remove();
            }
        },
        /**
         * @function
         * @private
         * @description * Update the TabView with active
         * choice/questions and relatives reply.
         * Display a message if there is no message.
         */
        syncUI: function() {
            var questions = this.get("variable.evaluated"),
                selectedTab, lastSelection;

            this.updateTabs(questions);

            this.hideOverlay();

            if (this.tabView.isEmpty()) {
                this.get("contentBox").addClass("empty");
                this.tabView.add(new Y.Tab({
                    label: "",
                    content: "

" + Y.Wegas.I18n.t('mcq.empty') + ""
                }));
                this.tabView.selectChild(0);
            } else {
                this.get("contentBox").removeClass("empty");
                selectedTab = this.tabView.get('selection');
                lastSelection = (selectedTab) ? selectedTab.get('index') : 0;
                if (lastSelection >= this.tabView.size()) { // Can occur when questions list has changed during event
                    lastSelection = 0;
                }
                this.tabView.selectChild(lastSelection);
            }
        },
        /**
         * @function
         * @param e description
         * @private
         * @description Display selected question's description on current tab.
         */
        onTabSelected: function(e) {
            var widget;
            if (e.newVal && e.newVal.cQuestion
                && !this.isRemovingTabs && !e.newVal.loaded) {
                e.newVal.loaded = true;
                widget = new Y.Wegas.MCQView({
                    variable: {
                        "name": e.newVal.cQuestion.get("name")
                    }
                });
                e.newVal.add(widget);
            }
        },
        getEditorLabel: function() {
            var variable = this.get("variable.evaluated");
            if (variable && variable.getEditorLabel) {
                return variable.getEditorLabel();
            }
            return Wegas.MCQTabView.EDITORNAME;
        },
        /**
         * @function
         * @private
         * @description Destroy TabView and detach all functions created
         *  by this widget
         */
        destructor: function() {
            var i;
            this.tabView.destroy();
            for (i in this.handlers) {
                this.handlers[i].detach();
            }
        }
    }, {
        EDITORNAME: "Question display",
        /** @lends Y.Wegas.MCQTabView */
        /**
         * @field
         * @static
         * @description
         *
Attributes

         *

         *
variable: The target variable, returned either based on the name
         *     attribute, and if absent by evaluating the expr attribute.

         *

         */
        ATTRS: {
            variable: {
                /**
                 * The target variable, returned either based on the name attribute,
                 * and if absent by evaluating the expr attribute.
                 */
                getter: Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                _inputex: {
                    _type: "variableselect",
                    label: "Question folder",
                    classFilter: ["ListDescriptor"]
                }
            },
            highlightUnanswered: {
                type: "boolean",
                value: true,
                _inputex: {
                    label: "Higlight Unanswered",
                    wrapperClassName: "inputEx-fieldWrapper wegas-advanced-feature"
                }
            }
        }
    });
    Wegas.MCQTabView = MCQTabView;
});
<commit_msg>MCQ Tabview hotfix<commit_after>/*
 * Wegas
 * http://wegas.albasim.ch
 *
 * Copyright (c) 2013, 2014, 2015 School of Business and Engineering Vaud, Comem
 * Licensed under the MIT License
 */
/**
 * @fileoverview
 * @author Francois-Xavier Aeberhard <fx@red-agent.com>
 */
YUI.add('wegas-mcq-tabview', function(Y) {
    "use strict";
    var CONTENTBOX = 'contentBox',
        Wegas = Y.Wegas,
        MCQTabView;
    /**
     * @name Y.Wegas.MCQTabView
     * @extends Y.Widget
     * @borrows Y.WidgetChild, Y.Wegas.Widget, Y.Wegas.Editable
     * @class Class to view and reply to questions and choices.
     * @constructor
     * @description  Display and allow to reply at questions and choice sent
     *  to the current player
     */
    MCQTabView = Y.Base.create("wegas-mcqtabview", Y.Widget, [Y.WidgetChild, Wegas.Widget, Wegas.Editable], {
        /** @lends Y.Wegas.MCQTabView# */
        // *** Lifecycle Methods *** //
        CONTENT_TEMPLATE: null,
        /**
         * @function
         * @private
         * @description Set variable with initials values.
         */
        initializer: function() {
            /**
             * datasource from Y.Wegas.Facade.Variable
             */
            this.dataSource = Wegas.Facade.Variable;
            this.tabView = new Y.TabView();
            /**
             * Reference to each used functions
             */
            this.handlers = {};
            this.isRemovingTabs = false;
        },
        /**
         * @function
         * @private
         * @description Render the TabView widget in the content box.
         */
        renderUI: function() {
            var cb = this.get(CONTENTBOX);
            this.tabView.render(cb);
            this.tabView.get("boundingBox").addClass("horizontal-tabview");
            cb.append("

");
        },
        /**
         * @function
         * @private
         * @description bind function to events.
         * When submit button is clicked, send the selected choice
         * When datasource is updated, do syncUI;
         */
        bindUI: function() {
            this.tabView.after("selectionChange", this.onTabSelected, this);
            this.handlers.response = this.dataSource.after("update", this.syncUI, this);
            /*
             // This code does not refresh the display when a question list is to be replaced by another list:
             this.handlers.response = this.dataSource.after("updatedInstance", function(e) {
             // Easy test to possibly avoid entering the loop:
             if (e.entity.get("@class") !== "QuestionInstance") return;
             var questions = this.get("variable.evaluated");
             // Check for null and undefined:
             if (questions == null) return;
             if (!Y.Lang.isArray(questions)) {
             questions = [questions];
             }
             for (var i=0; i<questions.length; i++) {
             var q = questions[i];
             if (q.getInstance().get("id") === e.entity.get("id")) {
             this.syncUI();
             return;
             }
             }
             }, this);
             */
        },
        updateTab: function(tab, question) {
            var questionInstance = question.getInstance(),
                choiceDescriptor,
                label, cbxType = question.get("cbx"),
                validatedCbx = (cbxType ? questionInstance.get('validated') : false),
                nbReplies = questionInstance.get("replies").length,
                highlightUnanswered = (this.get("highlightUnanswered") && (cbxType ? !validatedCbx : (nbReplies === 0)));

            if (cbxType) {
                if (validatedCbx)
                    label = ""; // Dummy status string
            } else if (nbReplies > 0) {
                if (question.get("allowMultipleReplies")) {
                    label = questionInstance.get("replies").length + "x";
                } else { // Find the last selected replies
                    choiceDescriptor = questionInstance.get("replies")[questionInstance.get("replies").length - 1 ].getChoiceDescriptor();
                    label = choiceDescriptor.get("title") || "";
                    label = (label.length >= 15) ? label.substr(0, 15) + "..." : label;
                }
            }

            if (Y.Lang.isNull(label)) {
                label = (!question.get("allowMultipleReplies")) ? Y.Wegas.I18n.t('mcq.unanswered') : Y.Wegas.I18n.t('mcq.notDone');
            }

            label = '

'
                + (question.get("title") || question.get("label") || "undefined") + "
"
                + '
' + label + "
"
                + '
';

            tab.set("label", label);
        },
        createTab: function(question) {
            var tab = new Y.Wegas.Tab();
            tab.loaded = false;
            tab.cQuestion = question;
            tab.set("content", "

");
            this.updateTab(tab, question);
            return tab;
        },
        updateTabs: function(questions) {
            var question, questionInstance, oldTab, newTab, tabs, toRemove, index, queue,
                oldIndex;
            tabs = this.tabView._items;
            toRemove = tabs.slice(0);

            if (!Y.Lang.isArray(questions)) {
                queue = [questions];
            } else {
                queue = questions;
            }

            index = 0;
            while (question = queue.shift()) {
                if (question instanceof Wegas.persistence.QuestionDescriptor) {
                    oldTab = Y.Array.find(tabs, function(item) {
                        return item.cQuestion && item.cQuestion.get("id") === question.get("id");
                    });
                    questionInstance = question.getInstance();

                    if (questionInstance.get("active")) {
                        if (oldTab) {
                            // Simple Update
                            this.updateTab(oldTab, question);
                            oldIndex = toRemove.indexOf(oldTab);
                            if (oldIndex >= 0) {
                                toRemove.splice(oldIndex, 1);
                            }
                        } else {
                            // Just activated
                            newTab = this.createTab(question);
                            this.tabView.add(newTab, index);
                        }
                        index++;
                    }
                } else if (question instanceof Wegas.persistence.ListDescriptor) {
                    queue = queue.concat(question.get("items"));
                }
            }

            /*
             * Remvoe tabs which are not to be no longer displayed
             */
            while (question = toRemove.shift()) {
                question.remove();
            }
        },
        /**
         * @function
         * @private
         * @description * Update the TabView with active
         * choice/questions and relatives reply.
         * Display a message if there is no message.
         */
        syncUI: function() {
            var questions = this.get("variable.evaluated"),
                selectedTab, lastSelection;

            this.updateTabs(questions);

            this.hideOverlay();

            if (this.tabView.isEmpty()) {
                this.get("contentBox").addClass("empty");
                this.tabView.add(new Y.Tab({
                    label: "",
                    content: "

" + Y.Wegas.I18n.t('mcq.empty') + ""
                }));
                this.tabView.selectChild(0);
            } else {
                this.get("contentBox").removeClass("empty");
                selectedTab = this.tabView.get('selection');
                lastSelection = (selectedTab) ? selectedTab.get('index') : 0;
                if (lastSelection >= this.tabView.size()) { // Can occur when questions list has changed during event
                    lastSelection = 0;
                }
                this.tabView.selectChild(lastSelection);
            }
        },
        /**
         * @function
         * @param e description
         * @private
         * @description Display selected question's description on current tab.
         */
        onTabSelected: function(e) {
            var widget;
            if (e.newVal && e.newVal.cQuestion
                && !this.isRemovingTabs && !e.newVal.loaded) {
                e.newVal.loaded = true;
                widget = new Y.Wegas.MCQView({
                    variable: {
                        "name": e.newVal.cQuestion.get("name")
                    }
                });
                e.newVal.add(widget);
            }
        },
        getEditorLabel: function() {
            var variable = this.get("variable.evaluated");
            if (variable && variable.getEditorLabel) {
                return variable.getEditorLabel();
            }
            return Wegas.MCQTabView.EDITORNAME;
        },
        /**
         * @function
         * @private
         * @description Destroy TabView and detach all functions created
         *  by this widget
         */
        destructor: function() {
            var i;
            this.tabView.destroy();
            for (i in this.handlers) {
                this.handlers[i].detach();
            }
        }
    }, {
        EDITORNAME: "Question display",
        /** @lends Y.Wegas.MCQTabView */
        /**
         * @field
         * @static
         * @description
         *
Attributes

         *

         *
variable: The target variable, returned either based on the name
         *     attribute, and if absent by evaluating the expr attribute.

         *

         */
        ATTRS: {
            variable: {
                /**
                 * The target variable, returned either based on the name attribute,
                 * and if absent by evaluating the expr attribute.
                 */
                getter: Wegas.Widget.VARIABLEDESCRIPTORGETTER,
                _inputex: {
                    _type: "variableselect",
                    label: "Question folder",
                    classFilter: ["ListDescriptor"]
                }
            },
            highlightUnanswered: {
                type: "boolean",
                value: true,
                _inputex: {
                    label: "Higlight Unanswered",
                    wrapperClassName: "inputEx-fieldWrapper wegas-advanced-feature"
                }
            }
        }
    });
    Wegas.MCQTabView = MCQTabView;
});
<|endoftext|>


<commit_before>"use strict";

var test = require('tape');
var net = require('net');
var randomstring = require('randomstring');
var ThaliEmitter = require('thali/thaliemitter');

function newPeerIdentifier() {
  return (+ new Date()).toString() + "." + process.pid;
}

test('ThaliEmitter can call repeatedly startBroadcasting and endBroadcasting without error',
function (t) {

  var TRIES = 10;

  var network_changed = 0;
  var e = new ThaliEmitter();

  e.on(ThaliEmitter.events.NETWORK_CHANGED, function(status) {
    t.ok(status.isAvailable == true, "Network status should be available");
    if (++network_changed == TRIES) {
      t.end();
    }
  });

  for (var i = 0; i < TRIES; i++) {
    e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
      t.notOk(err1, 'Should be able to call startBroadcasting without error');
      e.stopBroadcasting(function (err2) {
        t.notOk(err2, 'Should be able to call stopBroadcasting without error');
      });
    });
  }
});

test('ThaliEmitter calls startBroadcasting twice with error', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.startBroadcasting(newPeerIdentifier(), 5001, function (err2) {

      t.assert(!!err2, 'Cannot call startBroadcasting twice');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

test('ThaliEmitter throws on connection to bad peer', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.connect('foobar', function (err2, port) {
      t.assert(!!err2, 'Should not connect to a bad peer');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

test('ThaliEmitter throws on disconnect to bad peer', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.disconnect('foobar', function (err2, port) {
      t.assert(!!err2, 'Disconnect should fail to a non-existant peer ');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

function connectWithRetryTestAndDisconnect(t, testFunction) {
  var e = new ThaliEmitter();

  var _done = false;

  e.on(ThaliEmitter.events.PEER_AVAILABILITY_CHANGED, function (peers) {

    peers.forEach(function (peer) {

      if (peer.peerAvailable) {
        var connectToPeer = function(attempts) {

          if (!_done) {
            if (attempts === 0) {
              t.fail('Connecting failed');
              return t.end();
            }

            e.connect(peer.peerIdentifier, function (err2, port) {

              if (err2) {
                if (err2.message.indexOf("unreachable") != -1) {
                  // Peer has become unreachable no point retrying
                  return;
                } else {
                  // Retry
                  return setTimeout(function () { connectToPeer(attempts - 1); }, 1000);
                }
              }

              t.notOk(err2, 'Should be able to connect without error');
              t.ok(port > 0 && port <= 65536, 'Port should be within range');

              testFunction(t, e, peer, port, function() {
                e.stopBroadcasting(function (err4) {
                  t.notOk(err4, 'Should be able to call stopBroadcasting without error');
                  _done = true;
                  t.end();
                });
              });
            });
          };
        }

        connectToPeer(10);
      }
    });
  });

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');
  });
}

test('ThaliEmitter can discover and connect to peers', function (t) {
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    e.disconnect(peer.peerIdentifier, function (err3) {
      t.notOk(err3, 'Should be able to disconnect without error');
      cb();
    });
  });
});

test('ThaliEmitter can discover and connect to peers and then fail on double connect', function (t) {
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    e.connect(peer.peerIdentifier, function(err3, port) {
      t.ok(err3, 'Should fail on double connect');
      e.disconnect(peer.peerIdentifier, function (err3) {
        t.notOk(err3, 'Should be able to disconnect without error');
        cb();
      });
    });
  });
});

test('ThaliEmitter can discover and connect to peers and then fail on double disconnect', function (t) {
  connectWithRetryTestAndDisconnect(t, function (t, e, peer, port, cb) {
    e.disconnect(peer.peerIdentifier, function (err3) {
      t.notOk(err3, 'Should be able to disconnect without error');

      e.disconnect(peer.peerIdentifier, function (err4) {
        t.ok(err4, 'Disconnect should fail ');
        cb();
      });
    });
  });
});

test('ThaliEmitter can connect and send data', function (t) {
  var len = 1025;
  var testMessage = randomstring.generate(len);
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    var clientSocket = net.createConnection( { port: port }, function () {
      clientSocket.write(testMessage);
    });

    clientSocket.setTimeout(120000);
    clientSocket.setKeepAlive(true);

    var testData = '';

    clientSocket.on('data', function (data) {
      testData += data;

      if (testData.length === len) {
        t.equal(testData, testMessage, 'the test messages should be equal');

        e.disconnect(peer.peerIdentifier, function (err3) {
          t.notOk(err3, 'Should be able to disconnect without error');
          cb();
        });
      }
    });
  });
});
<commit_msg>Fixed test to work as advertised<commit_after>"use strict";

var test = require('tape');
var net = require('net');
var randomstring = require('randomstring');
var ThaliEmitter = require('thali/thaliemitter');

function newPeerIdentifier() {
  return (+ new Date()).toString() + "." + process.pid;
}

test('ThaliEmitter can call repeatedly startBroadcasting and stopBroadcasting without error', function (t) {
  var e = new ThaliEmitter();

  function repeatCalls(count) {
    if (count == 0) {
      t.end();
      return;
    }

    e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
      t.notOk(err1, 'Should be able to call startBroadcasting without error');
      e.stopBroadcasting(function (err2) {
        t.notOk(err2, 'Should be able to call stopBroadcasting without error');
        repeatCalls(count - 1);
      });
    });
  }

  repeatCalls(10);
});

test('ThaliEmitter calls startBroadcasting twice with error', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.startBroadcasting(newPeerIdentifier(), 5001, function (err2) {

      t.assert(!!err2, 'Cannot call startBroadcasting twice');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

test('ThaliEmitter throws on connection to bad peer', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.connect('foobar', function (err2, port) {
      t.assert(!!err2, 'Should not connect to a bad peer');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

test('ThaliEmitter throws on disconnect to bad peer', function (t) {
  var e = new ThaliEmitter();

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');

    e.disconnect('foobar', function (err2, port) {
      t.assert(!!err2, 'Disconnect should fail to a non-existant peer ');

      e.stopBroadcasting(function (err3) {
        t.notOk(err3, 'Should be able to call stopBroadcasting without error');
        t.end();
      });
    });
  });
});

function connectWithRetryTestAndDisconnect(t, testFunction) {
  var e = new ThaliEmitter();

  var _done = false;

  e.on(ThaliEmitter.events.PEER_AVAILABILITY_CHANGED, function (peers) {

    peers.forEach(function (peer) {

      if (peer.peerAvailable) {
        var connectToPeer = function(attempts) {

          if (!_done) {
            if (attempts === 0) {
              t.fail('Connecting failed');
              return t.end();
            }

            e.connect(peer.peerIdentifier, function (err2, port) {

              if (err2) {
                if (err2.message.indexOf("unreachable") != -1) {
                  // Peer has become unreachable no point retrying
                  return;
                } else {
                  // Retry
                  return setTimeout(function () { connectToPeer(attempts - 1); }, 1000);
                }
              }

              t.notOk(err2, 'Should be able to connect without error');
              t.ok(port > 0 && port <= 65536, 'Port should be within range');

              testFunction(t, e, peer, port, function() {
                e.stopBroadcasting(function (err4) {
                  t.notOk(err4, 'Should be able to call stopBroadcasting without error');
                  _done = true;
                  t.end();
                });
              });
            });
          };
        }

        connectToPeer(10);
      }
    });
  });

  e.startBroadcasting(newPeerIdentifier(), 5001, function (err1) {
    t.notOk(err1, 'Should be able to call startBroadcasting without error');
  });
}

test('ThaliEmitter can discover and connect to peers', function (t) {
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    e.disconnect(peer.peerIdentifier, function (err3) {
      t.notOk(err3, 'Should be able to disconnect without error');
      cb();
    });
  });
});

test('ThaliEmitter can discover and connect to peers and then fail on double connect', function (t) {
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    e.connect(peer.peerIdentifier, function(err3, port) {
      t.ok(err3, 'Should fail on double connect');
      e.disconnect(peer.peerIdentifier, function (err3) {
        t.notOk(err3, 'Should be able to disconnect without error');
        cb();
      });
    });
  });
});

test('ThaliEmitter can discover and connect to peers and then fail on double disconnect', function (t) {
  connectWithRetryTestAndDisconnect(t, function (t, e, peer, port, cb) {
    e.disconnect(peer.peerIdentifier, function (err3) {
      t.notOk(err3, 'Should be able to disconnect without error');

      e.disconnect(peer.peerIdentifier, function (err4) {
        t.ok(err4, 'Disconnect should fail ');
        cb();
      });
    });
  });
});

test('ThaliEmitter can connect and send data', function (t) {
  var len = 1025;
  var testMessage = randomstring.generate(len);
  connectWithRetryTestAndDisconnect(t, function(t, e, peer, port, cb) {
    var clientSocket = net.createConnection( { port: port }, function () {
      clientSocket.write(testMessage);
    });

    clientSocket.setTimeout(120000);
    clientSocket.setKeepAlive(true);

    var testData = '';

    clientSocket.on('data', function (data) {
      testData += data;

      if (testData.length === len) {
        t.equal(testData, testMessage, 'the test messages should be equal');

        e.disconnect(peer.peerIdentifier, function (err3) {
          t.notOk(err3, 'Should be able to disconnect without error');
          cb();
        });
      }
    });
  });
});
<|endoftext|>


<commit_before>function _App () {
  var self = this
  // Configure bower_components path
  requirejs.config({
    paths: {
      'localstorage': '../bower_components/local-storage-api/dist/storage.min',
      'jquery': '../bower_components/jquery/dist/jquery.min',
      'jquery-ui': '../bower_components/jquery-ui/jquery-ui.min',
      'vue': '../bower_components/vue/dist/vue.min',
      'jquery.ui.touch-punch': '../bower_components/jquery-ui-touch-punch-improved/jquery.ui.touch-punch-improved',
      'notify-me': '../bower_components/notify.me/dist/js/notify-me',
      'popup': '../bower_components/popupjs/dist/popup.min'
    }
  })

  // load modules
  requirejs([
    'vue',
    'localstorage',
    'jquery',
    'jquery-ui',
    'format'
  ], function (v, ls, jq, jqui, f) {
    self.Vue = v
    self.Storage = new Storage()

    $.indexOfO = function(array, compFunc){
      for(var key in array){
        if(compFunc(array[key])){
          return key
        }
      }
      return -1
    }

    requirejs([
      'jquery.ui.touch-punch',
      'notify-me',
      'popup'
    ], function (jquit, notif, pop) {
      requirejs([
        'loading',
        'top-menu',
        'config',
        'list'
      ], function (load, tm, conf, l) {
        requirejs([
          'left-menu',
          'directory'
        ], function(lm, dir){
          requirejs([
            'left-menu'
          ], function(){
            // Update Directory and Hash
            self.hash = document.location.hash.substring(1)
            if(self.hash[self.hash.length -1] !== '/'){
              self.hash += '/'
            }
            self.TopMenu.setAriane(self.getDirFromHash())
            self.Directory.getDir(function(dir){
              self.Directory.append(dir)
            })
            $(window).bind('hashchange', function () {
              self.hash = document.location.hash.substring(1)
              if(self.hash[self.hash.length -1] !== '/'){
                self.hash += '/'
              }

              self.TopMenu.setAriane(self.getDirFromHash())
              App.TopMenu.setActions({
                download: 'hide',
                rename: 'hide',
                remove: 'hide',
                info: 'hide'
              })
              self.Directory.getDir(function(dir){
                self.Directory.append(dir)
              })
            })

            // Trigger keydown event
            $(window).keydown(function (event) {
              switch (event.keyCode) {
                case 13:
                  // trigger start / search torrent
                break
              }
            })

            // Everithing is loaded
            self.Loading.hide()
          })
        })
      })
    })
  })
}

_App.prototype.getDirFromHash = function () {
  var dir = this.hash.split('/') || ''
  for (var i in dir) {
    if (dir[i] === '') {
      dir.splice(i, 1)
    }
  }
  return dir
}

// Global var with all the modules
var App = new _App()
<commit_msg>reformat hash only if necessary<commit_after>function _App () {
  var self = this
  // Configure bower_components path
  requirejs.config({
    paths: {
      'localstorage': '../bower_components/local-storage-api/dist/storage.min',
      'jquery': '../bower_components/jquery/dist/jquery.min',
      'jquery-ui': '../bower_components/jquery-ui/jquery-ui.min',
      'vue': '../bower_components/vue/dist/vue.min',
      'jquery.ui.touch-punch': '../bower_components/jquery-ui-touch-punch-improved/jquery.ui.touch-punch-improved',
      'notify-me': '../bower_components/notify.me/dist/js/notify-me',
      'popup': '../bower_components/popupjs/dist/popup.min'
    }
  })

  // load modules
  requirejs([
    'vue',
    'localstorage',
    'jquery',
    'jquery-ui',
    'format'
  ], function (v, ls, jq, jqui, f) {
    self.Vue = v
    self.Storage = new Storage()

    $.indexOfO = function(array, compFunc){
      for(var key in array){
        if(compFunc(array[key])){
          return key
        }
      }
      return -1
    }

    requirejs([
      'jquery.ui.touch-punch',
      'notify-me',
      'popup'
    ], function (jquit, notif, pop) {
      requirejs([
        'loading',
        'top-menu',
        'config',
        'list'
      ], function (load, tm, conf, l) {
        requirejs([
          'left-menu',
          'directory'
        ], function(lm, dir){
          requirejs([
            'left-menu'
          ], function(){
            // Update Directory and Hash
            self.hash = document.location.hash.substring(1)
            if(self.hash[self.hash.length -1] !== '/' && self.hash.length > 0){
              self.hash += '/'
            }
            self.TopMenu.setAriane(self.getDirFromHash())
            self.Directory.getDir(function(dir){
              self.Directory.append(dir)
            })
            $(window).bind('hashchange', function () {
              self.hash = document.location.hash.substring(1)
              if(self.hash[self.hash.length -1] !== '/' && self.hash.length > 0){
                self.hash += '/'
              }

              self.TopMenu.setAriane(self.getDirFromHash())
              App.TopMenu.setActions({
                download: 'hide',
                rename: 'hide',
                remove: 'hide',
                info: 'hide'
              })
              self.Directory.getDir(function(dir){
                self.Directory.append(dir)
              })
            })

            // Trigger keydown event
            $(window).keydown(function (event) {
              switch (event.keyCode) {
                case 13:
                  // trigger start / search torrent
                break
              }
            })

            // Everithing is loaded
            self.Loading.hide()
          })
        })
      })
    })
  })
}

_App.prototype.getDirFromHash = function () {
  var dir = this.hash.split('/') || ''
  for (var i in dir) {
    if (dir[i] === '') {
      dir.splice(i, 1)
    }
  }
  return dir
}

// Global var with all the modules
var App = new _App()
<|endoftext|>


<commit_before>//This is the "Offline copy of pages" wervice worker

//Install stage sets up the index page (home page) in the cahche and opens a new cache
self.addEventListener('install', function(event) {
  var indexPage = new Request('index.html');
  event.waitUntil(
    fetch(indexPage).then(function(response) {
      return caches.open('pwabuilder-offline').then(function(cache) {
        console.log('[PWA Builder] Cached index page during Install'+ response.url);
        return cache.put(indexPage, response);
      });
  }));
});

//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
  var updateCache = function(request){
    return caches.open('pwabuilder-offline').then(function (cache) {
      return fetch(request).then(function (response) {
        console.log('[PWA Builder] add page to offline'+response.url)
        try {
			return cache.put(request, response);
		} catch (e) {
			alert(e);
			console.log(request);
			console.log(response);
		}
      });
    });
  };

  event.waitUntil(updateCache(event.request));

  event.respondWith(
    fetch(event.request).catch(function(error) {
      console.log( '[PWA Builder] Network request Failed. Serving content from cache: ' + error );

      //Check to see if you have it in the cache
      //Return response
      //If not in the cache, then return error page
      return caches.open('pwabuilder-offline').then(function (cache) {
        return cache.match(event.request).then(function (matching) {
          var report =  !matching || matching.status == 404?Promise.reject('no-match'): matching;
          return report
        });
      });
    })
  );
})
<commit_msg>Fix Javascript errors in ServiceWorker<commit_after>//This is the "Offline copy of pages" service worker

//Install stage sets up the index page (home page) in the cahche and opens a new cache
self.addEventListener('install', function(event) {
  var indexPage = new Request('index.html');
  event.waitUntil(
    fetch(indexPage).then(function(response) {
      return caches.open('pwabuilder-offline').then(function(cache) {
        console.log('[PWA Builder] Cached index page during Install '+ response.url);
        return cache.put(indexPage, response);
      });
  }));
});

//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
  var updateCache = function(request){
    return caches.open('pwabuilder-offline').then(function (cache) {
      return fetch(request).then(function (response) {
        console.log('[PWA] add page to offline '+response.url);
		if (request.method=="GET") {
			return cache.put(request, response);
		};
      });
    });
  };

  event.waitUntil(updateCache(event.request));

  event.respondWith(
    fetch(event.request).catch(function(error) {
      console.log( '[PWA] Network request Failed. Serving content from cache: ' + error );

      //Check to see if you have it in the cache
      //Return response
      //If not in the cache, then return error page
      return caches.open('pwabuilder-offline').then(function (cache) {
        return cache.match(event.request).then(function (matching) {
          var report =  !matching || matching.status == 404?Promise.reject('no-match'): matching;
          return report
        });
      });
    })
  );
})
<|endoftext|>


<commit_before>import {
  attempt,
  cond,
  flow,
  identity,
  isError,
  matchesProperty,
  overSome,
  partial,
  stubTrue
} from 'lodash';

import protect from '@freecodecamp/loop-protect';

import * as vinyl from '../utils/polyvinyl.js';
import createWorker from '../utils/worker-executor';

// the config files are created during the build, but not before linting
// eslint-disable-next-line import/no-unresolved
import { filename as sassCompile } from '../../../../config/sass-compile';

const protectTimeout = 100;
const testProtectTimeout = 1500;
const loopsPerTimeoutCheck = 2000;

function loopProtectCB(line) {
  console.log(
    `Potential infinite loop detected on line ${line}. Tests may fail if this is not changed.`
  );
}

function testLoopProtectCB(line) {
  console.log(
    `Potential infinite loop detected on line ${line}. Tests may be failing because of this.`
  );
}

// hold Babel, presets and options so we don't try to import them multiple times

let Babel;
let presetEnv, presetReact;
let babelOptionsJSBase, babelOptionsJS, babelOptionsJSX, babelOptionsJSPreview;

async function loadBabel() {
  if (Babel) return;
  /* eslint-disable no-inline-comments */
  Babel = await import(
    /* webpackChunkName: "@babel/standalone" */ '@babel/standalone'
  );
  /* eslint-enable no-inline-comments */
  Babel.registerPlugin(
    'loopProtection',
    protect(protectTimeout, loopProtectCB)
  );
  Babel.registerPlugin(
    'testLoopProtection',
    protect(testProtectTimeout, testLoopProtectCB, loopsPerTimeoutCheck)
  );
}

async function loadPresetEnv() {
  if (presetEnv) return;
  /* eslint-disable no-inline-comments */
  presetEnv = await import(
    /* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
  );
  /* eslint-enable no-inline-comments */

  babelOptionsJSBase = {
    presets: [presetEnv]
  };
  babelOptionsJS = {
    ...babelOptionsJSBase,
    plugins: ['testLoopProtection']
  };
  babelOptionsJSPreview = {
    ...babelOptionsJSBase,
    plugins: ['loopProtection']
  };
}

async function loadPresetReact() {
  if (presetReact) return;
  /* eslint-disable no-inline-comments */
  presetReact = await import(
    /* webpackChunkName: "@babel/preset-react" */ '@babel/preset-react'
  );
  if (!presetEnv)
    presetEnv = await import(
      /* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
    );
  /* eslint-enable no-inline-comments */
  babelOptionsJSX = {
    plugins: ['loopProtection'],
    presets: [presetEnv, presetReact]
  };
}

const babelTransformCode = options => code =>
  Babel.transform(code, options).code;

// const sourceReg =
//  /(<!-- fcc-start-source -->)([\s\S]*?)(?=<!-- fcc-end-source -->)/g;
const NBSPReg = new RegExp(String.fromCharCode(160), 'g');

const testJS = matchesProperty('ext', 'js');
const testJSX = matchesProperty('ext', 'jsx');
const testHTML = matchesProperty('ext', 'html');
const testHTML$JS$JSX = overSome(testHTML, testJS, testJSX);
export const testJS$JSX = overSome(testJS, testJSX);

export const replaceNBSP = cond([
  [
    testHTML$JS$JSX,
    partial(vinyl.transformContents, contents => contents.replace(NBSPReg, ' '))
  ],
  [stubTrue, identity]
]);

function tryTransform(wrap = identity) {
  return function transformWrappedPoly(source) {
    const result = attempt(wrap, source);
    if (isError(result)) {
      // note(Bouncey): Error thrown here to collapse the build pipeline
      // At the minute, it will not bubble up
      // We collapse the pipeline so the app doesn't fall over trying
      // parse bad code (syntax/type errors etc...)
      throw result;
    }
    return result;
  };
}

const babelTransformer = ({ preview = false, protect = true }) => {
  let options = babelOptionsJSBase;
  // we always protect the preview, since it evaluates as the user types and
  // they may briefly have infinite looping code accidentally
  if (protect) {
    options = preview ? babelOptionsJSPreview : babelOptionsJS;
  } else {
    options = preview ? babelOptionsJSPreview : options;
  }
  return cond([
    [
      testJS,
      async code => {
        await loadBabel();
        await loadPresetEnv();
        return partial(
          vinyl.transformHeadTailAndContents,
          tryTransform(babelTransformCode(options))
        )(code);
      }
    ],
    [
      testJSX,
      async code => {
        await loadBabel();
        await loadPresetReact();
        return flow(
          partial(
            vinyl.transformHeadTailAndContents,
            tryTransform(babelTransformCode(babelOptionsJSX))
          ),
          partial(vinyl.setExt, 'js')
        )(code);
      }
    ],
    [stubTrue, identity]
  ]);
};

const sassWorker = createWorker(sassCompile);
async function transformSASS(element) {
  const styleTags = element.querySelectorAll('style[type="text/sass"]');
  await Promise.all(
    [].map.call(styleTags, async style => {
      style.type = 'text/css';
      style.innerHTML = await sassWorker.execute(style.innerHTML, 5000).done;
    })
  );
}

async function transformScript(element) {
  await loadBabel();
  await loadPresetReact();
  const scriptTags = element.querySelectorAll('script');
  scriptTags.forEach(script => {
    script.innerHTML = tryTransform(babelTransformCode(babelOptionsJSX))(
      script.innerHTML
    );
  });
}

const transformHtml = async function(file) {
  const div = document.createElement('div');
  div.innerHTML = file.contents;
  await Promise.all([transformSASS(div), transformScript(div)]);
  return vinyl.transformContents(() => div.innerHTML, file);
};

export const composeHTML = cond([
  [
    testHTML,
    flow(
      partial(vinyl.transformHeadTailAndContents, source => {
        const div = document.createElement('div');
        div.innerHTML = source;
        return div.innerHTML;
      }),
      partial(vinyl.compileHeadTail, '')
    )
  ],
  [stubTrue, identity]
]);

export const htmlTransformer = cond([
  [testHTML, transformHtml],
  [stubTrue, identity]
]);

export const getTransformers = config => [
  replaceNBSP,
  babelTransformer(config ? config : {}),
  composeHTML,
  htmlTransformer
];
<commit_msg>fix: assume scripts inside HTML are js, not jsx<commit_after>import {
  attempt,
  cond,
  flow,
  identity,
  isError,
  matchesProperty,
  overSome,
  partial,
  stubTrue
} from 'lodash';

import protect from '@freecodecamp/loop-protect';

import * as vinyl from '../utils/polyvinyl.js';
import createWorker from '../utils/worker-executor';

// the config files are created during the build, but not before linting
// eslint-disable-next-line import/no-unresolved
import { filename as sassCompile } from '../../../../config/sass-compile';

const protectTimeout = 100;
const testProtectTimeout = 1500;
const loopsPerTimeoutCheck = 2000;

function loopProtectCB(line) {
  console.log(
    `Potential infinite loop detected on line ${line}. Tests may fail if this is not changed.`
  );
}

function testLoopProtectCB(line) {
  console.log(
    `Potential infinite loop detected on line ${line}. Tests may be failing because of this.`
  );
}

// hold Babel, presets and options so we don't try to import them multiple times

let Babel;
let presetEnv, presetReact;
let babelOptionsJSBase, babelOptionsJS, babelOptionsJSX, babelOptionsJSPreview;

async function loadBabel() {
  if (Babel) return;
  /* eslint-disable no-inline-comments */
  Babel = await import(
    /* webpackChunkName: "@babel/standalone" */ '@babel/standalone'
  );
  /* eslint-enable no-inline-comments */
  Babel.registerPlugin(
    'loopProtection',
    protect(protectTimeout, loopProtectCB)
  );
  Babel.registerPlugin(
    'testLoopProtection',
    protect(testProtectTimeout, testLoopProtectCB, loopsPerTimeoutCheck)
  );
}

async function loadPresetEnv() {
  if (presetEnv) return;
  /* eslint-disable no-inline-comments */
  presetEnv = await import(
    /* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
  );
  /* eslint-enable no-inline-comments */

  babelOptionsJSBase = {
    presets: [presetEnv]
  };
  babelOptionsJS = {
    ...babelOptionsJSBase,
    plugins: ['testLoopProtection']
  };
  babelOptionsJSPreview = {
    ...babelOptionsJSBase,
    plugins: ['loopProtection']
  };
}

async function loadPresetReact() {
  if (presetReact) return;
  /* eslint-disable no-inline-comments */
  presetReact = await import(
    /* webpackChunkName: "@babel/preset-react" */ '@babel/preset-react'
  );
  if (!presetEnv)
    presetEnv = await import(
      /* webpackChunkName: "@babel/preset-env" */ '@babel/preset-env'
    );
  /* eslint-enable no-inline-comments */
  babelOptionsJSX = {
    plugins: ['loopProtection'],
    presets: [presetEnv, presetReact]
  };
}

const babelTransformCode = options => code =>
  Babel.transform(code, options).code;

// const sourceReg =
//  /(<!-- fcc-start-source -->)([\s\S]*?)(?=<!-- fcc-end-source -->)/g;
const NBSPReg = new RegExp(String.fromCharCode(160), 'g');

const testJS = matchesProperty('ext', 'js');
const testJSX = matchesProperty('ext', 'jsx');
const testHTML = matchesProperty('ext', 'html');
const testHTML$JS$JSX = overSome(testHTML, testJS, testJSX);
export const testJS$JSX = overSome(testJS, testJSX);

export const replaceNBSP = cond([
  [
    testHTML$JS$JSX,
    partial(vinyl.transformContents, contents => contents.replace(NBSPReg, ' '))
  ],
  [stubTrue, identity]
]);

function tryTransform(wrap = identity) {
  return function transformWrappedPoly(source) {
    const result = attempt(wrap, source);
    if (isError(result)) {
      // note(Bouncey): Error thrown here to collapse the build pipeline
      // At the minute, it will not bubble up
      // We collapse the pipeline so the app doesn't fall over trying
      // parse bad code (syntax/type errors etc...)
      throw result;
    }
    return result;
  };
}

const babelTransformer = ({ preview = false, protect = true }) => {
  let options = babelOptionsJSBase;
  // we always protect the preview, since it evaluates as the user types and
  // they may briefly have infinite looping code accidentally
  if (protect) {
    options = preview ? babelOptionsJSPreview : babelOptionsJS;
  } else {
    options = preview ? babelOptionsJSPreview : options;
  }
  return cond([
    [
      testJS,
      async code => {
        await loadBabel();
        await loadPresetEnv();
        return partial(
          vinyl.transformHeadTailAndContents,
          tryTransform(babelTransformCode(options))
        )(code);
      }
    ],
    [
      testJSX,
      async code => {
        await loadBabel();
        await loadPresetReact();
        return flow(
          partial(
            vinyl.transformHeadTailAndContents,
            tryTransform(babelTransformCode(babelOptionsJSX))
          ),
          partial(vinyl.setExt, 'js')
        )(code);
      }
    ],
    [stubTrue, identity]
  ]);
};

const sassWorker = createWorker(sassCompile);
async function transformSASS(element) {
  const styleTags = element.querySelectorAll('style[type="text/sass"]');
  await Promise.all(
    [].map.call(styleTags, async style => {
      style.type = 'text/css';
      style.innerHTML = await sassWorker.execute(style.innerHTML, 5000).done;
    })
  );
}

async function transformScript(element) {
  await loadBabel();
  await loadPresetEnv();
  const scriptTags = element.querySelectorAll('script');
  scriptTags.forEach(script => {
    script.innerHTML = tryTransform(babelTransformCode(babelOptionsJS))(
      script.innerHTML
    );
  });
}

const transformHtml = async function(file) {
  const div = document.createElement('div');
  div.innerHTML = file.contents;
  await Promise.all([transformSASS(div), transformScript(div)]);
  return vinyl.transformContents(() => div.innerHTML, file);
};

export const composeHTML = cond([
  [
    testHTML,
    flow(
      partial(vinyl.transformHeadTailAndContents, source => {
        const div = document.createElement('div');
        div.innerHTML = source;
        return div.innerHTML;
      }),
      partial(vinyl.compileHeadTail, '')
    )
  ],
  [stubTrue, identity]
]);

export const htmlTransformer = cond([
  [testHTML, transformHtml],
  [stubTrue, identity]
]);

export const getTransformers = config => [
  replaceNBSP,
  babelTransformer(config ? config : {}),
  composeHTML,
  htmlTransformer
];
<|endoftext|>


<commit_before>$(document).ready(function(){

    $("li#icon-task a").css("background-color","#192532");
    $("li#icon-task a").css("color", "#e9d460");

    var addTaskButton = $(".task-button");

   $('.uncompleted').show();
   $('.completed').hide();

    $('#uncompleted-button').prop("disabled",true);

    addTaskButton.click(function () {

        var clickBtnValue = $(this).val();
        console.log(clickBtnValue);
        var ajaxurl = '../api/tasks/create-task.php',
            data =  {'action': clickBtnValue};
        $.post(ajaxurl, data, function (response) {
            var data = JSON.parse(response);
            console.log(data);
            var tasklist = document.getElementById('task-list');
            var row = tasklist.insertRow();
            row.setAttribute("class", "task");
            row.setAttribute("id", data[0].id);
            var cell1 = row.insertCell(0);
            var cell2 = row.insertCell(1);
            var cell3 = row.insertCell(2);
            cell1.innerHTML = "";
            cell2.innerHTML = "
<textarea onclick='toggle("+data[0].id+");' id="+data[0].id+">New Task</textarea>
";
            cell3.innerHTML = "";
        });

    });

    $('.input-group.date').datepicker();

    $('.select2-assign').select2({
        tags: true,
        maximumSelectionLength: 1
    });

    $('.select2-tags').select2({
        maximumSelectionLength: 1,
        tags: true
    });

    $("#task-name").keyup(function(e) {

        var m=$("input#task-name");
        var value= m.val();
        var taskId = $("#task-name").attr("name");
        $("#"+taskId + " textarea").val(value);

        console.log(taskId);

        console.log($("#"+taskId + " textarea").innerHTML);
        console.log( $("#task-name").attr("name"));
        console.log(m.val());
    });

    $(".select2").on('focusout', function() {

        $.ajax({
            type:'post',
            url: '../api/tasks/assign-task.php',
            data:  {'taskAssign': $("#task-assign option:selected").val(), 'taskId': $("#task-name").attr("name")},
            success: function() {

                console.log($("#task-assign option:selected").val());

            }

        });

        if($("#task-tags option:selected").html() != undefined){
            $.ajax({
                type:'post',
                url: '../api/tasks/set-task-tags.php',
                data:  {'taskTag':$("#task-tags option:selected").html() , 'taskId': $("#task-name").attr("name")},
                success: function(data) {

                    console.log(data);
                    console.log($("#task-tags option:selected").html());

                }

            });
        }

    });

    $("#task-name").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/change-task-name.php',
            data:  {'taskName': $("#task-name").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {

                console.log("done");

            }

        });

    });

    $("#task-deadline-date").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-deadline.php',
            data:  {'taskDeadline': $("#task-deadline-date").val()+" "+$("#task-deadline-time").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log($("#task-deadline-date").val()+" "+$("#task-deadline-time").val());
            }

        });

    });

    $("#task-deadline-time").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-deadline.php',
            data:  {'taskDeadline': $("#task-deadline-date").val()+" "+$("#task-deadline-time").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log($("#task-deadline-date").val()+" "+$("#task-deadline-time").val());
            }

        });
    });

    $("#task-description").focusout(function(e){
        $.ajax({
            type:'post',
            url: '../api/tasks/change-task-description.php',
            data:  {'taskDescription': $("#task-description").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log("description: " + $("#task-description").val());
                console.log("id: " + $("#task-name").attr("name"));

            }

        });
    });

    $("#task-tags").focusout(function(e){
        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-tags.php',
            data:  {'taskTags':  $("#task-assign option:selected").val() , 'taskId': $("#task-name").attr("name")},
            success: function(data) {
                console.log($("#task-assign option:selected").val());
                console.log("id: " + $("#task-name").attr("name"));

                console.log(data);
            }

        });
    });

    $("#add-comment-btn").click(function(e){
        if($("#comment-content").val() != ""){
            $.ajax({
                 type:'post',
                 url: '../api/tasks/add-task-comment.php',
                 data:  {'taskComment': $("#comment-content").val() , 'taskId': $("#task-name").attr("name")},
                 success: function() {
                     console.log($("#comment-content").val(""));
                 }

             });

            $.ajax({
                type:'post',
                url: '../api/tasks/get-task-comments.php',
                data:  {'taskId': $("#task-name").attr("name")},
                success: function(request) {
                    var response = JSON.parse(request);

                    console.log(response);

                    $("#task-comments").html("");
                    for (var i = 0; i < response[0].length; i++) {
                        $("#task-comments").append("

" + response[0][i].name + "

" + response[0][i].creation_date.substring(0,16) + "

" + response[0][i].content +"
");
                    }
                }

            });
        }

    });

});
function toggle(taskId) {

    $.ajax({
        type:'post',
        url: '../api/tasks/task-details.php',
        data:  {'taskId': taskId},
        success: function(request) {

            console.log(request);

            var response = JSON.parse(request);

            console.log(response);

            $("#task-assign").html("");
            $("#task-assign").append($('<option>', {
                value: response[2].id,
                text: response[2].name,
                selected: true
            }));

            $("#task-tags").html("");
            if(response[1].length > 0){
                $("#task-tags").append($('<option>', {
                    value: response[1][0].id,
                    text: response[1][0].name,
                    selected: true
                }));
            }

            for (var i = 0; i < response[5].length; i++) {
                if(response[5][i].name != "null-tag"){
                    $("#task-tags").append($('<option>', {
                        value: response[5][i].id,
                        text: response[5][i].name,
                    }));
                }
            }

            for (var i = 0; i < response[3].length; i++) {
                $("#task-assign").append($('<option>', {
                    value: response[3][i].id,
                    text: response[3][i].name,
                }));
            }
            if(response[0][0].name != null){
                $("#task-name").val(response[0][0].name);
            }
            $("#task-name").attr("name",taskId);
            if(response[0][0].description != null){
                $("#task-description").val(response[0][0].description);
            }
            if(response[0][0].deadline != null){
                $("#task-deadline-date").attr("value",response[0][0].deadline.split(" ")[0]);
                $("#task-deadline-time").attr("value",response[0][0].deadline.split(" ")[1]);
            }
            var id = "create-task";
            var taskCard = document.getElementById("task-card");
            var state = document.getElementById(id).style.display;
            if (state != 'none') {
                document.getElementById(id).style.display = 'none';
                document.getElementById(id).style.width = '1%';
                taskCard.style.display = 'inline-block';
                document.getElementById('mobile-back').style.display = 'none';
            } else {
                if ($(window).width() < 768) {
                    taskCard.style.width = '1%';
                    taskCard.style.display = 'none';
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById('mobile-back').style.display = 'inline-block';
                } else {
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '40%';
                    taskCard.style.width = '40%';
                    document.getElementById('mobile-back').style.display = 'none';
                }
            }

            $("#task-comments").html("");
            for (var i = 0; i < response[4].length; i++) {
                $("#task-comments").append("

" + response[4][i].name + "

" + response[4][i].creation_date.substring(0,16) + "

" + response[4][i].content +"
");
            }
        }

    });

}

function deleteTask(taskId) {

    $.ajax({
        type:'post',
        url: '../api/tasks/delete-task.php',
        data:  {'taskId': taskId},
        success: function() {

            $('#'+taskId).remove();

        }

    });
}

function completeTask(taskId) {

    if($("#"+taskId).hasClass("uncompleted")){
        $("#"+taskId).removeClass("uncompleted");
        $("#"+taskId).addClass("completed");
    }else{
        $("#"+taskId).removeClass("completed");
        $("#"+taskId).addClass("uncompleted");
    }

    if($('#uncompleted-button').prop("disabled")){
        showUncompletedTasks();
    }else{
        showCompletedTasks();
    }

    $.ajax({
        type:'post',
        url: '../api/tasks/complete-task.php',
        data:  {'taskId': taskId},
        success: function(result) {

            console.log(result);

        }

    });
}

function back() {

            var id = "create-task";
            var taskCard = document.getElementById("task-card");
            var state = document.getElementById(id).style.display;
            if (state != 'none') {
                document.getElementById(id).style.display = 'none';
                document.getElementById(id).style.width = '1%';
                taskCard.style.width = '70%';
                taskCard.style.display = 'inline-block';
                document.getElementById('mobile-back').style.display = 'none';
            } else {
                if ($(window).width() < 768) {
                    taskCard.style.width = '1%';
                    taskCard.style.display = 'none';
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '70%';
                    document.getElementById('mobile-back').style.display = 'inline-block';
                } else {
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '40%';
                    taskCard.style.width = '40%';
                    document.getElementById('mobile-back').style.display = 'none';
                }
            }
}

function showUncompletedTasks() {
    $('.uncompleted').show();
    $('.completed').hide();

    $('#uncompleted-button').prop("disabled",true);
    $('#completed-button').prop("disabled",false);
}

function showCompletedTasks() {
    $('.uncompleted').hide();
    $('.completed').show();

    $('#uncompleted-button').prop("disabled",false);
    $('#completed-button').prop("disabled",true);
}

function changeTagName(tag_name){

    $("#tag-name").html(tag_name).append('');

    if(tag_name === 'All'){
        $('.hashtag').each(function(i, obj) {

            console.log($(this));

            $(this).parents('.task').show();

            if($(this).hasClass("uncompleted")){
                if($('#uncompleted-button').prop("disabled")){
                    $(this).parents('.task').show();
                }else{
                    $(this).parents('.task').hide();
                }
            }

            if($(this).hasClass("completed")){
                if($('#completed-button').prop("disabled")){
                    $(this).parents('.task').show();
                }else {
                    $(this).parents('.task').hide();
                }
            }

            console.log($(this).hasClass("uncompleted"));

        });
    }
    else{
        $('.hashtag').each(function(i, obj) {
            var name = tag_name;
            console.log($(this).text());

            if($(this).text() != name || $(this).text() === "#"){
                $(this).parents('.task').hide();
                console.log($(this).parents('.task'));
            }else{
                console.log(tag_name);
                if($(this).hasClass("uncompleted")){
                    if($('#uncompleted-button').prop("disabled")){
                        $(this).parents('.task').show();
                    }else{
                        $(this).parents('.task').hide();
                    }
                }
                if($(this).hasClass("completed")){
                    if($('#completed-button').prop("disabled")){
                        $(this).parents('.task').show();
                    }else {
                        $(this).parents('.task').hide();
                    }
                }
            }
        });

    }
}
<commit_msg>task<commit_after>$(document).ready(function(){

    $("li#icon-task a").css("background-color","#192532");
    $("li#icon-task a").css("color", "#e9d460");

    var addTaskButton = $(".task-button");

   $('.uncompleted').show();
   $('.completed').hide();

    $('#uncompleted-button').prop("disabled",true);

    addTaskButton.click(function () {

        var clickBtnValue = $(this).val();
        console.log(clickBtnValue);
        var ajaxurl = '../api/tasks/create-task.php',
            data =  {'action': clickBtnValue};
        $.post(ajaxurl, data, function (response) {
            var data = JSON.parse(response);
            console.log(data);
            var tasklist = document.getElementById('task-list');
            var row = tasklist.insertRow();
            row.setAttribute("class", "task");
            row.setAttribute("id", data[0].id);
            var cell1 = row.insertCell(0);
            var cell2 = row.insertCell(1);
            var cell3 = row.insertCell(2);
            cell1.innerHTML = "";
            cell2.innerHTML = "
<textarea onclick='toggle("+data[0].id+");' id="+data[0].id+" disabled='true'>New Task</textarea>
";
            cell3.innerHTML = "";
        });

    });

    $('.input-group.date').datepicker();

    $('.select2-assign').select2({
        tags: true,
        maximumSelectionLength: 1
    });

    $('.select2-tags').select2({
        maximumSelectionLength: 1,
        tags: true
    });

    $("#task-name").keyup(function(e) {

        var m=$("input#task-name");
        var value= m.val();
        var taskId = $("#task-name").attr("name");
        $("#"+taskId + " textarea").val(value);

        console.log(taskId);

        console.log($("#"+taskId + " textarea").innerHTML);
        console.log( $("#task-name").attr("name"));
        console.log(m.val());
    });

    $(".select2").on('focusout', function() {

        $.ajax({
            type:'post',
            url: '../api/tasks/assign-task.php',
            data:  {'taskAssign': $("#task-assign option:selected").val(), 'taskId': $("#task-name").attr("name")},
            success: function() {

                console.log($("#task-assign option:selected").val());

            }

        });

        if($("#task-tags option:selected").html() != undefined){
            $.ajax({
                type:'post',
                url: '../api/tasks/set-task-tags.php',
                data:  {'taskTag':$("#task-tags option:selected").html() , 'taskId': $("#task-name").attr("name")},
                success: function(data) {

                    console.log(data);
                    console.log($("#task-tags option:selected").html());

                }

            });
        }

    });

    $("#task-name").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/change-task-name.php',
            data:  {'taskName': $("#task-name").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {

                console.log("done");

            }

        });

    });

    $("#task-deadline-date").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-deadline.php',
            data:  {'taskDeadline': $("#task-deadline-date").val()+" "+$("#task-deadline-time").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log($("#task-deadline-date").val()+" "+$("#task-deadline-time").val());
            }

        });

    });

    $("#task-deadline-time").focusout(function(e) {

        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-deadline.php',
            data:  {'taskDeadline': $("#task-deadline-date").val()+" "+$("#task-deadline-time").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log($("#task-deadline-date").val()+" "+$("#task-deadline-time").val());
            }

        });
    });

    $("#task-description").focusout(function(e){
        $.ajax({
            type:'post',
            url: '../api/tasks/change-task-description.php',
            data:  {'taskDescription': $("#task-description").val() , 'taskId': $("#task-name").attr("name")},
            success: function() {
                console.log("description: " + $("#task-description").val());
                console.log("id: " + $("#task-name").attr("name"));

            }

        });
    });

    $("#task-tags").focusout(function(e){
        $.ajax({
            type:'post',
            url: '../api/tasks/set-task-tags.php',
            data:  {'taskTags':  $("#task-assign option:selected").val() , 'taskId': $("#task-name").attr("name")},
            success: function(data) {
                console.log($("#task-assign option:selected").val());
                console.log("id: " + $("#task-name").attr("name"));

                console.log(data);
            }

        });
    });

    $("#add-comment-btn").click(function(e){
        if($("#comment-content").val() != ""){
            $.ajax({
                 type:'post',
                 url: '../api/tasks/add-task-comment.php',
                 data:  {'taskComment': $("#comment-content").val() , 'taskId': $("#task-name").attr("name")},
                 success: function() {
                     console.log($("#comment-content").val(""));
                 }

             });

            $.ajax({
                type:'post',
                url: '../api/tasks/get-task-comments.php',
                data:  {'taskId': $("#task-name").attr("name")},
                success: function(request) {
                    var response = JSON.parse(request);

                    console.log(response);

                    $("#task-comments").html("");
                    for (var i = 0; i < response[0].length; i++) {
                        $("#task-comments").append("

" + response[0][i].name + "

" + response[0][i].creation_date.substring(0,16) + "

" + response[0][i].content +"
");
                    }
                }

            });
        }

    });

});
function toggle(taskId) {

    $.ajax({
        type:'post',
        url: '../api/tasks/task-details.php',
        data:  {'taskId': taskId},
        success: function(request) {

            console.log(request);

            var response = JSON.parse(request);

            console.log(response);

            $("#task-assign").html("");
            $("#task-assign").append($('<option>', {
                value: response[2].id,
                text: response[2].name,
                selected: true
            }));

            $("#task-tags").html("");
            if(response[1].length > 0){
                $("#task-tags").append($('<option>', {
                    value: response[1][0].id,
                    text: response[1][0].name,
                    selected: true
                }));
            }

            for (var i = 0; i < response[5].length; i++) {
                if(response[5][i].name != "null-tag"){
                    $("#task-tags").append($('<option>', {
                        value: response[5][i].id,
                        text: response[5][i].name,
                    }));
                }
            }

            for (var i = 0; i < response[3].length; i++) {
                $("#task-assign").append($('<option>', {
                    value: response[3][i].id,
                    text: response[3][i].name,
                }));
            }
            if(response[0][0].name != null){
                $("#task-name").val(response[0][0].name);
            }
            $("#task-name").attr("name",taskId);
            if(response[0][0].description != null){
                $("#task-description").val(response[0][0].description);
            }
            if(response[0][0].deadline != null){
                $("#task-deadline-date").attr("value",response[0][0].deadline.split(" ")[0]);
                $("#task-deadline-time").attr("value",response[0][0].deadline.split(" ")[1]);
            }
            var id = "create-task";
            var taskCard = document.getElementById("task-card");
            var state = document.getElementById(id).style.display;
            if (state != 'none') {
                document.getElementById(id).style.display = 'none';
                document.getElementById(id).style.width = '1%';
                taskCard.style.display = 'inline-block';
                document.getElementById('mobile-back').style.display = 'none';
            } else {
                if ($(window).width() < 768) {
                    taskCard.style.width = '1%';
                    taskCard.style.display = 'none';
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById('mobile-back').style.display = 'inline-block';
                } else {
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '40%';
                    taskCard.style.width = '40%';
                    document.getElementById('mobile-back').style.display = 'none';
                }
            }

            $("#task-comments").html("");
            for (var i = 0; i < response[4].length; i++) {
                $("#task-comments").append("

" + response[4][i].name + "

" + response[4][i].creation_date.substring(0,16) + "

" + response[4][i].content +"
");
            }
        }

    });

}

function deleteTask(taskId) {

    $.ajax({
        type:'post',
        url: '../api/tasks/delete-task.php',
        data:  {'taskId': taskId},
        success: function() {

            $('#'+taskId).remove();

        }

    });
}

function completeTask(taskId) {

    if($("#"+taskId).hasClass("uncompleted")){
        $("#"+taskId).removeClass("uncompleted");
        $("#"+taskId).addClass("completed");
    }else{
        $("#"+taskId).removeClass("completed");
        $("#"+taskId).addClass("uncompleted");
    }

    if($('#uncompleted-button').prop("disabled")){
        showUncompletedTasks();
    }else{
        showCompletedTasks();
    }

    $.ajax({
        type:'post',
        url: '../api/tasks/complete-task.php',
        data:  {'taskId': taskId},
        success: function(result) {

            console.log(result);

        }

    });
}

function back() {

            var id = "create-task";
            var taskCard = document.getElementById("task-card");
            var state = document.getElementById(id).style.display;
            if (state != 'none') {
                document.getElementById(id).style.display = 'none';
                document.getElementById(id).style.width = '1%';
                taskCard.style.width = '70%';
                taskCard.style.display = 'inline-block';
                document.getElementById('mobile-back').style.display = 'none';
            } else {
                if ($(window).width() < 768) {
                    taskCard.style.width = '1%';
                    taskCard.style.display = 'none';
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '70%';
                    document.getElementById('mobile-back').style.display = 'inline-block';
                } else {
                    document.getElementById(id).style.display = 'inline-block';
                    document.getElementById(id).style.width = '40%';
                    taskCard.style.width = '40%';
                    document.getElementById('mobile-back').style.display = 'none';
                }
            }
}

function showUncompletedTasks() {
    $('.uncompleted').show();
    $('.completed').hide();

    $('#uncompleted-button').prop("disabled",true);
    $('#completed-button').prop("disabled",false);
}

function showCompletedTasks() {
    $('.uncompleted').hide();
    $('.completed').show();

    $('#uncompleted-button').prop("disabled",false);
    $('#completed-button').prop("disabled",true);
}

function changeTagName(tag_name){

    $("#tag-name").html(tag_name).append('');

    if(tag_name === 'All'){
        $('.hashtag').each(function(i, obj) {

            console.log($(this));

            $(this).parents('.task').show();

            if($(this).hasClass("uncompleted")){
                if($('#uncompleted-button').prop("disabled")){
                    $(this).parents('.task').show();
                }else{
                    $(this).parents('.task').hide();
                }
            }

            if($(this).hasClass("completed")){
                if($('#completed-button').prop("disabled")){
                    $(this).parents('.task').show();
                }else {
                    $(this).parents('.task').hide();
                }
            }

            console.log($(this).hasClass("uncompleted"));

        });
    }
    else{
        $('.hashtag').each(function(i, obj) {
            var name = tag_name;
            console.log($(this).text());

            if($(this).text() != name || $(this).text() === "#"){
                $(this).parents('.task').hide();
                console.log($(this).parents('.task'));
            }else{
                console.log(tag_name);
                if($(this).hasClass("uncompleted")){
                    if($('#uncompleted-button').prop("disabled")){
                        $(this).parents('.task').show();
                    }else{
                        $(this).parents('.task').hide();
                    }
                }
                if($(this).hasClass("completed")){
                    if($('#completed-button').prop("disabled")){
                        $(this).parents('.task').show();
                    }else {
                        $(this).parents('.task').hide();
                    }
                }
            }
        });

    }
}
<|endoftext|>


<commit_before>/*
 * r_highlight_rules.js
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
define("mode/r_highlight_rules", function(require, exports, module)
{

   var oop = require("ace/lib/oop");
   var lang = require("ace/lib/lang");
   var TextHighlightRules = require("ace/mode/text_highlight_rules")
         .TextHighlightRules;
   var TexHighlightRules = require("mode/tex_highlight_rules").TexHighlightRules;

   var RHighlightRules = function()
   {

      var keywords = lang.arrayToMap(
            ("function|if|in|break|next|repeat|else|for|return|switch|while|try|tryCatch|stop|warning|require|library|attach|detach|source|setMethod|setGeneric|setGroupGeneric|setClass|setRefClass")
                  .split("|")
            );

      var buildinConstants = lang.arrayToMap(
            ("NULL|NA|TRUE|FALSE|T|F|Inf|NaN|NA_integer_|NA_real_|NA_character_|" +
             "NA_complex_").split("|")
            );

      // regexp must not have capturing parentheses. Use (?:) instead.
      // regexps are ordered -> the first match is used

      this.$rules = {
         "start" : [
            {
               // Roxygen
               token : "comment.sectionhead",
               regex : "#+(?!').*(?:----|====|####)\\s*$"
            },
            {
               // Roxygen
               token : "comment",
               regex : "#+'",
               next : "rd-start"
            },
            {
               token : "comment",
               regex : "#.*$"
            },
            {
               token : "string", // single line
               regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
            },
            {
               token : "string", // single line
               regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"
            },
            {
               token : "string", // multi line string start
               merge : true,
               regex : '["]',
               next : "qqstring"
            },
            {
               token : "string", // multi line string start
               merge : true,
               regex : "[']",
               next : "qstring"
            },
            {
               token : "constant.numeric", // hex
               regex : "0[xX][0-9a-fA-F]+[Li]?\\b",
               merge : false
            },
            {
               token : "constant.numeric", // number + integer
               regex : "\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?[iL]?\\b",
               merge : false
            },
            {
               token : "constant.numeric", // number + integer with leading decimal
               regex : "\\.\\d+(?:[eE][+\\-]?\\d*)?[iL]?\\b",
               merge : false
            },
            {
               token : "constant.language.boolean",
               regex : "(?:TRUE|FALSE|T|F)\\b",
               merge : false
            },
            {
               token : "identifier",
               regex : "`.*?`",
               merge : false
            },
            {
               token : function(value)
               {
                  if (keywords[value])
                     return "keyword";
                  else if (buildinConstants[value])
                     return "constant.language";
                  else if (value.match(/^\.\.\d+$/))
                     return "variable.language";
                  else
                     return "identifier";
               },
               regex : "[a-zA-Z.][a-zA-Z0-9._]*"
            },
            {
               token : "keyword.operator",
               regex : ":::|::|:=|%%|>=|<=|==|!=|\\->|<\\-|<<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:|@",
               merge : false
            },
            {
               token : "keyword.operator.infix", // infix operators
               regex : "%.*?%",
               merge : false
            },
            {
               // Obviously these are neither keywords nor operators, but
               // labelling them as such was the easiest way to get them
               // to be colored distinctly from regular text
               token : "paren.keyword.operator",
               merge : false,
               regex : "[[({]"
            },
            {
               // Obviously these are neither keywords nor operators, but
               // labelling them as such was the easiest way to get them
               // to be colored distinctly from regular text
               token : "paren.keyword.operator",
               merge : false,
               regex : "[\\])}]"
            },
            {
               token : "punctuation",
               regex : "[;,]",
               merge : false
            },
            {
               token : "text",
               regex : "\\s+"
            }
         ],
         "qqstring" : [
            {
               token : "string",
               regex : '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
               next : "start"
            },
            {
               token : "string",
               regex : '.+',
               merge : true
            }
         ],
         "qstring" : [
            {
               token : "string",
               regex : "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
               next : "start"
            },
            {
               token : "string",
               regex : '.+',
               merge : true
            }
         ]
      };

      var rdRules = new TexHighlightRules("comment").getRules();

      // Make all embedded TeX virtual-comment so they don't interfere with
      // auto-indent.
      for (var i = 0; i < rdRules["start"].length; i++) {
         rdRules["start"][i].token += ".virtual-comment";
      }

      this.addRules(rdRules, "rd-");
      this.$rules["rd-start"].unshift({
          token: "text",
          regex: "^",
          next: "start"
      });
      this.$rules["rd-start"].unshift({
         token : "keyword",
         regex : "@(?!@)[^ ]*"
      });
      this.$rules["rd-start"].unshift({
         token : "comment",
         regex : "@@"
      });
      this.$rules["rd-start"].push({
         token : "comment",
         regex : "[^%\\\\[({\\])}]+"
      });
   };

   oop.inherits(RHighlightRules, TextHighlightRules);

   exports.RHighlightRules = RHighlightRules;
});
<commit_msg>smarter highlight rules for R keyword functions<commit_after>/*
 * r_highlight_rules.js
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
define("mode/r_highlight_rules", function(require, exports, module)
{

   var oop = require("ace/lib/oop");
   var lang = require("ace/lib/lang");
   var TextHighlightRules = require("ace/mode/text_highlight_rules")
         .TextHighlightRules;
   var TexHighlightRules = require("mode/tex_highlight_rules").TexHighlightRules;

   var RHighlightRules = function()
   {
      var keywordFunctions = lang.arrayToMap([
         "function", "if", "else", "in", "break", "next", "repeat", "for", "while"
      ]);

      var specialFunctions = lang.arrayToMap([
         "return", "switch", "try", "tryCatch", "stop",
         "warning", "require", "library", "attach", "detach",
         "source", "setMethod", "setGeneric", "setGroupGeneric",
         "setClass", "setRefClass", "R6Class"
      ]);

      var buildinConstants = lang.arrayToMap([
         "NULL", "NA", "TRUE", "FALSE", "T", "F", "Inf",
         "NaN", "NA_integer_", "NA_real_", "NA_character_",
         "NA_complex_"
      ]);

      // regexp must not have capturing parentheses. Use (?:) instead.
      // regexps are ordered -> the first match is used
      this.$rules = {

         "before": [
            {
               // Roxygen
               token : "comment.sectionhead",
               regex : "#+(?!').*(?:----|====|####)\\s*$",
               next  : "start"
            },
            {
               // Roxygen
               token : "comment",
               regex : "#+'",
               next  : "rd-start"
            },
            {
               token : "comment",
               regex : "#.*$",
               next  : "start"
            },
            {
               token : "string", // single line
               regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]',
               next  : "start"
            },
            {
               token : "string", // single line
               regex : "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']",
               next  : "start"
            },
            {
               token : "string", // multi line string start
               merge : true,
               regex : '["]',
               next : "qqstring"
            },
            {
               token : "string", // multi line string start
               merge : true,
               regex : "[']",
               next : "qstring"
            },
            {
               token : "constant.numeric", // hex
               regex : "0[xX][0-9a-fA-F]+[Li]?\\b",
               merge : false,
               next  : "start"
            },
            {
               token : "constant.numeric", // number + integer
               regex : "\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d*)?[iL]?\\b",
               merge : false,
               next  : "start"
            },
            {
               token : "constant.numeric", // number + integer with leading decimal
               regex : "\\.\\d+(?:[eE][+\\-]?\\d*)?[iL]?\\b",
               merge : false,
               next  : "start"
            },
            {
               token : "constant.language.boolean",
               regex : "(?:TRUE|FALSE|T|F)\\b",
               merge : false,
               next  : "start"
            },
            {
               token : "identifier",
               regex : "`.*?`",
               merge : false,
               next  : "start"
            }
         ],

         "allowKeywordFunctions": [
            {
               token : function(value) {
                  if (keywordFunctions[value] || specialFunctions[value])
                     return "keyword";
                  else
                     return "identifier";
               },
               regex : "[a-zA-Z.][a-zA-Z0-9._]*(?=\\s*\\()",
               next  : "start"
            }
         ],

         "after": [
            {
               token : function(value)
               {
                  if (buildinConstants[value])
                     return "constant.language";
                  else if (value.match(/^\.\.\d+$/))
                     return "variable.language";
                  else
                     return "identifier";
               },
               regex : "[a-zA-Z.][a-zA-Z0-9._]*",
               next  : "start"
            },
            {
               token : "keyword.operator",
               regex : "\\$|@",
               merge : false,
               next  : "afterDollar"
            },
            {
               token : "keyword.operator",
               regex : ":::|::|:=|%%|>=|<=|==|!=|\\->|<\\-|<<\\-|\\|\\||&&|=|\\+|\\-|\\*|/|\\^|>|<|!|&|\\||~|\\$|:|@",
               merge : false,
               next  : "start"
            },
            {
               token : "keyword.operator.infix", // infix operators
               regex : "%.*?%",
               merge : false,
               next  : "start"
            },
            {
               // Obviously these are neither keywords nor operators, but
               // labelling them as such was the easiest way to get them
               // to be colored distinctly from regular text
               token : "paren.keyword.operator",
               merge : false,
               regex : "[[({]",
               next  : "start"
            },
            {
               // Obviously these are neither keywords nor operators, but
               // labelling them as such was the easiest way to get them
               // to be colored distinctly from regular text
               token : "paren.keyword.operator",
               merge : false,
               regex : "[\\])}]",
               next  : "start"
            },
            {
               token : "punctuation",
               regex : "[;,]",
               merge : false,
               next  : "start"
            },
            {
               token : "text",
               regex : "\\s+",
               next  : "start"
            }
         ],

         "start" : [
            {
               include: "before"
            },
            {
               include: "allowKeywordFunctions"
            },
            {
               include: "after"
            }
         ],

         "afterDollar" : [
            {
               include : "before"
            },
            {
               include: "after"
            }
         ],

         "qqstring" : [
            {
               token : "string",
               regex : '(?:(?:\\\\.)|(?:[^"\\\\]))*?"',
               next  : "start"
            },
            {
               token : "string",
               regex : '.+',
               merge : true
            }
         ],

         "qstring" : [
            {
               token : "string",
               regex : "(?:(?:\\\\.)|(?:[^'\\\\]))*?'",
               next  : "start"
            },
            {
               token : "string",
               regex : '.+',
               merge : true
            }
         ]

      };

      var rdRules = new TexHighlightRules("comment").getRules();

      // Make all embedded TeX virtual-comment so they don't interfere with
      // auto-indent.
      for (var i = 0; i < rdRules["start"].length; i++) {
         rdRules["start"][i].token += ".virtual-comment";
      }

      this.addRules(rdRules, "rd-");
      this.$rules["rd-start"].unshift({
          token: "text",
          regex: "^",
          next: "start"
      });
      this.$rules["rd-start"].unshift({
         token : "keyword",
         regex : "@(?!@)[^ ]*"
      });
      this.$rules["rd-start"].unshift({
         token : "comment",
         regex : "@@"
      });
      this.$rules["rd-start"].push({
         token : "comment",
         regex : "[^%\\\\[({\\])}]+"
      });

      this.normalizeRules();
   };

   oop.inherits(RHighlightRules, TextHighlightRules);

   exports.RHighlightRules = RHighlightRules;
});
<|endoftext|>