<commit_before>/*************************************************
 * Copyright (c) 2016 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
['$rootScope', '$location', '$log','$state',
    function ($rootScope, $location, $log, $state) {
        return {
            init: function() {
                var self = this,
                    host = window.location.host,
                    url = "ws://" + host + "/websocket/";
                if (!$rootScope.sessionTimer || ($rootScope.sessionTimer && !$rootScope.sessionTimer.isExpired())) {
                    // We have a valid session token, so attempt socket connection
                    $log.debug('Socket connecting to: ' + url);

                    self.socket = new ReconnectingWebSocket(url, null, {
                        timeoutInterval: 3000,
                        maxReconnectAttempts: 10
                    });

                    self.socket.onopen = function () {
                        $log.debug("Websocket connection opened.");
                        $rootScope.socketPromise.resolve();
                    };

                    self.socket.onerror = function (error) {
                        $log.debug('Websocket Error Logged: ' + error); //log errors
                    };

                    self.socket.onclose = function (error) {
                        $log.debug('Websocket Disconnected: '+error);
                        self.checkStatus();
                    };

                    self.socket.onmessage = this.onMessage;

                    return self.socket;
                }
                else {
                    // encountered expired token, redirect to login page
                    $rootScope.sessionTimer.expireSession('idle');
                    $location.url('/login');
                }

                setTimeout(function() {
                        self.checkStatus();
                        $log.debug('socket status: ' + $rootScope.socketStatus);
                }, 2000);
            },
            onMessage: function(e){
                $log.debug('Received From Server: ' + e.data);
                var data = JSON.parse(e.data), str = "";
                if(data.group_name==="jobs" && !('status' in data)){
                    // we know that this must have been a
                    // summary complete message b/c status is missing
                    $log.debug('Job summary_complete ' + data.unified_job_id);
                    $rootScope.$emit('ws-jobs-summary', data);
                    return;
                }
                else if(data.group_name==="job_events"){
                    str = `ws-${data.group_name}-${data.job}`;
                }
                else if(data.group_name==="ad_hoc_command_events"){
                    str = `ws-${data.group_name}-${data.ad_hoc_command}`;
                }
                else if(data.group_name==="control"){
                    $log.debug(data.reason);
                    $rootScope.sessionTimer.expireSession('session_limit');
                    $state.go('signOut');
                }
                else {
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name.
                    // ex: 'ws-jobs'
                    str = `ws-${data.group_name}`;
                }
                $rootScope.$emit(str, data);
            },
            disconnect: function(){
                if(this.socket){
                    this.socket.close();
                }
            },
            subscribe: function(state){
                this.emit(JSON.stringify(state.socket));
                this.setLast(state);
            },
            unsubscribe: function(state){
                if(this.requiresNewSubscribe(state)){
                    this.emit(JSON.stringify(state.socket));
                }
                this.setLast(state);
            },
            setLast: function(state){
                this.last = state;
            },
            getLast: function(){
                return this.last;
            },
            requiresNewSubscribe(state){
                if (this.getLast() !== undefined){
                    if( _.isEmpty(state.socket.groups) && _.isEmpty(this.getLast().socket.groups)){
                        return false;
                    }
                    else {
                        return true;
                    }
                }
                else {
                    return true;
                }
            },
            checkStatus: function() {
                function getSocketTip(status) {
                    var result = '';
                    switch(status) {
                        case 'error':
                            result = "Live events: error connecting to the Tower server.";
                            break;
                        case 'connecting':
                            result = "Live events: attempting to connect to the Tower server.";
                            break;
                        case "ok":
                            result = "Live events: connected. Pages containing job status information will automatically update in real-time.";
                    }
                    return result;
                }
                // Check connection status
                var self = this;
                if(self){
                    if(self.socket){
                        if (self.socket.readyState === 0 ) {
                            $rootScope.socketStatus = 'connecting';
                        }
                        else if (self.socket.readyState === 1){
                            $rootScope.socketStatus = 'ok';
                        }
                        else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
                            $rootScope.socketStatus = 'error';
                        }
                        self.socketTip = getSocketTip(self.socketStatus);
                        return $rootScope.socketStatus;
                    }
                }

            },
            emit: function(data, callback) {
                var self = this;
                $log.debug('Sent to Websocket Server: ' + data);
                $rootScope.socketPromise.promise.then(function(){
                    self.socket.send(data, function () {
                        var args = arguments;
                        self.scope.$apply(function () {
                            if (callback) {
                                callback.apply(self.socket, args);
                            }
                        });
                    });
                });
            }
        };
    }];
<commit_msg>fixing statusTip and socketStatus<commit_after>/*************************************************
 * Copyright (c) 2016 Ansible, Inc.
 *
 * All Rights Reserved
 *************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
['$rootScope', '$location', '$log','$state',
    function ($rootScope, $location, $log, $state) {
        return {
            init: function() {
                var self = this,
                    host = window.location.host,
                    url = "ws://" + host + "/websocket/";
                if (!$rootScope.sessionTimer || ($rootScope.sessionTimer && !$rootScope.sessionTimer.isExpired())) {
                    // We have a valid session token, so attempt socket connection
                    $log.debug('Socket connecting to: ' + url);

                    self.socket = new ReconnectingWebSocket(url, null, {
                        timeoutInterval: 3000,
                        maxReconnectAttempts: 10
                    });

                    self.socket.onopen = function () {
                        $log.debug("Websocket connection opened.");
                        $rootScope.socketPromise.resolve();
                    };

                    self.socket.onerror = function (error) {
                        $log.debug('Websocket Error Logged: ' + error); //log errors
                    };

                    self.socket.onclose = function () {
                        $log.debug('Websocket Disconnected');
                    };

                    self.socket.onmessage = this.onMessage;
                    setTimeout(function() {
                            self.checkStatus();
                            $log.debug('Socket Status: ' + $rootScope.socketStatus);
                    }, 2000);
                    return self.socket;
                }
                else {
                    // encountered expired token, redirect to login page
                    $rootScope.sessionTimer.expireSession('idle');
                    $location.url('/login');
                }
            },
            onMessage: function(e){
                $log.debug('Received From Server: ' + e.data);
                var data = JSON.parse(e.data), str = "";
                if(data.group_name==="jobs" && !('status' in data)){
                    // we know that this must have been a
                    // summary complete message b/c status is missing
                    $log.debug('Job summary_complete ' + data.unified_job_id);
                    $rootScope.$emit('ws-jobs-summary', data);
                    return;
                }
                else if(data.group_name==="job_events"){
                    str = `ws-${data.group_name}-${data.job}`;
                }
                else if(data.group_name==="ad_hoc_command_events"){
                    str = `ws-${data.group_name}-${data.ad_hoc_command}`;
                }
                else if(data.group_name==="control"){
                    $log.debug(data.reason);
                    $rootScope.sessionTimer.expireSession('session_limit');
                    $state.go('signOut');
                }
                else {
                    // The naming scheme is "ws" then a
                    // dash (-) and the group_name.
                    // ex: 'ws-jobs'
                    str = `ws-${data.group_name}`;
                }
                $rootScope.$emit(str, data);
            },
            disconnect: function(){
                if(this.socket){
                    this.socket.close();
                }
            },
            subscribe: function(state){
                this.emit(JSON.stringify(state.socket));
                this.setLast(state);
            },
            unsubscribe: function(state){
                if(this.requiresNewSubscribe(state)){
                    this.emit(JSON.stringify(state.socket));
                }
                this.setLast(state);
            },
            setLast: function(state){
                this.last = state;
            },
            getLast: function(){
                return this.last;
            },
            requiresNewSubscribe(state){
                if (this.getLast() !== undefined){
                    if( _.isEmpty(state.socket.groups) && _.isEmpty(this.getLast().socket.groups)){
                        return false;
                    }
                    else {
                        return true;
                    }
                }
                else {
                    return true;
                }
            },
            checkStatus: function() {
                var self = this;
                if(self){
                    if(self.socket){
                        if (self.socket.readyState === 0 ) {
                            $rootScope.socketStatus = 'connecting';
                            $rootScope.socketTip = "Live events: attempting to connect to the Tower server.";
                        }
                        else if (self.socket.readyState === 1){
                            $rootScope.socketStatus = 'ok';
                            $rootScope.socketTip = "Live events: connected. Pages containing job status information will automatically update in real-time.";
                        }
                        else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
                            $rootScope.socketStatus = 'error';
                            $rootScope.socketTip = "Live events: error connecting to the Tower server.";
                        }
                        return;
                    }
                }

            },
            emit: function(data, callback) {
                var self = this;
                $log.debug('Sent to Websocket Server: ' + data);
                $rootScope.socketPromise.promise.then(function(){
                    self.socket.send(data, function () {
                        var args = arguments;
                        self.scope.$apply(function () {
                            if (callback) {
                                callback.apply(self.socket, args);
                            }
                        });
                    });
                });
            }
        };
    }];
<|endoftext|>


<commit_before>'use strict';

const FILE = 'storage-files/delet.json';
const fs = require('fs');
let deletoff = false;
let delets = JSON.parse(fs.readFileSync(FILE));

function saveDelets() {
	fs.writeFileSync(File, JSON.stringify(delets, null, 1));
}

exports.commands = {

deletthis: function (target, room, user) {
			if (!this.canTalk()) return this.errorReply("You cannot use this command");
			if (deletoff) return this.sendReply("'Delet This' are currently disabled.");
		let message = delets[Math.floor(Math.random() * delets.length)];
		this.add('|html|');
	},

};<commit_msg>lets see if this works<commit_after>'use strict';

const FILE = 'storage-files/delet.json';
const fs = require('fs');
let deletoff = false;
let delets = JSON.parse(fs.readFileSync(FILE));

function saveDelets() {
	fs.writeFileSync(File, JSON.stringify(delets, null, 1));
}

exports.commands = {

deletthis: function (target, room, user) {
			if (!this.canTalk()) return this.errorReply("You cannot use this command");
			if (deletoff) return this.sendReply("'Delet This' are currently disabled.");
		let message = delets[Math.floor(Math.random() * delets.length)];
		this.sendReplyBox('');
	},

};<|endoftext|>


<commit_before>import React from "react";
import { mount } from "enzyme";
import { fromJS } from "immutable";
import ActivityAnswers from "../../../js/components/dashboard/activity-answers";
import Answer from "../../../js/containers/dashboard/answer";
import ProgressBar from "../../../js/components/dashboard/progress-bar";

import { mountWithStore } from "../../setupTest";

const state = fromJS({
  report: {
    answers: {
      a1 : {
        questionId: "1",
        platformUserId: "studentWithIncorrect",
        type: "multiple_choice_answer",
        answer: {
          choiceIds: [ "incorrectChoice" ]
        }
      },
      a2 : {
        questionId: "1",
        platformUserId: "studentWithCorrect",
        type: "multiple_choice_answer",
        answer: {
          choiceIds: [ "correctChoice" ]
        }
      }
    },
    students: {
      studentWithIncorrect: {},
      studentWithCorrect: {}
    },
    questions: {
      1: {
        choices: [
          { id: "incorrectChoice" },
          { id: "correctChoice", correct: true } ]
      }
    }
  }
});

describe("<ActivityAnswers />", () => {
  it("should render a progress bar when not expanded", () => {
    const activity = fromJS({questions: []});
    const wrapper = mountWithStore(<ActivityAnswers activity={activity} expanded={false} />, state);
    expect(wrapper.find(ProgressBar)).toHaveLength(1);
  });

  it("should render answers when expanded", () => {
    const activity = fromJS({questions: [{id: 1, visible: true}]});
    const expandedQuestions = fromJS({1: true});
    const wrapper = mountWithStore(<ActivityAnswers
      activity={activity} expanded={true} expandedQuestions={expandedQuestions}/>, state);
    expect(wrapper.find(Answer)).toHaveLength(1);
  });

  it("should render 0/1 when expanded with a scored incorrect answer", () => {
    const activity = fromJS({questions: [
      {id: "1", visible: true, type: "multiple_choice", scored: true }
    ]});
    const student = fromJS({id: "studentWithIncorrect"});
    const expandedQuestions = fromJS({1: true});
    const wrapper = mountWithStore(<ActivityAnswers
      activity={activity}
      expanded={true}
      multChoiceSummary={true}
      student={student}
      expandedQuestions={expandedQuestions}/>, state);
    expect(wrapper.find(".multChoiceSummary")).toHaveLength(1);
    expect(wrapper.find(".multChoiceSummary").text()).toBe("0 / 1");
  });

  it("should render 1/1 when expanded with a scored incorrect answer", () => {
    const activity = fromJS({questions: [
      {id: "1", visible: true, type: "multiple_choice", scored: true }
    ]});
    const student = fromJS({id: "studentWithCorrect"});
    const expandedQuestions = fromJS({1: true});
    const wrapper = mountWithStore(<ActivityAnswers
      activity={activity}
      expanded={true}
      multChoiceSummary={true}
      student={student}
      expandedQuestions={expandedQuestions}/>, state);
    expect(wrapper.find(".multChoiceSummary")).toHaveLength(1);
    expect(wrapper.find(".multChoiceSummary").text()).toBe("1 / 1");
  });

});
<commit_msg>remove activity-answers-spec<commit_after><|endoftext|>


<commit_before>var vows            = require('vows')
  , assert          = require('assert')
  , printTestResult = require('../printTestResult.js')
  , schema          = require('../../index.js')

// Create a Test Suite
vows.describe('Validation Object with errors').addBatch({
  'Object': {
    'Invalid input : empty object': function() {
      var input = {
        object: {},
        number: 3
      };
      var B = function B() {};
      var instance = schema({
        func: Function,
        number: Number.min(6).max(9),
        boolean: Boolean,
        string: String,
        class: B,
        object: {
          a: Number.min(2).max(7),
          b: Function
        }
      });
      var result = instance.errors(input);
      console.log('Input : \n', vows.inspect(input));
      console.log('Errors : \n', vows.inspect(result));
      assert(result, printTestResult(input, result))
    },
    'Invalid nested input': function() {
      var input = {
        a: {
          aa: false,
          cc: false
        },
        d: true
      };
      var instance = schema({
        a: {
          aa: [Number.min(2), String],
          bb: {
            aaa: Boolean,
            bbb: Number,
            ccc: {
              aaaa: Boolean
            }
          },
          cc: Function
        },
        b: Number,
        c: Boolean,
        d: Number
      });
      var result = instance.errors(input);
      console.log('Input : \n', vows.inspect(input));
      console.log('Errors : \n', vows.inspect(result));
      assert(result, printTestResult(input, result))
    },
    "original validate schema has a bug with regexp field validation.": function () {
      //object should have at least one String field
      var validation = schema({
        "+.+" : String
      });
      var emptyObject = {};
      assert(false, validation(emptyObject));

      var emptyArray = [];
      assert(false, validation(emptyArray));
    },
    //this test is commented, because there is not "strict" mode yet
    /*
    "some fields are valid, all the rest are not": function () {
      var validation = schema({
        "+check.?" : Number
      });
      var correctObject = {
        check1 : 1,
        check2 : 2
      };
      assert(true, validation(correctObject));
      var incorrectObject = {
        check1 : 1,
        check2 : 2,
        someOther : 3
      };
      assert(false, validation(incorrectObject));
    },
    */
    "with allowed other fields": function () {
      var anotherValidation = schema({
        "+check.?": Number,
        "*": Number
      });
      var o1 = {
        check1: 1,
        check2: 2
      };
      var o2 = {
        check1: 1,
        check2: 2,
        someOther: 3
      };

      assert(true, anotherValidation(o1));
      assert(true, anotherValidation(o2));
    },
    "empty object should be valid sometimes": function () {
      var allowAll = schema({
        "*": String
      });
      assert(true, allowAll({}));

      var allowSome = schema({
        "check": [null, String]
      });
      assert(true, allowSome({}));
    }
  }
}).export(module)
<commit_msg>enable test back<commit_after>var vows            = require('vows')
  , assert          = require('assert')
  , printTestResult = require('../printTestResult.js')
  , schema          = require('../../index.js')

