<commit_before>(function () {
'use strict';
var module = angular.module('fim.base');

function getCSS(clazz, property) {
  var p = document.createElement("span");
  p.setAttribute("class", clazz);
  p.setAttribute("style", "display:none");
  document.body.appendChild(p);
  var result;
  if (Array.isArray(property)) {
    result = [];
    for (var i=0; i<property.length; i++) {
      result.push(window.getComputedStyle(p).getPropertyValue(property[i]));
    }
  }
  else {
    result = window.getComputedStyle(p).getPropertyValue(property);
  }
  p.parentNode.removeChild(p);
  return result;
}

function generateSpeechBubbleBootstrapCSS() {
  var fromMe = getCSS('btn btn-primary', ['background-color', 'color']);
  var fromThem = getCSS('alert alert-info', ['background-color', 'color']);
  //var fromThem = getCSS('jumbotron', ['background-color', 'color']);
  var background = window.getComputedStyle(document.body).getPropertyValue('background-color');
  var styleElement = document.getElementById('speech-bubble-css');
  if (!styleElement) {
    styleElement = document.createElement('style');
    styleElement.setAttribute('id', 'speech-bubble-css');
    styleElement.setAttribute('type', 'text/css');
    document.head.appendChild(styleElement);
  }
  var html = [
    '.chat .fromMe { color:',fromMe[1],' !important; margin-left:30px; background:',fromMe[0],' !important; }\n',
    '.chat .fromMe:before { border-right:20px solid ',fromMe[0],' !important;}\n',
    '.chat .fromMe:after { background:',background,' !important;}\n',
    '.chat .fromThem { color:',fromThem[1],' !important; margin-right:30px; background:',fromThem[0],' !important;}\n',
    '.chat .fromThem:before { border-left:20px solid ',fromThem[0],' !important;}\n',
    '.chat .fromThem:after { background:',background,' !important;}\n',
  ].join('');
  if (styleElement.innerHTML != html) {
    styleElement.innerHTML = html;
  }
}

module.config(function($routeProvider) {
  $routeProvider
    .when('/messenger/:id_rs?', {
      templateUrl: 'partials/messenger.html',
      controller: 'MessengerController'
    });
});

module.controller('MessengerController', function($location, $q, $scope, modals, $rootScope,
  $routeParams, nxt, plugins, GossipChatMessagesProvider, Gossip, Emoji,
  KeyService, $timeout, settings, publicKeyService, GossipChatListProvider) {

  $rootScope.unread = false;

  /* might not have been started */
  Gossip.onActivated();
  $scope.gossipUI = Gossip.ui;

  $scope.id_rs   = $routeParams.id_rs;
  $scope.message = {
    text: '',
    html: '',
    name: '',
    recipient: '',
    recipientPublicKey: '',
    recipientValid: false
  };
  $scope.emoji   = { groups: Emoji.groups };
  $scope.ui      = {};
  $scope.ui.emojiCollapse = true;
  $scope.ui.editRecipient = false;
  $scope.ui.chat          = null;
  $scope.ui.sendOffline   = false;
  $scope.ui.isTyping      = false;

  var typing_timeout = null;

  /* have to login first */
  if (!$rootScope.currentAccount) {
    $rootScope.destURL = $location.url();
    $location.path('/login-to');
    return;
  }

  var api = nxt.get($rootScope.currentAccount.id_rs);
  if (!api) {
    $location.path('/login-to');
    return;
  }

  $scope.feeCost = api.engine.feeCost + ' ' + api.engine.symbol;

  generateSpeechBubbleBootstrapCSS();
  settings.resolve('themes.default.theme', function () {
    $timeout(function () {
      generateSpeechBubbleBootstrapCSS();
    });
  });

  $scope.chatListProvider = new GossipChatListProvider(api, $scope, $rootScope.currentAccount.id_rs);
  var promise = $scope.chatListProvider.reload();

  /* enable the add contact editor */
  if ($routeParams.id_rs == 'new') {
    $scope.ui.editRecipient = true;
  }
  /* empty url - load contacts and select first one or go to add contact */
  else if (!$scope.id_rs) {
    promise.then(
      function () {
        var chat = $scope.chatListProvider.entities[0];
        if (chat) {
          $location.path('/messenger/'+chat.otherRS);
        }
        else {
          $location.path('/messenger/new');
        }
      }
    );
  }
  /* contact id provided */
  else {
    promise.then(
      function () {
        /* look up the chat in the provider */
        var provider = $scope.chatListProvider;
        for (var i=0; i<provider.entities.length; i++) {
          if (provider.entities[i].otherRS == $scope.id_rs) {
            $scope.$evalAsync(function () {
              var chat = $scope.ui.chat = provider.entities[i];
              /* sends a ping if user is offline */
              if (chat.provider && !chat.provider.online) {
                Gossip.ping(chat.otherRS);
              }
              /* create the messages provider */
              $scope.chatMessagesProvider = new GossipChatMessagesProvider(api, $scope, 8, $rootScope.currentAccount.id_rs, $scope.id_rs);
              $scope.chatMessagesProvider.reload().then(
                function () {
                  $scope.$evalAsync(function () {
                    $scope.contactName = $scope.chatMessagesProvider.accountTwoName;
                  });
                }
              );
            });
            return;
          }
        }
        /* if it aint there - assume it's an account we want to add */
        $scope.$evalAsync(function () {
          $scope.message.recipient = $scope.id_rs;
          $scope.ui.editRecipient  = true;
          $scope.accountChanged();
        });
      }
    );
  }

  var unregister = Gossip.addListener(
                      Gossip.IS_TYPING_TOPIC,
                      $scope.id_rs,
                      $rootScope.currentAccount.id_rs,
  function (gossip) {
    $scope.$evalAsync(
      function () {
        $scope.ui.isTyping = true;
        $timeout(function () {
          $scope.ui.isTyping = false;
        }, 10*1000);
      }
    );
  });
  $scope.$on('$destroy', unregister);

  $scope.reload = function () {
    if ($scope.chatListProvider) {
      $scope.chatListProvider.reload();
    }
    if ($scope.chatMessagesProvider) {
      $scope.chatMessagesProvider.reload();
    }
  }

  $scope.messageChanged = function () {
    /* notifies that the user is typing a message */
    if ($scope.ui.sendOffline) {
      if (!typing_timeout) {
        typing_timeout = $timeout(function () { typing_timeout = null }, 15*1000, false);
        Gossip.sendGossip($scope.id_rs, "typing", Gossip.IS_TYPING_TOPIC);
      }
    }
    $scope.message.html = Emoji.emojifi($scope.message.text);
  }

  $scope.accountChanged = function () {
    $scope.$evalAsync(function () {
      $scope.message.recipientValid = false;
      $scope.message.recipientPublicKey = '';
      $scope.message.name = '';

      var id_rs = $scope.message.recipient;
      if (id_rs && id_rs.trim().length > 0) {
        var address = api.createAddress();
        if (address.set(id_rs)) {
          $scope.message.recipientValid = true;

          /* see if we can look up the name and public key */
          var arg = {account:id_rs, requestType:'getAccount'};
          api.engine.socket().callAPIFunction(arg).then(function (data) {
            if (data.publicKey || data.accountName) {
              $scope.$evalAsync(function () {
                if (data.publicKey) {
                  $scope.message.recipientPublicKey = data.publicKey;
                  publicKeyService.set(id_rs, data.publicKey);
                }
                $scope.message.name = data.accountName;
              });
            }
          });
        }
      }
    });
  }

  $scope.insertEmoji = function (name) {
    $scope.$evalAsync(function () {
      $scope.message.text += ':'+Emoji.toBase32(name)+':';
      $scope.message.html = Emoji.emojifi($scope.message.text);
    });
  }

  /* Handler for the New Message button.
   * Based on message type (gossip or blockchain) this action has a different effect */
  $scope.newMessage = function () {
    $scope.ui.editRecipient = true;
    $scope.slide.offCanvas  = false;
    $scope.chatMessagesProvider = null;
  }

  /* Handler for the Send Message button.
   * Based on message type (gossip or blockchain) this action has a different effect */
  $scope.sendDirectMessage = function () {
    if ($scope.ui.sendOffline) {
      plugins.get('transaction').get('sendMessage').execute($rootScope.currentAccount.id_rs, {
        recipient: $scope.id_rs,
        message: $scope.message.text,
        autoSubmit: true,
      }).then(function (items) {
        $scope.$evalAsync(function () {
          if (items != null) {
            $scope.ui.emojiCollapse  = true;
            $scope.message.text = '';
            $scope.message.html = '';
            $scope.message.recipient = '';
            $scope.message.recipientPublicKey = '';
            $scope.message.send = true;
            $timeout(function () { $scope.message.send = false }, 5000);
          }
        });
      });
    }
    else {
      Gossip.message($scope.id_rs, $scope.message.text).then(
        function (data) {
          console.log(data);
          $scope.$evalAsync(function () {
            $scope.ui.emojiCollapse  = true;
            $scope.message.text = '';
            $scope.message.html = '';
            $scope.message.recipient = '';
            $scope.message.recipientPublicKey = '';
            $scope.message.send = true;
            $timeout(function () { $scope.message.send = false }, 5000);
          });
          // TODO - update the chat in GossipChatListProvider last timestamp!
          // $timeout(function () { $scope.chatListProvider.reload() }, 3000);
          $timeout.cancel(typing_timeout);
          typing_timeout = null;
        }
      );
    }
  };

  $scope.addContact = function (id_rs) {

    /* dont add duplicates */
    var provider = $scope.chatListProvider;
    for (var i=0; i<provider.entities.length; i++) {
      if (provider.entities[i].otherRS == id_rs) {
        $location.path('/messenger/'+id_rs);
        return;
      }
    }

    /* store the chat in the db - then go to its page */
    Gossip.getChatService().add(id_rs).then(
      function (chat) {
        $location.path('/messenger/'+id_rs);
      }
    );
  }

  $scope.removeContact = function (id_rs) {
    plugins.get('alerts').confirm({
      title: 'Remove contact',
      message: 'Do you want to remove this contact? Removal cannot be undone.'
    }).then(
      function (value) {
        if (value) {
          Gossip.removeContact(id_rs).then(
            function () {
              /* deleted the active account - navigate away */
              if ($scope.id_rs == id_rs) {
                $location.path('/messenger');
              }
            }
          );
        }
      }
    );
  }

  $scope.goOnlineAgain = function () {
    Gossip.setEnabled();
  }

  $scope.activateTextbox = function() {
    $rootScope.unread = false;
  }
});
})();<commit_msg>hide unread icon after 5 seconds if on same page<commit_after>(function () {
'use strict';
var module = angular.module('fim.base');

function getCSS(clazz, property) {
  var p = document.createElement("span");
  p.setAttribute("class", clazz);
  p.setAttribute("style", "display:none");
  document.body.appendChild(p);
  var result;
  if (Array.isArray(property)) {
    result = [];
    for (var i=0; i<property.length; i++) {
      result.push(window.getComputedStyle(p).getPropertyValue(property[i]));
    }
  }
  else {
    result = window.getComputedStyle(p).getPropertyValue(property);
  }
  p.parentNode.removeChild(p);
  return result;
}

function generateSpeechBubbleBootstrapCSS() {
  var fromMe = getCSS('btn btn-primary', ['background-color', 'color']);
  var fromThem = getCSS('alert alert-info', ['background-color', 'color']);
  //var fromThem = getCSS('jumbotron', ['background-color', 'color']);
  var background = window.getComputedStyle(document.body).getPropertyValue('background-color');
  var styleElement = document.getElementById('speech-bubble-css');
  if (!styleElement) {
    styleElement = document.createElement('style');
    styleElement.setAttribute('id', 'speech-bubble-css');
    styleElement.setAttribute('type', 'text/css');
    document.head.appendChild(styleElement);
  }
  var html = [
    '.chat .fromMe { color:',fromMe[1],' !important; margin-left:30px; background:',fromMe[0],' !important; }\n',
    '.chat .fromMe:before { border-right:20px solid ',fromMe[0],' !important;}\n',
    '.chat .fromMe:after { background:',background,' !important;}\n',
    '.chat .fromThem { color:',fromThem[1],' !important; margin-right:30px; background:',fromThem[0],' !important;}\n',
    '.chat .fromThem:before { border-left:20px solid ',fromThem[0],' !important;}\n',
    '.chat .fromThem:after { background:',background,' !important;}\n',
  ].join('');
  if (styleElement.innerHTML != html) {
    styleElement.innerHTML = html;
  }
}

module.config(function($routeProvider) {
  $routeProvider
    .when('/messenger/:id_rs?', {
      templateUrl: 'partials/messenger.html',
      controller: 'MessengerController'
    });
});

// module.run(function($rootScope, $location, $interval) {
//   $rootScope.$watch(function() {

//     if($location.path().indexOf("/messenger/") > -1) {
//       $interval(function() {
//         $rootScope.unread = false;
//       }, 5000);
//       console.log("called")
//     } else {
//       $interval.cancel();
//       console.log("cancelled");
//     }
//   })
// })

module.controller('MessengerController', function($location, $q, $scope, modals, $rootScope,
  $routeParams, nxt, plugins, GossipChatMessagesProvider, Gossip, Emoji,
  KeyService, $timeout, settings, publicKeyService, GossipChatListProvider, $interval) {

  $rootScope.unread = false;

  var unreadIcon = $interval(function() {
    $rootScope.unread = false;
  }, 5000);

  $scope.$on( "$routeChangeStart", function(event, next, current) {
    if(next.loadedTemplateUrl != "partials/messenger.html") {
       $scope.$on('$destroy', function () {
        $interval.cancel(unreadIcon);
      });
    }
  })

  /* might not have been started */
  Gossip.onActivated();
  $scope.gossipUI = Gossip.ui;

  $scope.id_rs   = $routeParams.id_rs;
  $scope.message = {
    text: '',
    html: '',
    name: '',
    recipient: '',
    recipientPublicKey: '',
    recipientValid: false
  };
  $scope.emoji   = { groups: Emoji.groups };
  $scope.ui      = {};
  $scope.ui.emojiCollapse = true;
  $scope.ui.editRecipient = false;
  $scope.ui.chat          = null;
  $scope.ui.sendOffline   = false;
  $scope.ui.isTyping      = false;

  var typing_timeout = null;

  /* have to login first */
  if (!$rootScope.currentAccount) {
    $rootScope.destURL = $location.url();
    $location.path('/login-to');
    return;
  }

  var api = nxt.get($rootScope.currentAccount.id_rs);
  if (!api) {
    $location.path('/login-to');
    return;
  }

  $scope.feeCost = api.engine.feeCost + ' ' + api.engine.symbol;

  generateSpeechBubbleBootstrapCSS();
  settings.resolve('themes.default.theme', function () {
    $timeout(function () {
      generateSpeechBubbleBootstrapCSS();
    });
  });

  $scope.chatListProvider = new GossipChatListProvider(api, $scope, $rootScope.currentAccount.id_rs);
  var promise = $scope.chatListProvider.reload();

  /* enable the add contact editor */
  if ($routeParams.id_rs == 'new') {
    $scope.ui.editRecipient = true;
  }
  /* empty url - load contacts and select first one or go to add contact */
  else if (!$scope.id_rs) {
    promise.then(
      function () {
        var chat = $scope.chatListProvider.entities[0];
        if (chat) {
          $location.path('/messenger/'+chat.otherRS);
        }
        else {
          $location.path('/messenger/new');
        }
      }
    );
  }
  /* contact id provided */
  else {
    promise.then(
      function () {
        /* look up the chat in the provider */
        var provider = $scope.chatListProvider;
        for (var i=0; i<provider.entities.length; i++) {
          if (provider.entities[i].otherRS == $scope.id_rs) {
            $scope.$evalAsync(function () {
              var chat = $scope.ui.chat = provider.entities[i];
              /* sends a ping if user is offline */
              if (chat.provider && !chat.provider.online) {
                Gossip.ping(chat.otherRS);
              }
              /* create the messages provider */
              $scope.chatMessagesProvider = new GossipChatMessagesProvider(api, $scope, 8, $rootScope.currentAccount.id_rs, $scope.id_rs);
              $scope.chatMessagesProvider.reload().then(
                function () {
                  $scope.$evalAsync(function () {
                    $scope.contactName = $scope.chatMessagesProvider.accountTwoName;
                  });
                }
              );
            });
            return;
          }
        }
        /* if it aint there - assume it's an account we want to add */
        $scope.$evalAsync(function () {
          $scope.message.recipient = $scope.id_rs;
          $scope.ui.editRecipient  = true;
          $scope.accountChanged();
        });
      }
    );
  }

  var unregister = Gossip.addListener(
                      Gossip.IS_TYPING_TOPIC,
                      $scope.id_rs,
                      $rootScope.currentAccount.id_rs,
  function (gossip) {
    $scope.$evalAsync(
      function () {
        $scope.ui.isTyping = true;
        $timeout(function () {
          $scope.ui.isTyping = false;
        }, 10*1000);
      }
    );
  });
  $scope.$on('$destroy', unregister);

  $scope.reload = function () {
    if ($scope.chatListProvider) {
      $scope.chatListProvider.reload();
    }
    if ($scope.chatMessagesProvider) {
      $scope.chatMessagesProvider.reload();
    }
  }

  $scope.messageChanged = function () {
    /* notifies that the user is typing a message */
    if ($scope.ui.sendOffline) {
      if (!typing_timeout) {
        typing_timeout = $timeout(function () { typing_timeout = null }, 15*1000, false);
        Gossip.sendGossip($scope.id_rs, "typing", Gossip.IS_TYPING_TOPIC);
      }
    }
    $scope.message.html = Emoji.emojifi($scope.message.text);
  }

  $scope.accountChanged = function () {
    $scope.$evalAsync(function () {
      $scope.message.recipientValid = false;
      $scope.message.recipientPublicKey = '';
      $scope.message.name = '';

      var id_rs = $scope.message.recipient;
      if (id_rs && id_rs.trim().length > 0) {
        var address = api.createAddress();
        if (address.set(id_rs)) {
          $scope.message.recipientValid = true;

          /* see if we can look up the name and public key */
          var arg = {account:id_rs, requestType:'getAccount'};
          api.engine.socket().callAPIFunction(arg).then(function (data) {
            if (data.publicKey || data.accountName) {
              $scope.$evalAsync(function () {
                if (data.publicKey) {
                  $scope.message.recipientPublicKey = data.publicKey;
                  publicKeyService.set(id_rs, data.publicKey);
                }
                $scope.message.name = data.accountName;
              });
            }
          });
        }
      }
    });
  }

  $scope.insertEmoji = function (name) {
    $scope.$evalAsync(function () {
      $scope.message.text += ':'+Emoji.toBase32(name)+':';
      $scope.message.html = Emoji.emojifi($scope.message.text);
    });
  }

  /* Handler for the New Message button.
   * Based on message type (gossip or blockchain) this action has a different effect */
  $scope.newMessage = function () {
    $scope.ui.editRecipient = true;
    $scope.slide.offCanvas  = false;
    $scope.chatMessagesProvider = null;
  }

  /* Handler for the Send Message button.
   * Based on message type (gossip or blockchain) this action has a different effect */
  $scope.sendDirectMessage = function () {
    if ($scope.ui.sendOffline) {
      plugins.get('transaction').get('sendMessage').execute($rootScope.currentAccount.id_rs, {
        recipient: $scope.id_rs,
        message: $scope.message.text,
        autoSubmit: true,
      }).then(function (items) {
        $scope.$evalAsync(function () {
          if (items != null) {
            $scope.ui.emojiCollapse  = true;
            $scope.message.text = '';
            $scope.message.html = '';
            $scope.message.recipient = '';
            $scope.message.recipientPublicKey = '';
            $scope.message.send = true;
            $timeout(function () { $scope.message.send = false }, 5000);
          }
        });
      });
    }
    else {
      Gossip.message($scope.id_rs, $scope.message.text).then(
        function (data) {
          console.log(data);
          $scope.$evalAsync(function () {
            $scope.ui.emojiCollapse  = true;
            $scope.message.text = '';
            $scope.message.html = '';
            $scope.message.recipient = '';
            $scope.message.recipientPublicKey = '';
            $scope.message.send = true;
            $timeout(function () { $scope.message.send = false }, 5000);
          });
          // TODO - update the chat in GossipChatListProvider last timestamp!
          // $timeout(function () { $scope.chatListProvider.reload() }, 3000);
          $timeout.cancel(typing_timeout);
          typing_timeout = null;
        }
      );
    }
  };

  $scope.addContact = function (id_rs) {

    /* dont add duplicates */
    var provider = $scope.chatListProvider;
    for (var i=0; i<provider.entities.length; i++) {
      if (provider.entities[i].otherRS == id_rs) {
        $location.path('/messenger/'+id_rs);
        return;
      }
    }

    /* store the chat in the db - then go to its page */
    Gossip.getChatService().add(id_rs).then(
      function (chat) {
        $location.path('/messenger/'+id_rs);
      }
    );
  }

  $scope.removeContact = function (id_rs) {
    plugins.get('alerts').confirm({
      title: 'Remove contact',
      message: 'Do you want to remove this contact? Removal cannot be undone.'
    }).then(
      function (value) {
        if (value) {
          Gossip.removeContact(id_rs).then(
            function () {
              /* deleted the active account - navigate away */
              if ($scope.id_rs == id_rs) {
                $location.path('/messenger');
              }
            }
          );
        }
      }
    );
  }

  $scope.goOnlineAgain = function () {
    Gossip.setEnabled();
  }

  $scope.activateTextbox = function() {
    $rootScope.unread = false;
  }
});
})();<|endoftext|>