// Create a Test Suite
vows.describe('Validation Object with errors').addBatch({
  'Object': {
    'Invalid input : empty object': function() {
      var input = {
        object: {},
        number: 3
      };
      var B = function B() {};
      var instance = schema({
        func: Function,
        number: Number.min(6).max(9),
        boolean: Boolean,
        string: String,
        class: B,
        object: {
          a: Number.min(2).max(7),
          b: Function
        }
      });
      var result = instance.errors(input);
      console.log('Input : \n', vows.inspect(input));
      console.log('Errors : \n', vows.inspect(result));
      assert(result, printTestResult(input, result))
    },
    'Invalid nested input': function() {
      var input = {
        a: {
          aa: false,
          cc: false
        },
        d: true
      };
      var instance = schema({
        a: {
          aa: [Number.min(2), String],
          bb: {
            aaa: Boolean,
            bbb: Number,
            ccc: {
              aaaa: Boolean
            }
          },
          cc: Function
        },
        b: Number,
        c: Boolean,
        d: Number
      });
      var result = instance.errors(input);
      console.log('Input : \n', vows.inspect(input));
      console.log('Errors : \n', vows.inspect(result));
      assert(result, printTestResult(input, result))
    },
    "original validate schema has a bug with regexp field validation.": function () {
      //object should have at least one String field
      var validation = schema({
        "+.+" : String
      });
      var emptyObject = {};
      assert(false, validation(emptyObject));

      var emptyArray = [];
      assert(false, validation(emptyArray));
    },
    "some fields are valid, all the rest are not": function () {
      var validation = schema({
        "+check.?" : Number,
        "*" : null
      });
      var correctObject = {
        check1 : 1,
        check2 : 2
      };
      assert(true, validation(correctObject));
      var incorrectObject = {
        check1 : 1,
        check2 : 2,
        someOther : 3
      };
      assert(false, validation(incorrectObject));
    },
    "with allowed other fields": function () {
      var anotherValidation = schema({
        "+check.?": Number,
        "*": Number
      });
      var o1 = {
        check1: 1,
        check2: 2
      };
      var o2 = {
        check1: 1,
        check2: 2,
        someOther: 3
      };

      assert(true, anotherValidation(o1));
      assert(true, anotherValidation(o2));
    },
    "empty object should be valid sometimes": function () {
      var allowAll = schema({
        "*": String
      });
      assert(true, allowAll({}));

      var allowSome = schema({
        "check": [null, String]
      });
      assert(true, allowSome({}));
    }
  }
}).export(module)
<|endoftext|>


<commit_before>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetItemCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'PAGINATION', '$modal', '$timeout', '$location',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, PAGINATION, $modal, $timeout, $location) {
        var WidgetItem = this;
        WidgetItem.listeners = {};

        var currentView = ViewStack.getCurrentView();

        if (currentView.params && currentView.params.itemId && !currentView.params.stopSwitch) {
          buildfire.messaging.sendMessageToControl({
            id: currentView.params.itemId,
            type: 'OpenItem'
          });
        }

        buildfire.datastore.onRefresh(function () {
          // Do nothing
        });

        WidgetItem.getItemDetails = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item = result;
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error In Fetching Event', err);
            };

          if (currentView.params && currentView.params.itemId) {
            DataStore.getById(currentView.params.itemId, TAG_NAMES.COUPON_ITEMS).then(success, error);
          }
        };

        WidgetItem.safeHtml = function (html) {
          if (html) {
            var $html = $('<div />', {html: html});
            $html.find('iframe').each(function (index, element) {
              var src = element.src;
              console.log('element is: ', src, src.indexOf('http'));
              src = src && src.indexOf('file://') != -1 ? src.replace('file://', 'http://') : src;
              element.src = src && src.indexOf('http') != -1 ? src : 'http:' + src;
            });
            return $sce.trustAsHtml($html.html());
          }
        };

        WidgetItem.openLinks = function (actionItems) {
          if (actionItems && actionItems.length) {
            var options = {};
            var callback = function (error, result) {
              if (error) {
                console.error('Error:', error);
              }
            };
            buildfire.actionItems.list(actionItems, options, callback);
          }
        };

        WidgetItem.onAddressClick = function (long, lat) {
          if (WidgetItem.device && WidgetItem.device.platform == 'ios')
            buildfire.navigation.openWindow("maps://maps.google.com/maps?daddr=" + lat + "," + long, '_system');
          else
            buildfire.navigation.openWindow("http://maps.google.com/maps?daddr=" + lat + "," + long, '_system');
        };

        /**
         * This event listener is bound for "Carousel:LOADED" event broadcast
         */
        WidgetItem.listeners["CarouselLoaded"] = $rootScope.$on("Carousel2:LOADED", function () {
          //  WidgetItem.view = null;
          if (WidgetItem.view)
            WidgetItem.view._destroySlider();
          if (!WidgetItem.view) {
            WidgetItem.view = new Buildfire.components.carousel.view("#carousel2", []);
          }
          if (WidgetItem.item.data && WidgetItem.item.data.carouselImages) {
            WidgetItem.view.loadItems(WidgetItem.item.data.carouselImages);
          } else {
            WidgetItem.view.loadItems([]);
          }
        });

        /**
         * Check for current logged in user, if not show ogin screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          console.log("===========LoggedInUser", user);
          if (user) {
            WidgetItem.currentLoggedInUser = user;
          }
        });

        $scope.$on("$destroy", function () {
          for (var i in WidgetItem.listeners) {
            if (WidgetItem.listeners.hasOwnProperty(i)) {
              WidgetItem.listeners[i]();
            }
          }
          DataStore.clearListener();
        });

        WidgetItem.setSavedItem = function () {
          if (WidgetItem.item) {
            for (var save in WidgetItem.saved) {
              if (WidgetItem.saved[save].data.itemId == WidgetItem.item.id) {
                WidgetItem.item.isSaved = true;
                WidgetItem.item.savedId = WidgetItem.saved[save].id;
              }
            }
          }
        };

        WidgetItem.setRedeemedItem = function () {
          if (WidgetItem.item) {
            for (var redeem in WidgetItem.redeemed) {
              if (WidgetItem.redeemed[redeem].data.itemId == WidgetItem.item.id) {
                WidgetItem.item.isRedeemed = true;
                WidgetItem.item.redeemedOn = WidgetItem.redeemed[redeem].data.redeemedOn;
              }
            }
          }
        };

        WidgetItem.getSavedItems = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting saved items data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetItem.saved = result;
            WidgetItem.setSavedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
        };

        WidgetItem.getRedeemedCoupons = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting redeemed coupons data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetItem.redeemed = result;
            WidgetItem.setRedeemedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_REDEEMED).then(result, err);
        };

        WidgetItem.addToSaved = function (item, isSaved, onlyAdd) {
          Buildfire.spinner.show();
          if (isSaved && item.savedId && !onlyAdd) {
            var successRemove = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isSaved = false;
              WidgetItem.item.savedId = null;
              if (!$scope.$$phase)
                $scope.$digest();
              var removeSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Removed.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                removeSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorRemove = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem removing your data');
            };
            UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetItem.currentLoggedInUser._id).then(successRemove, errorRemove)
          } else {
            WidgetItem.savedItem = {
              data: {
                itemId: item.id
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isSaved = true;
              WidgetItem.item.savedId = result.id;
              console.log("Inserted", result);
              var addedSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                addedSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");
            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem saving your data');
            };
            UserData.insert(WidgetItem.savedItem.data, TAG_NAMES.COUPON_SAVED).then(successItem, errorItem);
          }
        };

        var loginCallback = function () {
          buildfire.auth.getCurrentUser(function (err, user) {
            if (user) {
              WidgetItem.currentLoggedInUser = user;
              $scope.$apply();
              WidgetItem.getSavedItems();
              WidgetItem.getRedeemedCoupons();
            }
          });
        };

        buildfire.auth.onLogin(loginCallback);

        WidgetItem.redeemCoupon = function(item){
          if(WidgetItem.currentLoggedInUser){
            WidgetItem.redeemedItem = {
              data: {
                itemId: item.id,
                redeemedOn: +new Date()
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isRedeemed = true;
              WidgetItem.item.redeemedOn = result.data.redeemedOn;
              var redeemedModal = $modal.open({
                templateUrl: 'templates/Redeem_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              WidgetItem.addToSaved(WidgetItem.item, WidgetItem.item.isSaved, true);
              $timeout(function () {
                redeemedModal.close();
              }, 2000);
            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem redeeming the coupon');
            };
            UserData.insert(WidgetItem.redeemedItem.data, TAG_NAMES.COUPON_REDEEMED).then(successItem, errorItem);

          }else{
            buildfire.auth.login({}, function () {

            });
          }
        };

        var onUpdateCallback = function (event) {
          setTimeout(function () {
            $scope.$digest();
            if (event && event.tag) {
              console.log("_____________________________", event);
              switch (event.tag) {
                case TAG_NAMES.COUPON_INFO:
                  WidgetItem.data = event.data;
                  if (!WidgetItem.data.design)
                    WidgetItem.data.design = {};
                  if (!WidgetItem.data.settings)
                    WidgetItem.data.settings = {};
                  break;
                case TAG_NAMES.COUPON_ITEMS:
                  if (event.data) {
                    WidgetItem.item.data = event.data;
                    if (WidgetItem.view) {
                      WidgetItem.view.loadItems(WidgetItem.item.data.carouselImages);
                    }
                  }
                  break;
              }
              $scope.$digest();
            }
          }, 500);
        };

        DataStore.onUpdate("item").then(null, null, onUpdateCallback);

        /*
         * Fetch user's data from datastore
         */
        var init = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.data = result.data;
              if (!WidgetItem.data.design)
                WidgetItem.data.design = {};
              WidgetItem.getItemDetails();
              var getDevice = function (error, data) {
                if (data)
                  WidgetItem.device = data.device;
                else
                  console.log("Error while getting the device context data", error)
              };
              buildfire.getContext(getDevice);
              if(WidgetItem.currentLoggedInUser){
                WidgetItem.getSavedItems();
                WidgetItem.getRedeemedCoupons();
              }
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);
        };

        $scope.$on("$destroy", function() {

          for(var i in WidgetItem.listeners) {
            if(WidgetItem.listeners.hasOwnProperty(i)) {
              WidgetItem.listeners[i]();
            }
          }
          DataStore.clearListener("item");

        });

        init();
      }]);
})(window.angular, window.buildfire, window);
<commit_msg>Added a check in case get currentUser method takes time, call again to get redeemed coupon details<commit_after>'use strict';