<commit_before>/*
This class makes a drop-down menu, and can be subclassed
into things like the CT.autocomplete classes.
*/

CT.Drop = CT.Class({
	CLASSNAME: "CT.Drop",
	_position: function() {
		var ipos = CT.align.offset(this.anchor);
		this.node.style.width = (this.anchor.clientWidth + 2) + "px";
		this.node.style.top = (ipos.top + this.anchor.clientHeight) + "px";
		this.node.style.left = (ipos.left + 2) + "px";
	},
	_hide: function() {
		this.node.className = this.className + " hider";
		this.opts.onHide && this.opts.onHide();
	},
	expand: function(cb) {
		cb = cb || this.opts.onShow;
		var n = this.node;
		!this.opts.relative && this._position();
		this.viewing = true;
		n.className = this.className + " drop-open";
		CT.trans.trans({
			node: n,
			cb: function() {
				n.className += " scrolly";
				cb && cb();
			}
		});
	},
	retract: function() {
		if (!this.viewing)
			return;
		this.viewing = false;
		this.node.className = this.className;
		CT.trans.trans({
			node: this.node,
			cb: this._hide
		});
	},
	slide: function() {
		if (this.viewing)
			this.retract();
		else
			this.expand();
	},
	_setAnchor: function() {
		if (this.opts.setClick)
			this.anchor.onclick = this.slide;
	},
	init: function(opts) {
		this.opts = opts = CT.merge(opts, {
			content: CT.dom.node(),
			parent: document.body,
			rows: false,
			relative: false,
			setClick: true,
			setNodeClick: true
		});
		this.className = opts.relative ? "relative drop" : "whiteback drop above";
		if (this.opts.rows)
			this.className += " drop-rows";
		this.anchor = opts.anchor;
		this.node = CT.dom.node(opts.content, null, this.className + " hider");
		if (opts.setNodeClick)
			this.node.onclick = this.slide;
		opts.parent.appendChild(this.node);
		CT.drag.makeDraggable(this.node, { constraint: "horizontal" });
		setTimeout(this._setAnchor); // for subclasses that define anchor in constructor
	}
});<commit_msg>CT.Drop: new option, setWidth (default false)<commit_after>/*
This class makes a drop-down menu, and can be subclassed
into things like the CT.autocomplete classes.
*/

CT.Drop = CT.Class({
	CLASSNAME: "CT.Drop",
	_position: function() {
		var ipos = CT.align.offset(this.anchor);
		if (this.opts.setWidth)
			this.node.style.width = (this.anchor.clientWidth + 2) + "px";
		this.node.style.top = (ipos.top + this.anchor.clientHeight) + "px";
		this.node.style.left = (ipos.left + 2) + "px";
	},
	_hide: function() {
		this.node.className = this.className + " hider";
		this.opts.onHide && this.opts.onHide();
	},
	expand: function(cb) {
		cb = cb || this.opts.onShow;
		var n = this.node;
		!this.opts.relative && this._position();
		this.viewing = true;
		n.className = this.className + " drop-open";
		CT.trans.trans({
			node: n,
			cb: function() {
				n.className += " scrolly";
				cb && cb();
			}
		});
	},
	retract: function() {
		if (!this.viewing)
			return;
		this.viewing = false;
		this.node.className = this.className;
		CT.trans.trans({
			node: this.node,
			cb: this._hide
		});
	},
	slide: function() {
		if (this.viewing)
			this.retract();
		else
			this.expand();
	},
	_setAnchor: function() {
		if (this.opts.setClick)
			this.anchor.onclick = this.slide;
	},
	init: function(opts) {
		this.opts = opts = CT.merge(opts, {
			content: CT.dom.node(),
			parent: document.body,
			rows: false,
			relative: false,
			setClick: true,
			setNodeClick: true,
			setWidth: false
		});
		this.className = opts.relative ? "relative drop" : "whiteback drop above";
		if (opts.rows)
			this.className += " drop-rows";
		this.anchor = opts.anchor;
		this.node = CT.dom.node(opts.content, null, this.className + " hider");
		if (opts.setNodeClick)
			this.node.onclick = this.slide;
		opts.parent.appendChild(this.node);
		CT.drag.makeDraggable(this.node, { constraint: "horizontal" });
		setTimeout(this._setAnchor); // for subclasses that define anchor in constructor
	}
});<|endoftext|>


<commit_before>/**
 * ntd chosen directive
 *
 * attributies:
 *  data-disable-search-threshold - int
 *  当大于设置条数时显示搜索
 *  data-on-search-promise - fn
 *  当搜索的时候回调函数, 要求返回一个promise
 *  data-options-node - string
 *  返回结果中option 所处的节点
 *  data-allow-single-deselect - bool
 *  是否允许清空选中项目
 *
 * multiple 模式下 autocomplete 完成 beta
 *
 * @author sunfengming@ec3s.com (Fengming Sun)
 */