(function (angular, buildfire, window) {
  angular.module('couponPluginWidget')
    .controller('WidgetItemCtrl', ['$scope', 'DataStore', 'TAG_NAMES', 'LAYOUTS', '$sce', '$rootScope', 'Buildfire', 'ViewStack', 'UserData', 'PAGINATION', '$modal', '$timeout', '$location',
      function ($scope, DataStore, TAG_NAMES, LAYOUTS, $sce, $rootScope, Buildfire, ViewStack, UserData, PAGINATION, $modal, $timeout, $location) {
        var WidgetItem = this;
        WidgetItem.listeners = {};

        var currentView = ViewStack.getCurrentView();

        if (currentView.params && currentView.params.itemId && !currentView.params.stopSwitch) {
          buildfire.messaging.sendMessageToControl({
            id: currentView.params.itemId,
            type: 'OpenItem'
          });
        }

        buildfire.datastore.onRefresh(function () {
          // Do nothing
        });

        WidgetItem.getItemDetails = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item = result;
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error In Fetching Event', err);
            };

          if (currentView.params && currentView.params.itemId) {
            DataStore.getById(currentView.params.itemId, TAG_NAMES.COUPON_ITEMS).then(success, error);
          }
        };

        WidgetItem.safeHtml = function (html) {
          if (html) {
            var $html = $('<div />', {html: html});
            $html.find('iframe').each(function (index, element) {
              var src = element.src;
              console.log('element is: ', src, src.indexOf('http'));
              src = src && src.indexOf('file://') != -1 ? src.replace('file://', 'http://') : src;
              element.src = src && src.indexOf('http') != -1 ? src : 'http:' + src;
            });
            return $sce.trustAsHtml($html.html());
          }
        };

        WidgetItem.openLinks = function (actionItems) {
          if (actionItems && actionItems.length) {
            var options = {};
            var callback = function (error, result) {
              if (error) {
                console.error('Error:', error);
              }
            };
            buildfire.actionItems.list(actionItems, options, callback);
          }
        };

        WidgetItem.onAddressClick = function (long, lat) {
          if (WidgetItem.device && WidgetItem.device.platform == 'ios')
            buildfire.navigation.openWindow("maps://maps.google.com/maps?daddr=" + lat + "," + long, '_system');
          else
            buildfire.navigation.openWindow("http://maps.google.com/maps?daddr=" + lat + "," + long, '_system');
        };

        /**
         * This event listener is bound for "Carousel:LOADED" event broadcast
         */
        WidgetItem.listeners["CarouselLoaded"] = $rootScope.$on("Carousel2:LOADED", function () {
          //  WidgetItem.view = null;
          if (WidgetItem.view)
            WidgetItem.view._destroySlider();
          if (!WidgetItem.view) {
            WidgetItem.view = new Buildfire.components.carousel.view("#carousel2", []);
          }
          if (WidgetItem.item.data && WidgetItem.item.data.carouselImages) {
            WidgetItem.view.loadItems(WidgetItem.item.data.carouselImages);
          } else {
            WidgetItem.view.loadItems([]);
          }
        });

        /**
         * Check for current logged in user, if not show ogin screen
         */
        buildfire.auth.getCurrentUser(function (err, user) {
          console.log("===========LoggedInUser", user);
          if (user) {
            WidgetItem.currentLoggedInUser = user;
          }
        });

        $scope.$on("$destroy", function () {
          for (var i in WidgetItem.listeners) {
            if (WidgetItem.listeners.hasOwnProperty(i)) {
              WidgetItem.listeners[i]();
            }
          }
          DataStore.clearListener();
        });

        WidgetItem.setSavedItem = function () {
          if (WidgetItem.item) {
            for (var save in WidgetItem.saved) {
              if (WidgetItem.saved[save].data.itemId == WidgetItem.item.id) {
                WidgetItem.item.isSaved = true;
                WidgetItem.item.savedId = WidgetItem.saved[save].id;
              }
            }
          }
        };

        WidgetItem.setRedeemedItem = function () {
          if (WidgetItem.item) {
            for (var redeem in WidgetItem.redeemed) {
              if (WidgetItem.redeemed[redeem].data.itemId == WidgetItem.item.id) {
                WidgetItem.item.isRedeemed = true;
                WidgetItem.item.redeemedOn = WidgetItem.redeemed[redeem].data.redeemedOn;
              }
            }
          }
        };

        WidgetItem.getSavedItems = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting saved items data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetItem.saved = result;
            WidgetItem.setSavedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_SAVED).then(result, err);
        };

        WidgetItem.getRedeemedCoupons = function () {
          Buildfire.spinner.show();
          var err = function (error) {
            Buildfire.spinner.hide();
            console.log("============ There is an error in getting redeemed coupons data", error);
          }, result = function (result) {
            Buildfire.spinner.hide();
            WidgetItem.redeemed = result;
            WidgetItem.setRedeemedItem();
          };
          UserData.search({}, TAG_NAMES.COUPON_REDEEMED).then(result, err);
        };

        WidgetItem.addToSaved = function (item, isSaved, onlyAdd) {
          Buildfire.spinner.show();
          if (isSaved && item.savedId && !onlyAdd) {
            var successRemove = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isSaved = false;
              WidgetItem.item.savedId = null;
              if (!$scope.$$phase)
                $scope.$digest();
              var removeSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Removed.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                removeSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");

            }, errorRemove = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem removing your data');
            };
            UserData.delete(item.savedId, TAG_NAMES.COUPON_SAVED, WidgetItem.currentLoggedInUser._id).then(successRemove, errorRemove)
          } else {
            WidgetItem.savedItem = {
              data: {
                itemId: item.id
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isSaved = true;
              WidgetItem.item.savedId = result.id;
              console.log("Inserted", result);
              var addedSavedModal = $modal.open({
                templateUrl: 'templates/Saved_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              $timeout(function () {
                addedSavedModal.close();
              }, 3000);
              $rootScope.$broadcast("ITEM_SAVED_UPDATED");
            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem saving your data');
            };
            UserData.insert(WidgetItem.savedItem.data, TAG_NAMES.COUPON_SAVED).then(successItem, errorItem);
          }
        };

        var loginCallback = function () {
          buildfire.auth.getCurrentUser(function (err, user) {
            if (user) {
              WidgetItem.currentLoggedInUser = user;
              $scope.$apply();
              WidgetItem.getSavedItems();
              WidgetItem.getRedeemedCoupons();
            }
          });
        };

        buildfire.auth.onLogin(loginCallback);

        WidgetItem.redeemCoupon = function(item){
          if(WidgetItem.currentLoggedInUser){
            WidgetItem.redeemedItem = {
              data: {
                itemId: item.id,
                redeemedOn: +new Date()
              }
            };
            var successItem = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.item.isRedeemed = true;
              WidgetItem.item.redeemedOn = result.data.redeemedOn;
              var redeemedModal = $modal.open({
                templateUrl: 'templates/Redeem_Confirmation.html',
                size: 'sm',
                backdropClass: "ng-hide"
              });
              WidgetItem.addToSaved(WidgetItem.item, WidgetItem.item.isSaved, true);
              $timeout(function () {
                redeemedModal.close();
              }, 2000);
            }, errorItem = function () {
              Buildfire.spinner.hide();
              return console.error('There was a problem redeeming the coupon');
            };
            UserData.insert(WidgetItem.redeemedItem.data, TAG_NAMES.COUPON_REDEEMED).then(successItem, errorItem);

          }else{
            buildfire.auth.login({}, function () {

            });
          }
        };

        var onUpdateCallback = function (event) {
          setTimeout(function () {
            $scope.$digest();
            if (event && event.tag) {
              console.log("_____________________________", event);
              switch (event.tag) {
                case TAG_NAMES.COUPON_INFO:
                  WidgetItem.data = event.data;
                  if (!WidgetItem.data.design)
                    WidgetItem.data.design = {};
                  if (!WidgetItem.data.settings)
                    WidgetItem.data.settings = {};
                  break;
                case TAG_NAMES.COUPON_ITEMS:
                  if (event.data) {
                    WidgetItem.item.data = event.data;
                    if (WidgetItem.view) {
                      WidgetItem.view.loadItems(WidgetItem.item.data.carouselImages);
                    }
                  }
                  break;
              }
              $scope.$digest();
            }
          }, 500);
        };

        DataStore.onUpdate("item").then(null, null, onUpdateCallback);

        /*
         * Fetch user's data from datastore
         */
        var init = function () {
          Buildfire.spinner.show();
          var success = function (result) {
              Buildfire.spinner.hide();
              WidgetItem.data = result.data;
              if (!WidgetItem.data.design)
                WidgetItem.data.design = {};
              WidgetItem.getItemDetails();
              var getDevice = function (error, data) {
                if (data)
                  WidgetItem.device = data.device;
                else
                  console.log("Error while getting the device context data", error)
              };
              buildfire.getContext(getDevice);
              if(WidgetItem.currentLoggedInUser){
                WidgetItem.getSavedItems();
                WidgetItem.getRedeemedCoupons();
              }
            else {
                buildfire.auth.getCurrentUser(function (err, user) {
                  if (user) {
                    WidgetItem.currentLoggedInUser = user;
                    $scope.$apply();
                    WidgetItem.getSavedItems();
                    WidgetItem.getRedeemedCoupons();
                  }
                });
              }
            }
            , error = function (err) {
              Buildfire.spinner.hide();
              console.error('Error while getting data', err);
            };
          DataStore.get(TAG_NAMES.COUPON_INFO).then(success, error);
        };

        $scope.$on("$destroy", function() {

          for(var i in WidgetItem.listeners) {
            if(WidgetItem.listeners.hasOwnProperty(i)) {
              WidgetItem.listeners[i]();
            }
          }
          DataStore.clearListener("item");

        });

        init();
      }]);
})(window.angular, window.buildfire, window);
<|endoftext|>


<commit_before>module.exports = {
  validate: {
    // only validate on these MIME types
    contentType: /(text\/html|application\/xhtml\+xml)/,
    fetch: {
      // this pattern will match the obvious file extensions
      // and treat directories with a trailing slash as index pages
      // so that they are also fetched
      file: /(\.(htm|html|xhtml|xht)|(\/))$/i,

      // this will catch paths without a trailing slash and no
      // file extension, this pattern is negated in the fetch condition!
      directory: /\..+$/i
    }
  },
  crawl: {
    downloadUnsupported: true,
    userAgent: 'Linkdown',
    // decrease interval and concurrency as normally
    // executing against local server, adjust for your needs
    interval: 200,
    maxConcurrency: 25
  }
}
<commit_msg>Set downloadUnsupported to false.<commit_after>module.exports = {
  validate: {
    // only validate on these MIME types
    contentType: /(text\/html|application\/xhtml\+xml)/,
    fetch: {
      // this pattern will match the obvious file extensions
      // and treat directories with a trailing slash as index pages
      // so that they are also fetched
      file: /(\.(htm|html|xhtml|xht)|(\/))$/i,

      // this will catch paths without a trailing slash and no
      // file extension, this pattern is negated in the fetch condition!
      directory: /\..+$/i
    }
  },
  crawl: {
    downloadUnsupported: false,
    userAgent: 'Linkdown',
    // decrease interval and concurrency as normally
    // executing against local server, adjust for your needs
    interval: 200,
    maxConcurrency: 25
  }
}
<|endoftext|>


<commit_before>self.deprecationWorkflow = self.deprecationWorkflow || {};
self.deprecationWorkflow.config = {
  workflow: [
    { handler: "silence", matchMessage: "Use of `assign` has been deprecated. Please use `Object.assign` or the spread operator instead." },
  ]
};
<commit_msg>Update deprecation-workflow.js<commit_after>window.deprecationWorkflow = window.deprecationWorkflow || {};
window.deprecationWorkflow.config = {
  workflow: [
    {
      handler: 'silence',
      matchMessage:
        'Use of `assign` has been deprecated. Please use `Object.assign` or the spread operator instead.',
    },
  ],
};
<|endoftext|>


<commit_before>import {
    getInventorySource,
    getJobTemplate,
    getProject,
    getWorkflowTemplate
} from '../fixtures';

let data;
const spinny = "//*[contains(@class, 'spinny')]";
const workflowTemplateNavTab = "//at-side-nav-item[contains(@name, 'TEMPLATES')]";
const workflowSelector = "//a[contains(text(), 'test-actions-workflow-template')]";
const workflowVisualizerBtn = "//button[contains(@id, 'workflow_job_template_workflow_visualizer_btn')]";

const rootNode = "//*[@id='node-2']";
const childNode = "//*[@id='node-3']";
const newChildNode = "//*[@id='node-5']";
const leafNode = "//*[@id='node-6']";
const nodeAdd = "//*[contains(@class, 'nodeAddCross')]";
const nodeRemove = "//*[contains(@class, 'nodeRemoveCross')]";

// one of the jobs or projects or inventories
const testActionsProject = "//td[contains(text(), 'test-actions-project')]";
const testActionsJob = "//td[contains(text(), 'test-actions-job')]";