(function(app, ng) {
  'use strict';
  var Chosen = function($parse, $timeout) {
    return {
      restrict: 'AC',
      link: function(scope, elem, attrs) {
        var ngOptions = attrs.ngOptions || null;
        var ngModelName = attrs.ngModel || null;
        var onSearch = attrs.onSearchPromise || null;
        var optionsNode = attrs.optionsNode || null;
        var multiple = attrs.multiple || null;
        var oldSearch = '';
        var initOptions;
        var disableSearchThreshold = attrs.disableSearchThreshold || 0;
        var allowSingleDeselect = attrs.allowSingleDeselect || false;
        allowSingleDeselect = allowSingleDeselect == 'true';

        // init chosen
        var options = {
          disable_search_threshold: disableSearchThreshold
        };
        var originStyleClass = elem.attr('class').split(' ')
          .filter(function(item) {
            item = $.trim(item);
            return (item != 'ntd-chosen' &&
              item.match(/^ng\-.*$/) === null);
          });
        var chosenEl = elem.chosen(options);
        var chosen = chosenEl.data('chosen');
        chosen.container.css('width', '');
        chosen.container.addClass(originStyleClass.join(' '));
        var selected_options = {};
        var searchTxt = scope.$new(false);

        if (onSearch) {
          // 不进入匹配模式
          chosen.winnow_results = function() {
            this.no_results_clear();
            var searchText = this.get_search_text();
            var results_data = this.results_data;
            var option_number = 0;

            for (var i = 0; i < results_data.length; i++) {
              var option = results_data[i];
              if (!option.empty) {
                option_number++;
                // 永远可被匹配
                option.search_match = true;
                option.search_text = option.group ? option.label : option.html;
              }
            }

            // 按照有效选项数量判断现实
            if (option_number <= 0) {
              this.update_results_content('');
              this.result_clear_highlight();
              return this.no_results(searchText);
            } else {
              this.update_results_content(this.results_option_build());
              return this.winnow_results_set_highlight();
            }
          };

          // 搜索模式时, 刷新chosen 不清空搜索框内容且
          chosen.show_search_field_default = function() {
            if (this.is_multiple && this.choices_count() < 1 &&
              !this.active_field) {
              this.search_field.val(this.default_text);
              return this.search_field.addClass('default');
            } else {
              return this.search_field.removeClass('default');
            }
          };
        }

        // reset allow single deselect
        chosen.allow_single_deselect = allowSingleDeselect;

        // watch for ng-options change
        if (ngOptions) {
          var optionsModelName = ngOptions.split(' ').pop();
          var optionsModelGetter = $parse(optionsModelName);
          var optionsModelSetter = optionsModelGetter.assign;
          scope.$watch(optionsModelName, function(newValue, oldValue) {
            chosenEl.trigger('liszt:data_loaded', {
              options: newValue,
              optionsModelName: optionsModelName
            });
          }, true);
        }

        // watch for ng-model change
        if (ngModelName) {
          scope.$watch(ngModelName, function(newValue, oldValue) {
            // if multiple chosen
            if (multiple) {
              ng.forEach(newValue, function(value) {
                if (!selected_options[value]) {
                  ng.forEach(optionsModelGetter(scope), function(item, index) {
                    if (item.id == value) {
                      selected_options[value] = optionsModelGetter(
                        scope
                      )[index];
                    }
                  });
                }
              });
            }
            if (newValue !== oldValue) {
              elem.trigger('liszt:updated');
            }
          }, true);
        }

        //reselect
        chosenEl.bind('liszt:hiding_dropdown', function(e) {
          if (!chosen.active_field && ng.isArray(initOptions)) {
            optionsModelSetter(scope, initOptions);
            searchTxt.$search = '';
            searchTxt.$apply();
            $timeout(function() {
              chosenEl.trigger('liszt:updated');
              chosen.search_field.val(searchTxt.$search);
            });
          } else if (chosen.active_field) {
            initOptions = optionsModelGetter(scope);
          }
        });

        // set chosen object
        chosenEl.bind('liszt:showing_dropdown', function(e, data) {
          if (onSearch) {
            if (!searchTxt.$search) {
              $timeout(function() {
                chosen.search_results.find('.no-results').text('请输入关键字...');
              });
              return;
            }
            // if single then set search field
            if (!multiple) {
              chosen.search_field.val(searchTxt.$search);
            }
            chosenEl.trigger('liszt:load_data', {
              onSearch: onSearch,
              optionsModelName: optionsModelName,
              needRecord: true
            });
          }
        });

        // load options data
        chosenEl.bind('liszt:load_data', function(e, data) {
          var promise = searchTxt.$eval(data.onSearch);
          // add loading pic
          chosen.search_field.addClass('loading');
          // add loading tip
          chosen.search_results.find('.no-results').text('加载中...');
          promise.then(
            function(result) {
              var options = [];
              if (optionsNode) {
                options = result[optionsNode];
              } else {
                options = result;
              }
              if (!ng.isArray(options)) {
                options = [];
              }
              if (data.needRecord && !initOptions) {
                initOptions = options;
              }
              chosenEl.trigger('liszt:data_loaded', {
                options: options,
                optionsModelName: data.optionsModelName
              });
            }
          );
        });

        // options data loaded
        chosenEl.bind('liszt:data_loaded', function(e, data) {
          if (onSearch) {
            // remove loading pic
            chosen.search_field.removeClass('loading');
            // load new options
            if (ng.isArray(data.options) && data.options.length > 0) {
              if (!initOptions) {
                initOptions = data.options;
              }
              optionsModelSetter(scope, data.options);
            } else {
              // show no results tip
              optionsModelSetter(scope, []);
            }
            if (multiple) {
              // concat selected options into loaded options
              ng.forEach(selected_options, function(selectedOption) {
                var hasOption = false;
                ng.forEach(optionsModelGetter(scope), function(option) {
                  if (option.id == selectedOption.id) {
                    hasOption = true;
                    return;
                  }
                });
                if (!hasOption) {
                  var options = optionsModelGetter(scope);
                  options.push(selectedOption);
                  if (ng.isArray(options)) {
                    optionsModelSetter(scope, options);
                  }
                }
              });
            }
          }
          // refresh chosen when options loaded
          $timeout(function() {
            chosenEl.trigger('liszt:updated');
            if (!searchTxt.$search) {
              chosen.search_results.find('.no-results').text('请输入关键字...');
            }
          });
        });

        // get new option list
        if (onSearch && optionsModelName) {
          // if chosen.search_field changed, callback onSearch func
          chosen.search_field.bind('keyup', function(e) {
            if (chosen && chosen.results_showing) {
              searchTxt.$search = chosen.get_search_text();
              $timeout(function() {
                  if (oldSearch != searchTxt.$search) {
                    oldSearch = searchTxt.$search;
                    chosenEl.trigger('liszt:load_data', {
                      onSearch: onSearch,
                      optionsModelName: optionsModelName
                    });
                  }
              },500);
            }
          });
        }

        // refresh the option list when chosen changed
        chosenEl.change(function(e) {
          elem.trigger('liszt:updated');
        });

      }
    };
  };

  var Linkage = function($parse) {
    return {
      restrict: 'AC',
      template: '' +
      ' <select class="col-sm-3" data-ntd-chosen' +
      ' data-placeholder="请选择"' +
      ' data-disable-search-threshold="10"' +
      ' data-ng-change="change($index)"' +
      ' data-ng-model="values[$index]"' +
      ' data-allow-single-deselect="true"' +
      ' data-ng-options="option as option.name' +
      ' for option in linkage">' +
      ' <option value=""></option>' +
      '</select>',
      scope: {
        source: '=',
        ngModel: '='
      },
      link: function(scope, elem, attrs) {
        var baseLevels;
        scope.$watch('source', function(value, oldValue) {
          if (!ng.isArray(scope.ngModel)) {
            scope.ngModel = [];
          }
          initOptions();
          changeSelect();
        }, true);

        var initOptions = function() {
          baseLevels = [];
          scope.values = [];
          scope.linkages = [];
          ng.forEach(scope.source, function(item) {
            baseLevels.push(item);
          });
          scope.linkages.push(baseLevels);
        };

        var changeSelect = function() {
          if (scope.ngModel.length > 0) {
            ng.forEach(scope.ngModel, function(id, index) {
              ng.forEach(scope.linkages[index], function(item) {
                if (item.id == id) {
                  scope.values[index] = item;
                  scope.change(index);
                }
              });
            });
          } else {
            scope.values[0] = '';
            scope.change(0);
          }
        };

        scope.change = function(index) {
          var tmpLevels = [];
          var level = scope.linkages.length - 1;
          var offset = scope.values[index];
          var values = [];
          if (!offset) {
            scope.linkages.splice(index + 1, level - index);
            scope.values.splice(index + 1, level - index);
          } else {
            if (offset.children) {
              ng.forEach(offset.children, function(item) {
                tmpLevels.push(item);
              });
            }
            if (level <= index && tmpLevels.length > 0) {
              scope.linkages.push(tmpLevels);
            } else if (index < level) {
              scope.linkages.splice(index + 1, level - index);
              scope.values.splice(index + 1, level - index);
              if (tmpLevels.length > 0) {
                scope.linkages[index + 1] = tmpLevels;
              }
            }
          }
          $(scope.values).each(function(index, item) {
            if (!!item == true && !!item.id == true) {
              values.push(item.id);
            }
          });
          scope.ngModel = values;
        };
      }
    };
  };

  app.directive('ntdChosen', ['$parse', '$timeout', Chosen]);
  app.directive('ntdLinkage', ['$parse', Linkage]);
})(angular.module('ntd.directives'), angular);
<commit_msg>在搜索框中输入的老数据和当前数据在下拉框关闭时候都应该被清空，防止再次搜索时候出错<commit_after>/**
 * ntd chosen directive
 *
 * attributies:
 *  data-disable-search-threshold - int
 *  当大于设置条数时显示搜索
 *  data-on-search-promise - fn
 *  当搜索的时候回调函数, 要求返回一个promise
 *  data-options-node - string
 *  返回结果中option 所处的节点
 *  data-allow-single-deselect - bool
 *  是否允许清空选中项目
 *
 * multiple 模式下 autocomplete 完成 beta
 *
 * @author sunfengming@ec3s.com (Fengming Sun)
 */