// dropdown bar which lets you select edge type
const edgeTypeDropdownBar = "//span[contains(@id, 'select2-workflow_node_edge-container')]";
const alwaysDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='Always']";
const successDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='On Success']";
const failureDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='On Failure']";
const selectButton = "//*[@id='workflow_maker_select_btn']";
const deleteConfirmation = "//button[@ng-click='confirmDeleteNode()']";

module.exports = {
    before: (client, done) => {
        const resources = [
            getInventorySource('test-actions'),
            getJobTemplate('test-actions'),
            getProject('test-actions'),
            getWorkflowTemplate('test-actions'),
        ];

        Promise.all(resources)
            .then(([source, template, project, workflow]) => {
                data = { source, template, project, workflow };
                done();
            });
        client
            .login()
            .waitForAngular()
            .resizeWindow(1200, 1000)
            .useXpath()
            .findThenClick(workflowTemplateNavTab)
            .pause(1500)
            .waitForElementNotVisible(spinny)
            .findThenClick(workflowSelector)
            .findThenClick(workflowVisualizerBtn);
    },
    'verify that workflow visualizer root node can only be set to always': client => {
        client
            .useXpath()
            .findThenClick(rootNode)
            .findThenClick(testActionsProject)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementNotPresent(successDropdown)
            .waitForElementNotPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown);
    },
    'verify that a non-root node can be set to always/success/failure': client => {
        client
            .useXpath()
            .findThenClick(childNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(edgeTypeDropdownBar);
    },
    'verify that a sibling node can be any edge type': client => {
        client
            .useXpath()
            .moveToElement(childNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                // Concatenating the xpaths lets us click the proper node
                client.click(childNode + nodeAdd);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(testActionsJob)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(alwaysDropdown)
            .click(selectButton);
    },
    'Verify node-shifting behavior upon deletion': client => {
        client
            .findThenClick(newChildNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .findThenClick(successDropdown)
            .click(selectButton)
            .moveToElement(newChildNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                client.click(newChildNode + nodeAdd);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(testActionsJob)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(alwaysDropdown)
            .click(selectButton)
            .moveToElement(newChildNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                client.click(newChildNode + nodeRemove);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(deleteConfirmation)
            .findThenClick(leafNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown);
    },
    after: client => {
        client.end();
    }
};
<commit_msg>added search for visualizer nodes<commit_after>import {
    getInventorySource,
    getJobTemplate,
    getProject,
    getWorkflowTemplate
} from '../fixtures';

let data;
const spinny = "//*[contains(@class, 'spinny')]";
const workflowTemplateNavTab = "//at-side-nav-item[contains(@name, 'TEMPLATES')]";
const workflowSelector = "//a[contains(text(), 'test-actions-workflow-template')]";
const workflowVisualizerBtn = "//button[contains(@id, 'workflow_job_template_workflow_visualizer_btn')]";

const rootNode = "//*[@id='node-2']";
const childNode = "//*[@id='node-3']";
const newChildNode = "//*[@id='node-5']";
const leafNode = "//*[@id='node-6']";
const nodeAdd = "//*[contains(@class, 'nodeAddCross')]";
const nodeRemove = "//*[contains(@class, 'nodeRemoveCross')]";

// one of the jobs or projects or inventories
const testActionsProject = "//td[contains(text(), 'test-actions-project')]";
const testActionsJob = "//td[contains(text(), 'test-actions-job')]";
const testActionsProjectText = "test-actions-project";
const testActionsJobText = "test-actions-job";

// search bar for visualizer templates
const jobSearchBar = "//*[contains(@id, 'workflow-jobs-list')]//input[contains(@class, 'SmartSearch-input')]";
const projectSearchBar = "//*[contains(@id, 'workflow-project-sync-list')]//input[contains(@class, 'SmartSearch-input')]";

// dropdown bar which lets you select edge type
const edgeTypeDropdownBar = "//span[contains(@id, 'select2-workflow_node_edge-container')]";
const alwaysDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='Always']";
const successDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='On Success']";
const failureDropdown = "//li[contains(@id, 'select2-workflow_node_edge') and text()='On Failure']";
const selectButton = "//*[@id='workflow_maker_select_btn']";
const deleteConfirmation = "//button[@ng-click='confirmDeleteNode()']";

module.exports = {
    before: (client, done) => {
        const resources = [
            getInventorySource('test-actions'),
            getJobTemplate('test-actions'),
            getProject('test-actions'),
            getWorkflowTemplate('test-actions'),
        ];

        Promise.all(resources)
            .then(([source, template, project, workflow]) => {
                data = { source, template, project, workflow };
                done();
            });
        client
            .login()
            .waitForAngular()
            .resizeWindow(1200, 1000)
            .useXpath()
            .findThenClick(workflowTemplateNavTab)
            .pause(1500)
            .waitForElementNotVisible(spinny)
            .findThenClick(workflowSelector)
            .findThenClick(workflowVisualizerBtn);
    },
    'verify that workflow visualizer root node can only be set to always': client => {
        client
            .useXpath()
            .findThenClick(rootNode)
            .clearValue(projectSearchBar)
            .setValue(projectSearchBar, [testActionsProjectText, client.Keys.ENTER])
            .pause(1000)
            .findThenClick(testActionsProject)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementNotPresent(successDropdown)
            .waitForElementNotPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown);
    },
    'verify that a non-root node can be set to always/success/failure': client => {
        client
            .useXpath()
            .findThenClick(childNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(edgeTypeDropdownBar);
    },
    'verify that a sibling node can be any edge type': client => {
        client
            .useXpath()
            .moveToElement(childNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                // Concatenating the xpaths lets us click the proper node
                client.click(childNode + nodeAdd);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .clearValue(jobSearchBar)
            .setValue(jobSearchBar, [testActionsJobText, client.Keys.ENTER])
            .pause(1000)
            .findThenClick(testActionsJob)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(alwaysDropdown)
            .click(selectButton);
    },
    'Verify node-shifting behavior upon deletion': client => {
        client
            .findThenClick(newChildNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .findThenClick(successDropdown)
            .click(selectButton)
            .moveToElement(newChildNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                client.click(newChildNode + nodeAdd);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .clearValue(jobSearchBar)
            .setValue(jobSearchBar, [testActionsJobText, client.Keys.ENTER])
            .pause(1000)
            .findThenClick(testActionsJob)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown)
            .findThenClick(alwaysDropdown)
            .click(selectButton)
            .moveToElement(newChildNode, 0, 0, () => {
                client.pause(500);
                client.waitForElementNotVisible(spinny);
                client.click(newChildNode + nodeRemove);
            })
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(deleteConfirmation)
            .findThenClick(leafNode)
            .pause(1000)
            .waitForElementNotVisible(spinny)
            .findThenClick(edgeTypeDropdownBar)
            .waitForElementPresent(successDropdown)
            .waitForElementPresent(failureDropdown)
            .waitForElementPresent(alwaysDropdown);
    },
    after: client => {
        client.end();
    }
};
<|endoftext|>


<commit_before>'use strict';
const AWS = require('aws-sdk');
const fs = require('fs');
const Promise = require('bluebird');
const encoding = 'utf8';

// Default values
let DynamicFile = '.i18n/dynamic.json';
let LogFolder = '.i18n';

function getFiles (rootDirectory) {
  // Scan through entire folder and get all file names
  const directories = [rootDirectory];
  const files = [];

  while (directories.length) {
    const dir = directories.shift();
    const res = fs.readdirSync(dir).map(x => dir + '/' + x);

    res.forEach(x => {
      const stat = fs.statSync(x);
      if (stat.isFile()) {
        files.push(x);
      } else if (stat.isDirectory()) {
        directories.push(x);
      }
    });
  }

  // Filter files based on fileType
  const fileType = '.js';
  const jsFiles = files.filter(x => x.endsWith(fileType));
  console.log(jsFiles.length + ' ' + fileType + ' files found.');

  // Get files' contents
  return jsFiles.map(x => fs.readFileSync(x, encoding));
}

function getMessages (contents) {
  const regex = /<FormattedH?T?M?L?Message[^<>]+?(?:<[^<>]+>[^<>]+)*(?:=>[^<>]+)*(?:<[^<>]+>[^<>]+)*>/g;
  const idRegex = /id="[^"]+"|id={[^{}]+}|id={`[\s\S]+?`}/g;
  const defaultMessageRegex = /defaultMessage="[^"]+"|defaultMessage={[^{}]+}|defaultMessage={`[\s\S]+?`}/g;

  // Get all formatted messages
  let messages = [], values = [];
  contents.forEach(x => {
    const matches = x.match(regex);
    if (matches) {
      messages = messages.concat(matches);
    }
  });

  // Remove white spaces, outer layer and values from formatted messages
  messages = messages
    .map(x => {
      const y = x.split('\n').map(y => y.trim()).join(' ').split('\' + \'').join('');
      const i = y.startsWith('<FormattedMessage') ? 17 : (x.startsWith('<FormattedHTMLMessage') ? 21 : 0);
      const j = y.endsWith('/>') ? y.length-2 : y.length-1;
      const z = y.substring(i,j).split('values={');
      if (z[1]) values.push(z[1]);
      return z[0].trim();
    });

  // Get formatted messages from values
  values.forEach(x => {
    const msg = x.match(regex);
    if (msg) {
      msg.forEach(y => {
        const i = y.startsWith('<FormattedMessage') ? 17 : (x.startsWith('<FormattedHTMLMessage') ? 21 : 0);
        const j = y.endsWith('/>') ? y.length-2 : y.length-1;
        messages.push(y.substring(i,j).trim());
      });
    }
  });

  console.log(messages.length + ' formatted messages found.');

  // Get id and defaultMessage from formatted messages
  const messageJSON = {}, check = {};
  messages.forEach(x => {
    let id = x.match(idRegex)[0];
    let msg = x.match(defaultMessageRegex)[0];
    let type = 'static';

    if (id.startsWith('id="')) {
      id = id.substring(4,id.length-1);
    } else if (id.startsWith('id={`')) {
      id = id.substring(5,id.length-2);
      type = 'dynamic';
    } else if (id.startsWith('id={')) {
      id = id.substring(4,id.length-1);
      type = 'dynamic';
    }

    if (msg.startsWith('defaultMessage="')) {
      msg = msg.substring(16,msg.length-1);
    } else if (msg.startsWith('defaultMessage={`')) {
      msg = msg.substring(17,msg.length-2);
    } else if (msg.startsWith('defaultMessage={')) {
      msg = msg.substring(16,msg.length-1);
    }

    messageJSON[id] = {
      id: id,
      defMsg: msg,
      type: type
    };

    if (check[id]) {
      if (check[id].indexOf(msg) === -1) {
        check[id].push(msg);
      }
    }
    else {
      check[id] = [msg];
    }
  });

  // If there are repeated id with different messages, throw error
  const repeat = Object.keys(check).map(x => ({id: x, msgs: check[x]})).filter(x => x.msgs.length > 1);
  if (repeat.length) {
    fs.writeFileSync(LogFolder + '/repeated.json', JSON.stringify(repeat, null, 2));
    throw 'There are repeated id with different default messages! Refer ' + LogFolder + '/repeated.json for more information.';
  }

  console.log(Object.keys(messageJSON).length + ' id found.');
  return messageJSON;
}

function reserve (rootDirectory) {
  const messages = getMessages(getFiles(rootDirectory));
  const dynamic = {};
  Object.keys(messages).forEach(x => {
    if (messages[x].type === 'dynamic') {
      dynamic[messages[x].id] = messages[x].defMsg;
    }
  });

  fs.writeFileSync(DynamicFile, JSON.stringify(dynamic, null, 2));
  console.log(DynamicFile + ' is created.');
}

function collate (rootDirectory, configFile, reserveFile) {
  // Filter and get static id and default messages
  const messages = getMessages(getFiles(rootDirectory));
  const results = {};
  Object.keys(messages).forEach(x => {
    if (messages[x].type === 'static') {
      results[messages[x].id] = messages[x].defMsg;
    }
  });

  console.log(Object.keys(results).length + ' static id found.');

  // Get reserve list
  const reserve = {};
  JSON.parse(fs.readFileSync(reserveFile, encoding)).forEach(x => {
    Object.keys(x.translationPairs).forEach(y => {
      if (reserve[y]) {
        console.log(y);
        throw 'The above is the repeated id in the given reserve file!'
      } else {
        reserve[y] = x.translationPairs[y];
      }
    });
  });

  // If there are repeated id between reserve and static, throw error
  const reserveIds = Object.keys(reserve);
  const repeat = reserveIds
    .map(x => ({
      id: x,
      reserveMsg: reserve[x],
      staticMsg: results[x]
    }))
    .filter(x => x.staticMsg);
  if (repeat.length) {
    fs.writeFileSync(LogFolder + '/repeatedReserveStatic.json', JSON.stringify(repeat, null, 2));
    throw 'There are repeated id between reserve and static! Refer ' + LogFolder + '/repeatedReserveStatic.json for more information.';
  }

  // AWS configurations
  const config = JSON.parse(fs.readFileSync(configFile, encoding));
  AWS.config.region = config.region;
  AWS.config.accessKeyId = config.accessKeyId;
  AWS.config.secretAccessKey = config.secretAccessKey;

  // Get list of locales from s3 bucket
  let locales;
  const s3 = new Promise.promisifyAll(new AWS.S3());
  s3.listObjectsV2Async({
    Bucket: config.bucket,
    Delimiter: '/',
    Prefix: config.folder + '/'
  })
    .then(data => {
      locales = data.Contents.filter(x => x.Key.endsWith('.json')).map(x => x.Key);
      console.log('locales: ' + locales);

      // Get list of files from s3 bucket
      return Promise.all(locales
        .map(x => s3.getObjectAsync({
          Bucket: config.bucket,
          Key: x
        })));
    })
    .then(data => {
      // Generate latest json object for each locale
      const ids = Object.keys(results);
      const files = data.map((x, i) => {
        const key = locales[i];
        const en = key.endsWith('en.json');
        const old = JSON.parse(x.Body.toString());
        const body = {};

        ids.forEach(id => {
          if (old[id]) {
            body[id] = old[id];
          } else {
            body[id] = en ? results[id] : '';
          }
        });

        reserveIds.forEach(id => {
          if (old[id]) {
            body[id] = old[id];
          } else {
            body[id] = en ? reserve[id] : '';
          }
        });

        return {
          key: key,
          body: JSON.stringify(body)
        };
      });

      // Upload list of files to s3 bucket
      return Promise.all(files
        .map((x,i) => s3.putObjectAsync({
          ACL: config.acl,
          Bucket: config.bucket,
          Key: x.key,
          Body: x.body,
          ContentType: 'application/json'
        })));
    })
    .then(() => console.log('s3 files are updated!'))
}

function error () {
  console.log('Your input is wrong!');
  console.log('Here\' the proper format: glints-collate-message command rootDirectory -c configFile -r reserveFile');
  console.log('command: reserve/collate');
  console.log('Example: ');
  console.log('$ glints-collate-message reserve app');
  console.log('$ glints-collate-message collate app -c config.json -r reserve.json');
}

module.exports = function (command, rootDirectory, configFile, reserveFile, dynamicFile, logFolder) {
  if (dynamicFile) DynamicFile = dynamicFile;
  if (logFolder) LogFolder = logFolder;

  switch (command) {
    case 'reserve':
      reserve(rootDirectory);
      break;
    case 'collate':
      if (!configFile || !reserveFile) error();
      else collate(rootDirectory, configFile, reserveFile);
      break;
    default:
      error();
      break;
  }
};<commit_msg>Minor typo corrections<commit_after>'use strict';
const AWS = require('aws-sdk');
const fs = require('fs');
const Promise = require('bluebird');
const encoding = 'utf8';

// Default values
let DynamicFile = '.i18n/dynamic.json';
let LogFolder = '.i18n';

function getFiles (rootDirectory) {
  // Scan through entire folder and get all file names
  const directories = [rootDirectory];
  const files = [];

  while (directories.length) {
    const dir = directories.shift();
    const res = fs.readdirSync(dir).map(x => dir + '/' + x);

    res.forEach(x => {
      const stat = fs.statSync(x);
      if (stat.isFile()) {
        files.push(x);
      } else if (stat.isDirectory()) {
        directories.push(x);
      }
    });
  }

  // Filter files based on fileType
  const fileType = '.js';
  const jsFiles = files.filter(x => x.endsWith(fileType));
  console.log(jsFiles.length + ' ' + fileType + ' file(s) found.');

  // Get files' contents
  return jsFiles.map(x => fs.readFileSync(x, encoding));
}

function getMessages (contents) {
  const regex = /<FormattedH?T?M?L?Message[^<>]+?(?:<[^<>]+>[^<>]+)*(?:=>[^<>]+)*(?:<[^<>]+>[^<>]+)*>/g;
  const idRegex = /id="[^"]+"|id={[^{}]+}|id={`[\s\S]+?`}/g;
  const defaultMessageRegex = /defaultMessage="[^"]+"|defaultMessage={[^{}]+}|defaultMessage={`[\s\S]+?`}/g;

  // Get all formatted messages
  let messages = [], values = [];
  contents.forEach(x => {
    const matches = x.match(regex);
    if (matches) {
      messages = messages.concat(matches);
    }
  });

  // Remove white spaces, outer layer and values from formatted messages
  messages = messages
    .map(x => {
      const y = x.split('\n').map(y => y.trim()).join(' ').split('\' + \'').join('');
      const i = y.startsWith('<FormattedMessage') ? 17 : (x.startsWith('<FormattedHTMLMessage') ? 21 : 0);
      const j = y.endsWith('/>') ? y.length-2 : y.length-1;
      const z = y.substring(i,j).split('values={');
      if (z[1]) values.push(z[1]);
      return z[0].trim();
    });

  // Get formatted messages from values
  values.forEach(x => {
    const msg = x.match(regex);
    if (msg) {
      msg.forEach(y => {
        const i = y.startsWith('<FormattedMessage') ? 17 : (x.startsWith('<FormattedHTMLMessage') ? 21 : 0);
        const j = y.endsWith('/>') ? y.length-2 : y.length-1;
        messages.push(y.substring(i,j).trim());
      });
    }
  });

  console.log(messages.length + ' formatted message(s) found.');

  // Get id and defaultMessage from formatted messages
  const messageJSON = {}, check = {};
  messages.forEach(x => {
    let id = x.match(idRegex)[0];
    let msg = x.match(defaultMessageRegex)[0];
    let type = 'static';

    if (id.startsWith('id="')) {
      id = id.substring(4,id.length-1);
    } else if (id.startsWith('id={`')) {
      id = id.substring(5,id.length-2);
      type = 'dynamic';
    } else if (id.startsWith('id={')) {
      id = id.substring(4,id.length-1);
      type = 'dynamic';
    }

    if (msg.startsWith('defaultMessage="')) {
      msg = msg.substring(16,msg.length-1);
    } else if (msg.startsWith('defaultMessage={`')) {
      msg = msg.substring(17,msg.length-2);
    } else if (msg.startsWith('defaultMessage={')) {
      msg = msg.substring(16,msg.length-1);
    }

    messageJSON[id] = {
      id: id,
      defMsg: msg,
      type: type
    };

    if (check[id]) {
      if (check[id].indexOf(msg) === -1) {
        check[id].push(msg);
      }
    }
    else {
      check[id] = [msg];
    }
  });

  // If there are repeated id with different messages, throw error
  const repeat = Object.keys(check).map(x => ({id: x, msgs: check[x]})).filter(x => x.msgs.length > 1);
  if (repeat.length) {
    fs.writeFileSync(LogFolder + '/repeated.json', JSON.stringify(repeat, null, 2));
    throw 'There are repeated id with different default messages! Refer ' + LogFolder + '/repeated.json for more information.';
  }

  console.log(Object.keys(messageJSON).length + ' id(s) found.');
  return messageJSON;
}

function reserve (rootDirectory) {
  const messages = getMessages(getFiles(rootDirectory));
  const dynamic = {};
  Object.keys(messages).forEach(x => {
    if (messages[x].type === 'dynamic') {
      dynamic[messages[x].id] = messages[x].defMsg;
    }
  });

  fs.writeFileSync(DynamicFile, JSON.stringify(dynamic, null, 2));
  console.log(DynamicFile + ' is created.');
}

function collate (rootDirectory, configFile, reserveFile) {
  // Filter and get static id and default messages
  const messages = getMessages(getFiles(rootDirectory));
  const results = {};
  Object.keys(messages).forEach(x => {
    if (messages[x].type === 'static') {
      results[messages[x].id] = messages[x].defMsg;
    }
  });

  console.log(Object.keys(results).length + ' static id(s) found.');

  // Get reserve list
  const reserve = {};
  JSON.parse(fs.readFileSync(reserveFile, encoding)).forEach(x => {
    Object.keys(x.translationPairs).forEach(y => {
      if (reserve[y]) {
        console.log(y);
        throw 'The above is the repeated id in the given reserve file!'
      } else {
        reserve[y] = x.translationPairs[y];
      }
    });
  });

  // If there are repeated id between reserve and static, throw error
  const reserveIds = Object.keys(reserve);
  const repeat = reserveIds
    .map(x => ({
      id: x,
      reserveMsg: reserve[x],
      staticMsg: results[x]
    }))
    .filter(x => x.staticMsg);
  if (repeat.length) {
    fs.writeFileSync(LogFolder + '/repeatedReserveStatic.json', JSON.stringify(repeat, null, 2));
    throw 'There are repeated id between reserve and static! Refer ' + LogFolder + '/repeatedReserveStatic.json for more information.';
  }

  // AWS configurations
  const config = JSON.parse(fs.readFileSync(configFile, encoding));
  AWS.config.region = config.region;
  AWS.config.accessKeyId = config.accessKeyId;
  AWS.config.secretAccessKey = config.secretAccessKey;

  // Get list of locales from s3 bucket
  let locales;
  const s3 = new Promise.promisifyAll(new AWS.S3());
  s3.listObjectsV2Async({
    Bucket: config.bucket,
    Delimiter: '/',
    Prefix: config.folder + '/'
  })
    .then(data => {
      locales = data.Contents.filter(x => x.Key.endsWith('.json')).map(x => x.Key);
      console.log('locales: ' + locales);

      // Get list of files from s3 bucket
      return Promise.all(locales
        .map(x => s3.getObjectAsync({
          Bucket: config.bucket,
          Key: x
        })));
    })
    .then(data => {
      // Generate latest json object for each locale
      const ids = Object.keys(results);
      const files = data.map((x, i) => {
        const key = locales[i];
        const en = key.endsWith('en.json');
        const old = JSON.parse(x.Body.toString());
        const body = {};

        ids.forEach(id => {
          if (old[id]) {
            body[id] = old[id];
          } else {
            body[id] = en ? results[id] : '';
          }
        });

        reserveIds.forEach(id => {
          if (old[id]) {
            body[id] = old[id];
          } else {
            body[id] = en ? reserve[id] : '';
          }
        });

        return {
          key: key,
          body: JSON.stringify(body)
        };
      });

      // Upload list of files to s3 bucket
      return Promise.all(files
        .map((x,i) => s3.putObjectAsync({
          ACL: config.acl,
          Bucket: config.bucket,
          Key: x.key,
          Body: x.body,
          ContentType: 'application/json'
        })));
    })
    .then(() => console.log('s3 files are updated!'))
}