(function(app, ng) {
  'use strict';
  var Chosen = function($parse, $timeout) {
    return {
      restrict: 'AC',
      link: function(scope, elem, attrs) {
        var ngOptions = attrs.ngOptions || null;
        var ngModelName = attrs.ngModel || null;
        var onSearch = attrs.onSearchPromise || null;
        var optionsNode = attrs.optionsNode || null;
        var multiple = attrs.multiple || null;
        var oldSearch = '';
        var initOptions;
        var disableSearchThreshold = attrs.disableSearchThreshold || 0;
        var allowSingleDeselect = attrs.allowSingleDeselect || false;
        allowSingleDeselect = allowSingleDeselect == 'true';

        // init chosen
        var options = {
          disable_search_threshold: disableSearchThreshold
        };
        var originStyleClass = elem.attr('class').split(' ')
          .filter(function(item) {
            item = $.trim(item);
            return (item != 'ntd-chosen' &&
              item.match(/^ng\-.*$/) === null);
          });
        var chosenEl = elem.chosen(options);
        var chosen = chosenEl.data('chosen');
        chosen.container.css('width', '');
        chosen.container.addClass(originStyleClass.join(' '));
        var selected_options = {};
        var searchTxt = scope.$new(false);

        if (onSearch) {
          // 不进入匹配模式
          chosen.winnow_results = function() {
            this.no_results_clear();
            var searchText = this.get_search_text();
            var results_data = this.results_data;
            var option_number = 0;

            for (var i = 0; i < results_data.length; i++) {
              var option = results_data[i];
              if (!option.empty) {
                option_number++;
                // 永远可被匹配
                option.search_match = true;
                option.search_text = option.group ? option.label : option.html;
              }
            }

            // 按照有效选项数量判断现实
            if (option_number <= 0) {
              this.update_results_content('');
              this.result_clear_highlight();
              return this.no_results(searchText);
            } else {
              this.update_results_content(this.results_option_build());
              return this.winnow_results_set_highlight();
            }
          };

          // 搜索模式时, 刷新chosen 不清空搜索框内容且
          chosen.show_search_field_default = function() {
            if (this.is_multiple && this.choices_count() < 1 &&
              !this.active_field) {
              this.search_field.val(this.default_text);
              return this.search_field.addClass('default');
            } else {
              return this.search_field.removeClass('default');
            }
          };
        }

        // reset allow single deselect
        chosen.allow_single_deselect = allowSingleDeselect;

        // watch for ng-options change
        if (ngOptions) {
          var optionsModelName = ngOptions.split(' ').pop();
          var optionsModelGetter = $parse(optionsModelName);
          var optionsModelSetter = optionsModelGetter.assign;
          scope.$watch(optionsModelName, function(newValue, oldValue) {
            chosenEl.trigger('liszt:data_loaded', {
              options: newValue,
              optionsModelName: optionsModelName
            });
          }, true);
        }

        // watch for ng-model change
        if (ngModelName) {
          scope.$watch(ngModelName, function(newValue, oldValue) {
            // if multiple chosen
            if (multiple) {
              ng.forEach(newValue, function(value) {
                if (!selected_options[value]) {
                  ng.forEach(optionsModelGetter(scope), function(item, index) {
                    if (item.id == value) {
                      selected_options[value] = optionsModelGetter(
                        scope
                      )[index];
                    }
                  });
                }
              });
            }
            if (newValue !== oldValue) {
              elem.trigger('liszt:updated');
            }
          }, true);
        }

        //reselect
        chosenEl.bind('liszt:hiding_dropdown', function(e) {
          if (!chosen.active_field && ng.isArray(initOptions)) {
            optionsModelSetter(scope, initOptions);
            searchTxt.$apply(function() {
              searchTxt.$search = '';
              oldSearch = '';
            });
            $timeout(function() {
              chosenEl.trigger('liszt:updated');
              chosen.search_field.val(searchTxt.$search);
            });
          } else if (chosen.active_field) {
            initOptions = optionsModelGetter(scope);
          }
        });

        // set chosen object
        chosenEl.bind('liszt:showing_dropdown', function(e, data) {
          if (onSearch) {
            if (!searchTxt.$search) {
              $timeout(function() {
                chosen.search_results.find('.no-results').text('请输入关键字...');
              });
              return;
            }
            // if single then set search field
            if (!multiple) {
              chosen.search_field.val(searchTxt.$search);
            }
            chosenEl.trigger('liszt:load_data', {
              onSearch: onSearch,
              optionsModelName: optionsModelName,
              needRecord: true
            });
          }
        });

        // load options data
        chosenEl.bind('liszt:load_data', function(e, data) {
          var promise = searchTxt.$eval(data.onSearch);
          // add loading pic
          chosen.search_field.addClass('loading');
          // add loading tip
          chosen.search_results.find('.no-results').text('加载中...');
          promise.then(
            function(result) {
              var options = [];
              if (optionsNode) {
                options = result[optionsNode];
              } else {
                options = result;
              }
              if (!ng.isArray(options)) {
                options = [];
              }
              if (data.needRecord && !initOptions) {
                initOptions = options;
              }
              chosenEl.trigger('liszt:data_loaded', {
                options: options,
                optionsModelName: data.optionsModelName
              });
            }
          );
        });

        // options data loaded
        chosenEl.bind('liszt:data_loaded', function(e, data) {
          if (onSearch) {
            // remove loading pic
            chosen.search_field.removeClass('loading');
            // load new options
            if (ng.isArray(data.options) && data.options.length > 0) {
              if (!initOptions) {
                initOptions = data.options;
              }
              optionsModelSetter(scope, data.options);
            } else {
              // show no results tip
              optionsModelSetter(scope, []);
            }
            if (multiple) {
              // concat selected options into loaded options
              ng.forEach(selected_options, function(selectedOption) {
                var hasOption = false;
                ng.forEach(optionsModelGetter(scope), function(option) {
                  if (option.id == selectedOption.id) {
                    hasOption = true;
                    return;
                  }
                });
                if (!hasOption) {
                  var options = optionsModelGetter(scope);
                  options.push(selectedOption);
                  if (ng.isArray(options)) {
                    optionsModelSetter(scope, options);
                  }
                }
              });
            }
          }
          // refresh chosen when options loaded
          $timeout(function() {
            chosenEl.trigger('liszt:updated');
            if (!searchTxt.$search) {
              chosen.search_results.find('.no-results').text('请输入关键字...');
            }
          });
        });

        // get new option list
        if (onSearch && optionsModelName) {
          // if chosen.search_field changed, callback onSearch func
          chosen.search_field.bind('keyup', function(e) {
            if (chosen && chosen.results_showing) {
              searchTxt.$search = chosen.get_search_text();
              $timeout(function() {
                  if (oldSearch != searchTxt.$search) {
                    oldSearch = searchTxt.$search;
                    chosenEl.trigger('liszt:load_data', {
                      onSearch: onSearch,
                      optionsModelName: optionsModelName
                    });
                  }
              },500);
            }
          });
        }

        // refresh the option list when chosen changed
        chosenEl.change(function(e) {
          elem.trigger('liszt:updated');
        });

      }
    };
  };

  var Linkage = function($parse) {
    return {
      restrict: 'AC',
      template: '' +
      ' <select class="col-sm-3" data-ntd-chosen' +
      ' data-placeholder="请选择"' +
      ' data-disable-search-threshold="10"' +
      ' data-ng-change="change($index)"' +
      ' data-ng-model="values[$index]"' +
      ' data-allow-single-deselect="true"' +
      ' data-ng-options="option as option.name' +
      ' for option in linkage">' +
      ' <option value=""></option>' +
      '</select>',
      scope: {
        source: '=',
        ngModel: '='
      },
      link: function(scope, elem, attrs) {
        var baseLevels;
        scope.$watch('source', function(value, oldValue) {
          if (!ng.isArray(scope.ngModel)) {
            scope.ngModel = [];
          }
          initOptions();
          changeSelect();
        }, true);

        var initOptions = function() {
          baseLevels = [];
          scope.values = [];
          scope.linkages = [];
          ng.forEach(scope.source, function(item) {
            baseLevels.push(item);
          });
          scope.linkages.push(baseLevels);
        };

        var changeSelect = function() {
          if (scope.ngModel.length > 0) {
            ng.forEach(scope.ngModel, function(id, index) {
              ng.forEach(scope.linkages[index], function(item) {
                if (item.id == id) {
                  scope.values[index] = item;
                  scope.change(index);
                }
              });
            });
          } else {
            scope.values[0] = '';
            scope.change(0);
          }
        };

        scope.change = function(index) {
          var tmpLevels = [];
          var level = scope.linkages.length - 1;
          var offset = scope.values[index];
          var values = [];
          if (!offset) {
            scope.linkages.splice(index + 1, level - index);
            scope.values.splice(index + 1, level - index);
          } else {
            if (offset.children) {
              ng.forEach(offset.children, function(item) {
                tmpLevels.push(item);
              });
            }
            if (level <= index && tmpLevels.length > 0) {
              scope.linkages.push(tmpLevels);
            } else if (index < level) {
              scope.linkages.splice(index + 1, level - index);
              scope.values.splice(index + 1, level - index);
              if (tmpLevels.length > 0) {
                scope.linkages[index + 1] = tmpLevels;
              }
            }
          }
          $(scope.values).each(function(index, item) {
            if (!!item == true && !!item.id == true) {
              values.push(item.id);
            }
          });
          scope.ngModel = values;
        };
      }
    };
  };

  app.directive('ntdChosen', ['$parse', '$timeout', Chosen]);
  app.directive('ntdLinkage', ['$parse', Linkage]);
})(angular.module('ntd.directives'), angular);
<|endoftext|>


<commit_before>var config = require('./config.json'),
  logger = require('./my_modules/logger.js'),
  Web = require('./my_modules/web.js'),
  DB = require('./my_modules/db.js'),
  BaseShowScraper = require('./my_modules/baseShowScraper.js'),
  EpisodeActorGrabber = require('./my_modules/episodeActorGrabber.js'),
  ActorCreditsGrabber = require('./my_modules/actorCreditsGrabber.js');

var db = new DB(config);

var queueAllActors = function() {
  db.connect('queueUpAllActors', function(){
    db.query({
      sql: 'Insert into ProcessActors(ActorID) select ActorID from Actors a where not exists (select 1 from ProcessActors pa where pa.ActorID = a.ActorID);',
      inserts: []
    }, function() {
      db.disconnect();
    });
  });
};

var scrapeBaseTitle = function(baseId) {
  var base = new BaseShowScraper(config, baseId);

  base.on('done',function(baseId) {
    logger.log({
      caller: 'BaseShowScraper',
      message: 'done',
      params: baseId
    });
  });

  base.start();
};

var startBaseTitles = function() {
  db.connect('queueUpAllActors', function(){
    db.query({
      sql: 'select * from BaseTitles;',
      inserts: []
    }, function(dbRes) {
      for (var i = 0; i < dbRes.length; i++) {
        scrapeBaseTitle(dbRes[i].BaseTitleID);
      }
      db.disconnect();
    });
  });
};

var cast = new EpisodeActorGrabber(config);
var credits = new ActorCreditsGrabber(config);

cast.start();
credits.start();

startBaseTitles();

var web = new Web(config);
web.startServer();<commit_msg>add checking intervals<commit_after>var config = require('./config.json'),
  logger = require('./my_modules/logger.js'),
  Web = require('./my_modules/web.js'),
  DB = require('./my_modules/db.js'),
  BaseShowScraper = require('./my_modules/baseShowScraper.js'),
  EpisodeActorGrabber = require('./my_modules/episodeActorGrabber.js'),
  ActorCreditsGrabber = require('./my_modules/actorCreditsGrabber.js');

var db = new DB(config);
var day = 1000 * 60 * 60 * 24;

var queueAllActors = function() {
  db.connect('queueUpAllActors', function(){
    db.query({
      sql: 'Insert into ProcessActors(ActorID) select ActorID from Actors a where not exists (select 1 from ProcessActors pa where pa.ActorID = a.ActorID);',
      inserts: []
    }, function() {
      db.disconnect();
    });
  });
};

var scrapeBaseTitle = function(baseId) {
  var base = new BaseShowScraper(config, baseId);

  base.on('done',function(baseId) {
    logger.log({
      caller: 'BaseShowScraper',
      message: 'done',
      params: baseId
    });

    setTimeout(function() {
      scrapeBaseTitle(baseId);
    }, day*7);
  });

  base.start();
};

var startBaseTitles = function() {
  db.connect('queueUpAllActors', function(){
    db.query({
      sql: 'select * from BaseTitles;',
      inserts: []
    }, function(dbRes) {
      for (var i = 0; i < dbRes.length; i++) {
        scrapeBaseTitle(dbRes[i].BaseTitleID);
      }
      db.disconnect();
    });
  });
};

var cast = new EpisodeActorGrabber(config);
var credits = new ActorCreditsGrabber(config);

cast.start();
credits.start();

startBaseTitles();

var web = new Web(config);
web.startServer();