function error () {
  console.log('Your input is wrong!');
  console.log('Here\' the proper format: glints-collate-message command rootDirectory -c configFile -r reserveFile');
  console.log('command: reserve/collate');
  console.log('Example: ');
  console.log('$ glints-collate-message reserve app');
  console.log('$ glints-collate-message collate app -c config.json -r reserve.json');
}

module.exports = function (command, rootDirectory, configFile, reserveFile, dynamicFile, logFolder) {
  if (dynamicFile) DynamicFile = dynamicFile;
  if (logFolder) LogFolder = logFolder;

  switch (command) {
    case 'reserve':
      reserve(rootDirectory);
      break;
    case 'collate':
      if (!configFile || !reserveFile) error();
      else collate(rootDirectory, configFile, reserveFile);
      break;
    default:
      error();
      break;
  }
};
<|endoftext|>


<commit_before>var args = arguments[0] || {};
_.defaults(args, {
	showSubmitButton: true,
	askBeforeSubmit: false,
	submitTitle: L('form_submit', 'Submit'),
	builders: {},
	validateOnReturn: true
});

var LCAT = "Ti.FormBuilder";

/////////////////////////
// Requirements checks //
/////////////////////////

if (Alloy.Globals.Trimethyl == null) {
	throw new Error(LCAT + ": you need to install Trimethyl to use this widget.");
}

var UIFactory = require('T/uifactory');
if (UIFactory == null) throw new Error(LCAT + ": you need the UIFactory module to use this widget");

var UIFactoryTextField = require('T/uifactory/textfield');
if (UIFactoryTextField == null) throw new Error(LCAT + ": you need the UIFactory.TextField module to use this widget");

var UIFactoryLabel = require('T/uifactory/label');
if (UIFactoryLabel == null) throw new Error(LCAT + ": you need the UIFactory.Label module to use this widget");

var UIFactorySelect = require('T/uifactory/select');
if (UIFactorySelect == null) throw new Error(LCAT + ": you need the UIFactory.Select module to use this widget");

var UIFactoryTimeSelect = require('T/uifactory/timeselect');
if (UIFactoryTimeSelect == null) throw new Error(LCAT + ": you need the UIFactory.TimeSelect module to use this widget");

var UIFactoryDateSelect = require('T/uifactory/dateselect');
if (UIFactoryDateSelect == null) throw new Error(LCAT + ": you need the UIFactory.TimeSelect module to use this widget");

var UIFactoryMultiSelect = require('T/uifactory/multiselect');
if (UIFactoryMultiSelect == null) throw new Error(LCAT + ": you need the UIFactory.MultiSelect module to use this widget");

var Dialog = require('T/dialog');
if (Dialog == null) throw new Error(LCAT + ": you need the Dialog module to use this widget");

////////////////
// Properties //
////////////////

var fields = args.fields;
var fields_map = [];

var submitting = false;

/////////////
// Methods //
/////////////

function addValidationError(f, msg) {
	f.uiValidation.text = msg;
	f.uiValidation.animate({
		opacity: 1,
	});

	if (_.isFunction(f.addError)) f.addError();
}

function removeValidationError(f) {
	f.uiValidation.text = "";
	f.uiValidation.animate({
		opacity: 1,
	});

	if (_.isFunction(f.removeError)) f.removeError();
}

function submit() {
	var obj = {};
	fields_map.forEach(function(f) {
		if (f.name) {
			obj[f.name] = _.isFunction(f.getValue) ? f.getValue() : f.ui.getValue();
		}
	});

	submitting = true;
	$.formMainView.touchEnabled = false;

	$.trigger('submit', {
		data: obj
	});
}

////////////////////
// Public methods //
////////////////////

$.endSubmit = function() {
	submitting = false;
	$.formMainView.touchEnabled = true;
};

$.submit = function() {
	if (args.askBeforeSubmit) {
		Dialog.option(L("form_submit_prompt", "Do you want to submit the form?"), [
		{
			title: L("form_submit", "Submit"),
			callback: function() {
				if ($.validate()) submit();
			}
		},
		{
			title: L("form_cancel", "Cancel"),
			cancel: true
		}
		]);
	} else {
		if ($.validate()) submit();
	}
};

$.validate = function() {
	var can_submit = true;
	var field_to_focus = null;

	_.each(fields_map, function(f, k) {
		var def = fields[k];
		var current_value = _.isFunction(f.getValue) ? f.getValue() : f.ui.getValue();

		// Required validation
		if (def.required) {
			var requiredValue = false;
			if (_.isBoolean(current_value)) {
				requiredValue = !!current_value;
			} else if (_.isNumber(current_value) || _.isDate(current_value)) {
				requiredValue = current_value != null;
			} else {
				requiredValue = !_.isEmpty(current_value);
			}

			if (requiredValue !== true) {
				addValidationError(f, L("form_required_field", "Required field"));
				can_submit = false;
				field_to_focus = field_to_focus || f;
				return false;
			}
		}

		// Validation custom
		if (_.isFunction(def.validator)) {
			var validMessage = def.validator(f.ui.value || '');
			if (validMessage !== true) {
				addValidationError(f, validMessage);
				can_submit = false;
				field_to_focus = field_to_focus || f;
				return false;
			}
		}

		removeValidationError(f);
	});

	if (field_to_focus != null) {
		if (_.isFunction(field_to_focus.focus)) {
			field_to_focus.focus();
		} else if (_.isFunction(field_to_focus.ui.focus)) {
			field_to_focus.ui.focus();
		}
	}

	return can_submit;
};

////////////////
// UI Builder //
////////////////

exports.UIBuilder = {};

exports.UIBuilder.text = function(e,f) {
	f.ui = UIFactoryTextField(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'TextField' }), {
		textType: e.type,
		hintText: e.placeholder,
		value: e.value,
		returnKeyType: Titanium.UI.RETURNKEY_NEXT
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('return', $.validate);
	}
};

exports.UIBuilder.boolean = function(e,f) {
	var switcher = Ti.UI.createSwitch({
		value: e.value,
		left: 0
	});
	var label = UIFactoryLabel(_.extend({}, $.createStyle({ classes: ['formBooleanLabel'], 'apiName':'Label' }), {
		left: 60,
		right: 0,
		html: e.placeholder
	}));

	f.ui = Ti.UI.createView();
	f.ui.add(switcher);
	f.ui.add(label);

	f.getValue = function() { return switcher.getValue(); };
};