setInterval(queueAllActors, day*3);<|endoftext|>


<commit_before>require( [
    'require-config'
  ],
  function() {
  require(['backbone', 'erfgeoviewer.common', 'communicator', 'jquery', 'config', 'q',
    'views/map', 'views/layout/header.layout', 'views/new', 'views/open', 'views/search/search', 'views/settings',
    'views/detail', 'views/detail-settings', 'views/publish', 'views/intro/header', 'views/intro/actions', 'views/intro/list',
    'views/layout/detail.layout', 'views/layout/intro.layout',
    'plugins/routeyou/routeyou', 'erfgeoviewer.search',
    'models/layers', 'models/state', 'models/sidenav', 'models/navbar'],

  function(Backbone, App, Communicator, $, Config, Q,
           MapView, HeaderView, NewMapView, OpenMapView, SearchView, SettingsView, DetailView, DetailSettingsView,
           PublishView, IntroHeaderView, IntroActionsView, IntroListView, DetailLayout, IntroLayout,
           RouteyouModule, SearchModule,
           LayerCollection, State, SideNav, NavBar) {

    /**
     * Init.
     */
    App.mode = "mapmaker";
    App.container.$el.addClass( "mode-" + App.mode );
    console.log('Erfgeoviewer: mapmaker mode.');

    // This object will be serialized and used for storing/restoring a map.
//    var state = new StateModel({
//      id: 1,
//      mapSettings: {
//        primaryColor: Config.colors.primary,
//        secondaryColor: Config.colors.secondary
//      }
//    });

    /**
     * Event handlers.
     */

    Communicator.mediator.on('map:ready', function() {
//      State.fetch();
    });
    Communicator.mediator.on("marker:click", function(m) {
      var detailLayout = new DetailLayout();
      App.flyouts.getRegion('detail').show( detailLayout );
      detailLayout.getRegion('controls').show( new DetailSettingsView( { model: m } ) );
      detailLayout.getRegion('container').show( new DetailView( { model: m } ) );

    });
    Communicator.mediator.on( "all", function( e, a ) {
      // Debugging:
      console.log( "EVENT '" + e + "'", a );
    } );
    Communicator.mediator.on( 'marker:removeModelByCid', function() {
      App.flyouts.getRegion('detail').hideFlyout();
    });

    /**
     * Router.
     */
    var searchView;
    var Router = Marionette.AppRouter.extend( {
      routes: {
        "": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).hideFlyout();
        },
        "new": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.layout.getRegion( 'modal' ).show(new NewMapView());
        },
        "open": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.layout.getRegion( 'modal' ).show(new OpenMapView());
        },
        "export": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).show(new PublishView());
        },
        "settings": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).show(new SettingsView());
        },
        "search": function() {
          var searchModule = new SearchModule({
            markers_collection: State.getPlugin('geojson_features').collection
          });

          if (!searchView || searchView.isDestroyed) {
            searchView = new SearchView({ searchModule: searchModule });
          }
          App.flyouts.getRegion( 'bottom' ).hideFlyout();

          if (App.flyouts.getRegion( 'right' ).hasView(searchView)) {
            App.flyouts.getRegion( 'right' ).expand();
          }
          App.flyouts.getRegion( 'right' ).show( searchView );
        }
      }
    } );
    App.router = new Router();
    Communicator.reqres.setHandler("app:get", function() { return App; });

    /**
     * Optional modules.
     */

    SideNav.addItem('new_map', {
      fragment: 'new',
      icon: 'map',
      label: 'Nieuwe kaart'
    });

    SideNav.addItem('open_map', {
      fragment: 'open',
      icon: 'folder-open-empty',
      label: 'Open'
    });

    NavBar.addItem('save', {
      fragment: 'export',
      label: 'Exporteer',
      weight: 900
    });

    NavBar.addItem('add', {
      fragment: 'search',
      label: 'Zoek',
      weight: 1000
    });

//    new RouteyouModule();

    /**
     * Initialize map.
     */
    State.pluginsInitialized.promise
      .then(function() {
        var d = Q.defer();

        State.fetch({
          success: d.resolve,
          error: function() {
            App.layout.getRegion( 'modal' ).show( IntroLayout );

            IntroLayout.getRegion( 'header' ).show( new IntroHeaderView() );
            IntroLayout.getRegion( 'content' ).show( new IntroActionsView() );
            IntroLayout.getRegion( 'footer' ).show( new IntroListView() );

            d.resolve(); // Also resolve on error to prevent unhandled exceptions on empty state
          }
        });

        return d.promise;
      })
      .then(function() {
        return Config.makiCollection.fetch();
      })
      .done(function() {
        App.map_view = new MapView({
          layout: App.layout
        });

        App.layout.getRegion( 'content' ).show( App.map_view );
        App.layout.getRegion( 'header' ).show( new HeaderView() );

        App.start();
      });

  });

});<commit_msg>second commit for #9064<commit_after>require( [
    'require-config'
  ],
  function() {
  require(['backbone', 'erfgeoviewer.common', 'communicator', 'jquery', 'config', 'q',
    'views/map', 'views/layout/header.layout', 'views/new', 'views/open', 'views/search/search', 'views/settings',
    'views/detail', 'views/detail-settings', 'views/publish', 'views/intro/header', 'views/intro/actions', 'views/intro/list',
    'views/layout/detail.layout', 'views/layout/intro.layout',
    'plugins/routeyou/routeyou', 'erfgeoviewer.search',
    'models/layers', 'models/state', 'models/sidenav', 'models/navbar'],

  function(Backbone, App, Communicator, $, Config, Q,
           MapView, HeaderView, NewMapView, OpenMapView, SearchView, SettingsView, DetailView, DetailSettingsView,
           PublishView, IntroHeaderView, IntroActionsView, IntroListView, DetailLayout, IntroLayout,
           RouteyouModule, SearchModule,
           LayerCollection, State, SideNav, NavBar) {

    /**
     * Init.
     */
    App.mode = "mapmaker";
    App.container.$el.addClass( "mode-" + App.mode );
    console.log('Erfgeoviewer: mapmaker mode.');

    // This object will be serialized and used for storing/restoring a map.
//    var state = new StateModel({
//      id: 1,
//      mapSettings: {
//        primaryColor: Config.colors.primary,
//        secondaryColor: Config.colors.secondary
//      }
//    });

    /**
     * Event handlers.
     */

    Communicator.mediator.on('map:ready', function() {
//      State.fetch();
    });
    Communicator.mediator.on("marker:click", function(m) {
      var detailLayout = new DetailLayout();
      App.flyouts.getRegion('detail').show( detailLayout );
      detailLayout.getRegion('controls').show( new DetailSettingsView( { model: m } ) );
      detailLayout.getRegion('container').show( new DetailView( { model: m } ) );

    });
    Communicator.mediator.on( "all", function( e, a ) {
      // Debugging:
      console.log( "EVENT '" + e + "'", a );
    } );
    Communicator.mediator.on( 'marker:removeModelByCid', function() {
      App.flyouts.getRegion('detail').hideFlyout();
    });

    /**
     * Router.
     */
    var searchView;
    var Router = Marionette.AppRouter.extend( {
      routes: {
        "": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).hideFlyout();
        },
        "new": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'detail' ).hideFlyout();
          App.layout.getRegion( 'modal' ).show(new NewMapView());
        },
        "open": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'detail' ).hideFlyout();
          App.layout.getRegion( 'modal' ).show(new OpenMapView());
        },
        "export": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).show(new PublishView());
        },
        "settings": function() {
          App.flyouts.getRegion( 'bottom' ).hideFlyout();
          App.flyouts.getRegion( 'right' ).show(new SettingsView());
        },
        "search": function() {
          var searchModule = new SearchModule({
            markers_collection: State.getPlugin('geojson_features').collection
          });

          if (!searchView || searchView.isDestroyed) {
            searchView = new SearchView({ searchModule: searchModule });
          }
          App.flyouts.getRegion( 'bottom' ).hideFlyout();

          if (App.flyouts.getRegion( 'right' ).hasView(searchView)) {
            App.flyouts.getRegion( 'right' ).expand();
          }
          App.flyouts.getRegion( 'right' ).show( searchView );
        }
      }
    } );
    App.router = new Router();
    Communicator.reqres.setHandler("app:get", function() { return App; });

    /**
     * Optional modules.
     */

    SideNav.addItem('new_map', {
      fragment: 'new',
      icon: 'map',
      label: 'Nieuwe kaart'
    });

    SideNav.addItem('open_map', {
      fragment: 'open',
      icon: 'folder-open-empty',
      label: 'Open'
    });

    NavBar.addItem('save', {
      fragment: 'export',
      label: 'Exporteer',
      weight: 900
    });

    NavBar.addItem('add', {
      fragment: 'search',
      label: 'Zoek',
      weight: 1000
    });

//    new RouteyouModule();

    /**
     * Initialize map.
     */
    State.pluginsInitialized.promise
      .then(function() {
        var d = Q.defer();

        State.fetch({
          success: d.resolve,
          error: function() {
            App.layout.getRegion( 'modal' ).show( IntroLayout );

            IntroLayout.getRegion( 'header' ).show( new IntroHeaderView() );
            IntroLayout.getRegion( 'content' ).show( new IntroActionsView() );
            IntroLayout.getRegion( 'footer' ).show( new IntroListView() );

            d.resolve(); // Also resolve on error to prevent unhandled exceptions on empty state
          }
        });

        return d.promise;
      })
      .then(function() {
        return Config.makiCollection.fetch();
      })
      .done(function() {
        App.map_view = new MapView({
          layout: App.layout
        });

        App.layout.getRegion( 'content' ).show( App.map_view );
        App.layout.getRegion( 'header' ).show( new HeaderView() );

        App.start();
      });

  });

});<|endoftext|>


<commit_before>angular.module('appverseClientIncubatorApp')

    /*
     * Factory Name: 'topics'
     * This factory contains is RESTful for retrieving topics
     * from data (in this case'topics.json').
     */
  .factory('topics', ['$http', function ($http, utils) {
    var path = 'views/topics/topics.json';
    var topics = $http.get(path).then(function (resp) {
      return resp.data.topics;
    });

    var factory = {};
    factory.all = function () {
      return topics;
    };
    factory.get = function (id) {
      return topics.then(function(){
        return utils.findById(topics, id);
      })
    };
    return factory;
  }])

   /*
    * Factory Name: 'utils'
    * Contains methods for data finding (demo).
    *
    * findById: Util for finding an object by its 'id' property among an array
    * newRandomKey: Util for returning a randomKey from a collection that also isn't the current key
    */
  .factory('utils', function () {

    return {

      //
      findById: function findById(a, id) {
        for (var i = 0; i < a.length; i++) {
          if (a[i].id == id) return a[i];
        }
        return null;
      },

      newRandomKey: function newRandomKey(coll, key, currentKey){
        var randKey;
        do {
          randKey = coll[Math.floor(coll.length * Math.random())][key];
        } while (randKey == currentKey);
        return randKey;
      }

    };

  });
<commit_msg>- Formatted to comply with JSHint - Added RESTFactory dependency - Used RESTFactory to retrieve topics list<commit_after>'use strict';

angular.module('appverseClientIncubatorApp')

/*
 * Factory Name: 'topics'
 * This factory contains is RESTful for retrieving topics
 * from data (in this case'topics.json').
 */
.factory('topics', ['$http', 'RESTFactory',
    function ($http, RESTFactory, utils) {

        var path = 'views/topics/topics.json';

        var topics = RESTFactory.rest_getAll(path);

        console.log("topics", topics);

        var factory = {};

        factory.all = function () {
            return topics;
        };
        factory.get = function (id) {
            console.log("factory.get", id);
            return topics.then(function () {
                return utils.findById(topics, id);
            });
        };
        return factory;
    }])

/*
 * Factory Name: 'utils'
 * Contains methods for data finding (demo).
 *
 * findById: Util for finding an object by its 'id' property among an array
 * newRandomKey: Util for returning a randomKey from a collection that also isn't the current key
 */
.factory('utils', function () {

    return {

        //
        findById: function findById(a, id) {
            for (var i = 0; i < a.length; i++) {
                if (a[i].id == id) {
                    return a[i];
                }
            }
            return null;
        },

        newRandomKey: function newRandomKey(coll, key, currentKey) {
            var randKey;
            do {
                randKey = coll[Math.floor(coll.length * Math.random())][key];
            } while (randKey == currentKey);
            return randKey;
        }

    };

});
<|endoftext|>


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

'use strict';

// Extension namespace.
window.LI = window.LI || {};