exports.UIBuilder.select = function(e,f) {
	f.ui = UIFactorySelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'Select' }), {
		values: e.values,
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.multiselect = function(e,f) {
	f.ui = UIFactoryMultiSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'MultiSelect' }), {
		values: e.values,
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.time = function(e,f) {
	f.ui = UIFactoryTimeSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'TimeSelect' }), {
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.date = function(e,f) {
	f.ui = UIFactoryTimeSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'DateSelect' }), {
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

_.extend(exports.UIBuilder, args.builders);

//////////////////
// Parse fields //
//////////////////

var index = 0;

_.each(_.groupBy(fields, 'group'), function(subFields, k) {
	if (k !== "undefined") {
		$.formMainView.add($.UI.create('Label', {
			classes: ['formGroupLabel'],
			text: k
		}));
		$.formMainView.add($.UI.create('View', {
			classes: ['formGroupSeparator']
		}));
	}

	$.formMainView.add(Ti.UI.createView({ height: 8 }));

	subFields.forEach(function(e) {
		var $wrap = $.UI.create('View', {
			classes: ['formWrap']
		});
		$wrap.add($.UI.create('Label', {
			classes: ['formLabel'],
			text: e.label
		}));

		var f = {
			name: e.name,
			index: (index++)
		};

		var factory = exports.UIBuilder[ e.type ] || exports.UIBuilder.text;
		factory(e,f);

		f.uiValidation = $.UI.create('Label', {
			text: '',
			opacity: 0,
			classes: ['formValidatorMessage']
		});

		$wrap.add(f.ui);
		$wrap.add(f.uiValidation);
		$.formMainView.add($wrap);

		fields_map.push(f);
	});
});

if (args.showSubmitButton == true) {
	var $submit_btn = $.UI.create('Button', {
		classes: ['formSubmitButton'],
		title: args.submitTitle
	});

	$submit_btn.addEventListener('click', function(e) {
		if (submitting) return;
		$.submit();
	});

	$.formMainView.add($submit_btn);
}
<commit_msg>Fixed custom field validation.<commit_after>var args = arguments[0] || {};
_.defaults(args, {
	showSubmitButton: true,
	askBeforeSubmit: false,
	submitTitle: L('form_submit', 'Submit'),
	builders: {},
	validateOnReturn: true
});

var LCAT = "Ti.FormBuilder";

/////////////////////////
// Requirements checks //
/////////////////////////

if (Alloy.Globals.Trimethyl == null) {
	throw new Error(LCAT + ": you need to install Trimethyl to use this widget.");
}

var UIFactory = require('T/uifactory');
if (UIFactory == null) throw new Error(LCAT + ": you need the UIFactory module to use this widget");

var UIFactoryTextField = require('T/uifactory/textfield');
if (UIFactoryTextField == null) throw new Error(LCAT + ": you need the UIFactory.TextField module to use this widget");

var UIFactoryLabel = require('T/uifactory/label');
if (UIFactoryLabel == null) throw new Error(LCAT + ": you need the UIFactory.Label module to use this widget");

var UIFactorySelect = require('T/uifactory/select');
if (UIFactorySelect == null) throw new Error(LCAT + ": you need the UIFactory.Select module to use this widget");

var UIFactoryTimeSelect = require('T/uifactory/timeselect');
if (UIFactoryTimeSelect == null) throw new Error(LCAT + ": you need the UIFactory.TimeSelect module to use this widget");

var UIFactoryDateSelect = require('T/uifactory/dateselect');
if (UIFactoryDateSelect == null) throw new Error(LCAT + ": you need the UIFactory.TimeSelect module to use this widget");

var UIFactoryMultiSelect = require('T/uifactory/multiselect');
if (UIFactoryMultiSelect == null) throw new Error(LCAT + ": you need the UIFactory.MultiSelect module to use this widget");

var Dialog = require('T/dialog');
if (Dialog == null) throw new Error(LCAT + ": you need the Dialog module to use this widget");

////////////////
// Properties //
////////////////

var fields = args.fields;
var fields_map = [];

var submitting = false;

/////////////
// Methods //
/////////////

function addValidationError(f, msg) {
	f.uiValidation.text = msg;
	f.uiValidation.animate({
		opacity: 1,
	});

	if (_.isFunction(f.addError)) f.addError();
}

function removeValidationError(f) {
	f.uiValidation.text = "";
	f.uiValidation.animate({
		opacity: 1,
	});

	if (_.isFunction(f.removeError)) f.removeError();
}

function submit() {
	var obj = {};
	fields_map.forEach(function(f) {
		if (f.name) {
			obj[f.name] = _.isFunction(f.getValue) ? f.getValue() : f.ui.getValue();
		}
	});

	submitting = true;
	$.formMainView.touchEnabled = false;

	$.trigger('submit', {
		data: obj
	});
}

////////////////////
// Public methods //
////////////////////

$.endSubmit = function() {
	submitting = false;
	$.formMainView.touchEnabled = true;
};

$.submit = function() {
	if (args.askBeforeSubmit) {
		Dialog.option(L("form_submit_prompt", "Do you want to submit the form?"), [
		{
			title: L("form_submit", "Submit"),
			callback: function() {
				if ($.validate()) submit();
			}
		},
		{
			title: L("form_cancel", "Cancel"),
			cancel: true
		}
		]);
	} else {
		if ($.validate()) submit();
	}
};

$.validate = function() {
	var can_submit = true;
	var field_to_focus = null;

	_.each(fields_map, function(f, k) {
		var def = fields[k];
		var current_value = _.isFunction(f.getValue) ? f.getValue() : f.ui.getValue();

		// Required validation
		if (def.required) {
			// Validation custom
			if (_.isFunction(def.validator)) {
				var validMessage = def.validator(current_value || '');
				if (validMessage !== true) {
					addValidationError(f, validMessage);
					can_submit = false;
					field_to_focus = field_to_focus || f;
					return false;
				}
			} else {
				var requiredValue = false;
				if (_.isBoolean(current_value)) {
					requiredValue = !!current_value;
				} else if (_.isNumber(current_value) || _.isDate(current_value)) {
					requiredValue = current_value != null;
				} else {
					requiredValue = !_.isEmpty(current_value);
				}

				if (requiredValue !== true) {
					addValidationError(f, L("form_required_field", "Required field"));
					can_submit = false;
					field_to_focus = field_to_focus || f;
					return false;
				}
			}
		}

		removeValidationError(f);
	});

	if (field_to_focus != null) {
		if (_.isFunction(field_to_focus.focus)) {
			field_to_focus.focus();
		} else if (_.isFunction(field_to_focus.ui.focus)) {
			field_to_focus.ui.focus();
		}
	}

	return can_submit;
};

////////////////
// UI Builder //
////////////////

exports.UIBuilder = {};

exports.UIBuilder.text = function(e,f) {
	f.ui = UIFactoryTextField(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'TextField' }), {
		textType: e.type,
		hintText: e.placeholder,
		value: e.value,
		returnKeyType: Titanium.UI.RETURNKEY_NEXT
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('return', $.validate);
	}
};

exports.UIBuilder.boolean = function(e,f) {
	var switcher = Ti.UI.createSwitch({
		value: e.value,
		left: 0
	});
	var label = UIFactoryLabel(_.extend({}, $.createStyle({ classes: ['formBooleanLabel'], 'apiName':'Label' }), {
		left: 60,
		right: 0,
		html: e.placeholder
	}));

	f.ui = Ti.UI.createView();
	f.ui.add(switcher);
	f.ui.add(label);

	f.getValue = function() { return switcher.getValue(); };
};

exports.UIBuilder.select = function(e,f) {
	f.ui = UIFactorySelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'Select' }), {
		values: e.values,
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.multiselect = function(e,f) {
	f.ui = UIFactoryMultiSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'MultiSelect' }), {
		values: e.values,
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.time = function(e,f) {
	f.ui = UIFactoryTimeSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'TimeSelect' }), {
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

exports.UIBuilder.date = function(e,f) {
	f.ui = UIFactoryTimeSelect(_.extend({}, $.createStyle({ classes: ['formInput'], apiName: 'DateSelect' }), {
		value: e.value,
		hintText: e.placeholder
	}));

	f.addError = function() { $.addClass(f.ui, "formInputError"); };
	f.removeError = function() { $.removeClass(f.ui, "formInputError"); };

	if (args.validateOnReturn == true) {
		f.ui.addEventListener('change', $.validate);
	}
};

_.extend(exports.UIBuilder, args.builders);

//////////////////
// Parse fields //
//////////////////

var index = 0;

_.each(_.groupBy(fields, 'group'), function(subFields, k) {
	if (k !== "undefined") {
		$.formMainView.add($.UI.create('Label', {
			classes: ['formGroupLabel'],
			text: k
		}));
		$.formMainView.add($.UI.create('View', {
			classes: ['formGroupSeparator']
		}));
	}

	$.formMainView.add(Ti.UI.createView({ height: 8 }));

	subFields.forEach(function(e) {
		var $wrap = $.UI.create('View', {
			classes: ['formWrap']
		});
		$wrap.add($.UI.create('Label', {
			classes: ['formLabel'],
			text: e.label
		}));

		var f = {
			name: e.name,
			index: (index++)
		};

		var factory = exports.UIBuilder[ e.type ] || exports.UIBuilder.text;
		factory(e,f);

		f.uiValidation = $.UI.create('Label', {
			text: '',
			opacity: 0,
			classes: ['formValidatorMessage']
		});

		$wrap.add(f.ui);
		$wrap.add(f.uiValidation);
		$.formMainView.add($wrap);

		fields_map.push(f);
	});
});

if (args.showSubmitButton == true) {
	var $submit_btn = $.UI.create('Button', {
		classes: ['formSubmitButton'],
		title: args.submitTitle
	});

	$submit_btn.addEventListener('click', function(e) {
		if (submitting) return;
		$.submit();
	});

	$.formMainView.add($submit_btn);
}
<|endoftext|>