(function(LI) {
    var BATCH_THRESHOLD = 3000;
    var PAGE_MATCH_THRESHOLD = 1000;

    LI.LoadScriptGenerator = function() {
        this.lastPage = 0;
    };

    LI.LoadScriptGenerator.prototype.generateFromTransactions = function(transactions, start, end) {
        var self = this,
            ir = [],
            pageOpen = false,
            pages = 0,
            batches = 0,
            batch = [],
            lastUrlTime = Date.now();

        // Make sure we have at least one transaction.
        if (!transactions.length) {
            return '-- No HTTP/HTTPS transactions have been recorded.';
        }

        // Build IR from requests.
        transactions.forEach(function(transaction) {
            var time = transaction.timeStamp;

            // Determine if this transaction represents a new page.
            if (self._isTransactionNewPage(transaction)) {
                // Close batch, if open.
                if (batch.length) {
                    ir.push(['batch', batch]);
                    ++batches;
                    batch = [];
                }

                // Close page, if open.
                if (0 < pages) {
                    ir.push(['pageEnd', 'Page ' + pages]);
                    var fracSleep = (time - lastUrlTime) / 1000,
                        sleep = Math.round(fracSleep);
                    if (sleep > 0) {
                        ir.push(['sleep', sleep]);
                    } else if (fracSleep >= 0.1) {
                        ir.push(['sleep', fracSleep.toFixed(2)]);
                    }
                    pageOpen = false;
                }

                // Start page.
                ir.push(['pageStart', 'Page ' + (pages + 1)]);
                pageOpen = true;
                ++pages;

                // Add page as separate batche.
                ir.push(['batch', [transaction]]);
                ++batches;
            } else {
                // Determine if this URL should start a new batch or join an
                // existing.
                if (!batch.length || (time - lastUrlTime) < BATCH_THRESHOLD) {
                    batch.push(transaction);
                    if (0 === batches) {
                        ir.push(['batch', batch]);
                        ++batches;
                        batch = [];
                    }
                } else {
                    ir.push(['batch', batch]);
                    ++batches;
                    batch = [transaction];
                }
            }

            lastUrlTime = time;
        });

        if (batch.length) {
            ir.push(['batch', batch]);
        }

        if (pageOpen) {
            ir.push(['pageEnd', 'Page ' + pages]);
        }

        ir.push(['sleep', 'math.random(20, 40)']);

        // Compile IR to Lua script.
        return this._compileIRToLua(ir);
    };

    LI.LoadScriptGenerator.prototype._convertFormDataToBodyData = function(formData) {
        var params = [];
        Object.keys(formData).forEach(function(key) {
            params.push(encodeURIComponent(key) + '=' + encodeURIComponent(formData[key][0]));
        });
        return params.join('&');
    };

    LI.LoadScriptGenerator.prototype._getRequestContentType = function(transaction) {
        var contentType = '';
        if (transaction.requestHeaders) {
            transaction.requestHeaders.forEach(function(header) {
                if ('Content-Type' === header.name) {
                    contentType = header.value;
                }
            });
        }
        return contentType;
    };

    LI.LoadScriptGenerator.prototype._getResponseContentType = function(transaction) {
        var contentType = '';
        if (transaction.responseHeaders) {
            transaction.responseHeaders.forEach(function(header) {
                if ('Content-Type' === header.name) {
                    contentType = header.value;
                }
            });
        }
        return contentType;
    };

    LI.LoadScriptGenerator.prototype._isTransactionXHR = function(transaction) {
        var isXHR = false;
        if (transaction.responseHeaders) {
            transaction.responseHeaders.forEach(function(header) {
                if ('X-Requested-With' === header.name &&
                    'XMLHttpRequest' === header.value) {
                    isXHR = true;
                }
            });
        }
        return isXHR;
    };

    LI.LoadScriptGenerator.prototype._isTransactionNewPage = function(transaction) {
        // Only accept 200 codes. This avoids redirect pages that often contain
        // proper HTML.
        if (200 !== transaction.responseStatusCode) {
            return false;
        }

        // Require "Content-Type" header.
        var contentType = this._getResponseContentType(transaction),
            hasContentType = '' !== contentType;
        if (!hasContentType) {
            return false;
        }

        // No XHR requests.
        if (this._isTransactionXHR(transaction)) {
            return false;
        }

        // Check for document/page content type.
        var hasDoContentType = false;
        ['text/html', 'application/xhtml', 'application/xml'].forEach(function(docContentType) {
            if (-1 !== contentType.indexOf(docContentType)) {
                hasDoContentType = true;
            }
        });
        if (!hasDoContentType) {
            return false;
        }

        // Check when last page was seen (if any) and how long ago that was.
        if (0 === this.lastPage ||
            (transaction.timeStamp - this.lastPage) >= PAGE_MATCH_THRESHOLD) {
            this.lastPage = transaction.timeStamp;
            return true;
        }

        return false;
    };

    LI.LoadScriptGenerator.prototype._compileIRToLua = function(ir) {
        var self = this,
            script = '';
        ir.forEach(function(node) {
            if ('batch' === node[0]) {
                script += "http.request_batch({\n";
                var requests = [];
                node[1].forEach(function(transaction) {
                    var method = transaction.method.toUpperCase(),
                        headers = {},
                        bodyMethods = ['POST', 'PUT', 'PATCH'],
                        body = transaction.requestBody,
                        contentType = self._getRequestContentType(transaction),
                        base64ContentTypes = ['multipart/form-data',
                                              'application/x-amf',
                                              'application/octet-stream',
                                              'image/tiff',
                                              'image/gif'],
                        requestIR = [method, transaction.url];

                    // Add X-headers.
                    if (transaction.requestHeaders) {
                        transaction.requestHeaders.forEach(function(header) {
                            if (0 === header.name.indexOf('X-')) {
                                headers[header.name] = header.value;
                            } else if (0 === header.name.indexOf('Authorization')) {
                                headers[header.name] = header.value;
                            }
                        });
                    }

                    // Handle request body.
                    if (transaction.requestBody && transaction.requestBody !== '') {
                        var shouldBase64Body = false;
                        if ($.inArray(method, bodyMethods) &&
                            '' !== contentType &&
                            $.inArray(contentType, base64ContentTypes)) {
                            shouldBase64Body = true;
                        } else if (/[\x00-\x08\x0E-\x1F\x80-\xFF]/.test(body)) { // Look for binary data.
                            shouldBase64Body = true;
                        }

                        headers['Content-Type'] = contentType;
                        requestIR.push(['headers', headers]);

                        if (shouldBase64Body) {
                            // check here whene pushing data
                            //
                            requestIR.push(['data', '"' + btoa(body[0].bytes) + '"']);
                            requestIR.push(['base64_encoded_body', 'true']);
                        } else {
                            if ($.isPlainObject(body)) {
                                requestIR.push(['data', '"' + self._convertFormDataToBodyData(body) + '"']);
                            } else {
                                var bodyAsString = String.fromCharCode.apply(null, new Uint8Array(body[0].bytes));
                                requestIR.push(['data', '"' + bodyAsString.replace(/"/g, '\\\"').replace(/[\r\n]/g, "") + '"']);
                            }
                        }
                    } else if (Object.keys(headers).length) {
                        requestIR.push(['headers', headers]);
                    }

                    // Add compression handling.
                    requestIR.push(['auto_decompress', 'true']);

                    // Compile request IR into a Lua string.
                    requests.push("\t{" + self._compileRequestIRToLua(requestIR) + "}");
                });
                script += requests.join(",\n") + "\n})\n\n";
            } else if ('pageStart' === node[0]) {
                script += 'http.page_start("' + node[1] + '")\n';
            } else if ('pageEnd' === node[0]) {
                script += 'http.page_end("' + node[1] + '")\n\n';
            } else if ('sleep' === node[0]) {
                script += 'client.sleep(' + node[1] + ')\n\n';
            }
        });
        return script;
    };

    LI.LoadScriptGenerator.prototype._compileRequestIRToLua = function(requestIR) {
        var params = [];
        requestIR.forEach(function(param) {
            if ($.isArray(param)) {
                if ($.isPlainObject(param[1])) {
                    var items = [];
                    Object.keys(param[1]).forEach(function(key) {
                        items.push('["' + key + '"]="' + param[1][key] + '"');
                    });
                    params.push(param[0] + '=' + '{' + items.join(',') + '}');
                } else {
                    params.push(param[0] + '=' + param[1]);
                }
            } else {
                params.push('"' + param + '"');
            }
        });
        return params.join(', ');
    };
})(LI);
<commit_msg>LoadScriptGenerator does not encode as Base64 when content is type of ArrayBuffer<commit_after>/*
 * Copyright 2014 Load Impact
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

// Extension namespace.
window.LI = window.LI || {};

(function(LI) {
    var BATCH_THRESHOLD = 3000;
    var PAGE_MATCH_THRESHOLD = 1000;

    LI.LoadScriptGenerator = function() {
        this.lastPage = 0;
    };

    LI.LoadScriptGenerator.prototype.generateFromTransactions = function(transactions, start, end) {
        var self = this,
            ir = [],
            pageOpen = false,
            pages = 0,
            batches = 0,
            batch = [],
            lastUrlTime = Date.now();

        // Make sure we have at least one transaction.
        if (!transactions.length) {
            return '-- No HTTP/HTTPS transactions have been recorded.';
        }

        // Build IR from requests.
        transactions.forEach(function(transaction) {
            var time = transaction.timeStamp;

            // Determine if this transaction represents a new page.
            if (self._isTransactionNewPage(transaction)) {
                // Close batch, if open.
                if (batch.length) {
                    ir.push(['batch', batch]);
                    ++batches;
                    batch = [];
                }

                // Close page, if open.
                if (0 < pages) {
                    ir.push(['pageEnd', 'Page ' + pages]);
                    var fracSleep = (time - lastUrlTime) / 1000,
                        sleep = Math.round(fracSleep);
                    if (sleep > 0) {
                        ir.push(['sleep', sleep]);
                    } else if (fracSleep >= 0.1) {
                        ir.push(['sleep', fracSleep.toFixed(2)]);
                    }
                    pageOpen = false;
                }

                // Start page.
                ir.push(['pageStart', 'Page ' + (pages + 1)]);
                pageOpen = true;
                ++pages;

                // Add page as separate batche.
                ir.push(['batch', [transaction]]);
                ++batches;
            } else {
                // Determine if this URL should start a new batch or join an
                // existing.
                if (!batch.length || (time - lastUrlTime) < BATCH_THRESHOLD) {
                    batch.push(transaction);
                    if (0 === batches) {
                        ir.push(['batch', batch]);
                        ++batches;
                        batch = [];
                    }
                } else {
                    ir.push(['batch', batch]);
                    ++batches;
                    batch = [transaction];
                }
            }

            lastUrlTime = time;
        });

        if (batch.length) {
            ir.push(['batch', batch]);
        }

        if (pageOpen) {
            ir.push(['pageEnd', 'Page ' + pages]);
        }

        ir.push(['sleep', 'math.random(20, 40)']);

        // Compile IR to Lua script.
        return this._compileIRToLua(ir);
    };

    LI.LoadScriptGenerator.prototype._convertFormDataToBodyData = function(formData) {
        var params = [];
        Object.keys(formData).forEach(function(key) {
            params.push(encodeURIComponent(key) + '=' + encodeURIComponent(formData[key][0]));
        });
        return params.join('&');
    };

    LI.LoadScriptGenerator.prototype._getRequestContentType = function(transaction) {
        var contentType = '';
        if (transaction.requestHeaders) {
            transaction.requestHeaders.forEach(function(header) {
                if ('Content-Type' === header.name) {
                    contentType = header.value;
                }
            });
        }
        return contentType;
    };

    LI.LoadScriptGenerator.prototype._getResponseContentType = function(transaction) {
        var contentType = '';
        if (transaction.responseHeaders) {
            transaction.responseHeaders.forEach(function(header) {
                if ('Content-Type' === header.name) {
                    contentType = header.value;
                }
            });
        }
        return contentType;
    };

    LI.LoadScriptGenerator.prototype._isTransactionXHR = function(transaction) {
        var isXHR = false;
        if (transaction.responseHeaders) {
            transaction.responseHeaders.forEach(function(header) {
                if ('X-Requested-With' === header.name &&
                    'XMLHttpRequest' === header.value) {
                    isXHR = true;
                }
            });
        }
        return isXHR;
    };

    LI.LoadScriptGenerator.prototype._isTransactionNewPage = function(transaction) {
        // Only accept 200 codes. This avoids redirect pages that often contain
        // proper HTML.
        if (200 !== transaction.responseStatusCode) {
            return false;
        }

        // Require "Content-Type" header.
        var contentType = this._getResponseContentType(transaction),
            hasContentType = '' !== contentType;
        if (!hasContentType) {
            return false;
        }

        // No XHR requests.
        if (this._isTransactionXHR(transaction)) {
            return false;
        }

        // Check for document/page content type.
        var hasDoContentType = false;
        ['text/html', 'application/xhtml', 'application/xml'].forEach(function(docContentType) {
            if (-1 !== contentType.indexOf(docContentType)) {
                hasDoContentType = true;
            }
        });
        if (!hasDoContentType) {
            return false;
        }

        // Check when last page was seen (if any) and how long ago that was.
        if (0 === this.lastPage ||
            (transaction.timeStamp - this.lastPage) >= PAGE_MATCH_THRESHOLD) {
            this.lastPage = transaction.timeStamp;
            return true;
        }

        return false;
    };

    LI.LoadScriptGenerator.prototype._compileIRToLua = function(ir) {
        var self = this,
            script = '';
        ir.forEach(function(node) {
            if ('batch' === node[0]) {
                script += "http.request_batch({\n";
                var requests = [];
                node[1].forEach(function(transaction) {
                    var method = transaction.method.toUpperCase(),
                        headers = {},
                        bodyMethods = ['POST', 'PUT', 'PATCH'],
                        body = transaction.requestBody,
                        contentType = self._getRequestContentType(transaction),
                        base64ContentTypes = ['multipart/form-data',
                                              'application/x-amf',
                                              'application/octet-stream',
                                              'image/tiff',
                                              'image/gif'],
                        requestIR = [method, transaction.url];

                    // Add X-headers.
                    if (transaction.requestHeaders) {
                        transaction.requestHeaders.forEach(function(header) {
                            if (0 === header.name.indexOf('X-')) {
                                headers[header.name] = header.value;
                            } else if (0 === header.name.indexOf('Authorization')) {
                                headers[header.name] = header.value;
                            }
                        });
                    }

                    // Handle request body.
                    if (body && body !== '') {

                        headers['Content-Type'] = contentType;
                        requestIR.push(['headers', headers]);

                        var shouldBase64Body = false;
                        var isArrayBuffer = (body instanceof Array &&
                                             body[0] && body[0].bytes &&
                                             body[0].bytes instanceof ArrayBuffer);

                        if (!isArrayBuffer) {
                          if ($.inArray(method, bodyMethods) &&
                              '' !== contentType &&
                              $.inArray(contentType, base64ContentTypes)) {
                              shouldBase64Body = true;
                          } else if (/[\x00-\x08\x0E-\x1F\x80-\xFF]/.test(body)) { // Look for binary data.
                              shouldBase64Body = true;
                          }
                        }

                        if (shouldBase64Body) {
                            requestIR.push(['data', '"' + btoa(body[0].bytes) + '"']);
                            requestIR.push(['base64_encoded_body', 'true']);
                        } else {
                            if ($.isPlainObject(body)) {
                                requestIR.push(['data', '"' + self._convertFormDataToBodyData(body) + '"']);
                            } else {
                                // serialize ArrayBuffer with Uint16Array fails
                                var bodyAsString = String.fromCharCode.apply(null, new Uint8Array(body[0].bytes));
                                requestIR.push(['data', '"' + bodyAsString.replace(/"/g, '\\\"').replace(/[\r\n]/g, "") + '"']);
                            }
                        }
                    } else if (Object.keys(headers).length) {
                        requestIR.push(['headers', headers]);
                    }

                    // Add compression handling.
                    requestIR.push(['auto_decompress', 'true']);

                    // Compile request IR into a Lua string.
                    requests.push("\t{" + self._compileRequestIRToLua(requestIR) + "}");
                });
                script += requests.join(",\n") + "\n})\n\n";
            } else if ('pageStart' === node[0]) {
                script += 'http.page_start("' + node[1] + '")\n';
            } else if ('pageEnd' === node[0]) {
                script += 'http.page_end("' + node[1] + '")\n\n';
            } else if ('sleep' === node[0]) {
                script += 'client.sleep(' + node[1] + ')\n\n';
            }
        });
        return script;
    };

    LI.LoadScriptGenerator.prototype._compileRequestIRToLua = function(requestIR) {
        var params = [];
        requestIR.forEach(function(param) {
            if ($.isArray(param)) {
                if ($.isPlainObject(param[1])) {
                    var items = [];
                    Object.keys(param[1]).forEach(function(key) {
                        items.push('["' + key + '"]="' + param[1][key] + '"');
                    });
                    params.push(param[0] + '=' + '{' + items.join(',') + '}');
                } else {
                    params.push(param[0] + '=' + param[1]);
                }
            } else {
                params.push('"' + param + '"');
            }
        });
        return params.join(', ');
    };
})(LI);
<|endoftext|>