<commit_before>/*
* DAO
*
*
* Copyright (c) 2013 dmccarthy
* Licensed under the MIT license.
*/

'use strict';

var zmq  = require('m2nodehandler')
var nano = require('nano')('http://localhost:5984');
var dbc  = require('dbc');
var flag = false;

var initDB = function(callback){
   if (flag){
      return
   }
   nano.db.destroy('db', function() {
      nano.db.create('db', function() {
         flag = true;
         callback()
      })
   })
}

var handleTypeParameters = function(db,msg) {
	var query = msg.query
	var params = {}
	query.replace(/([^=&]+)=([^&]*)/gi, function(m, key, value) {
		params[key] = value
	})

	var object = null

	db.get(msg.name[1], {revs_info: false}, function(err, db_body)
	{
		var doc
		var data
		var key
		if (typeof params['oids'] !== 'undefined' && params['oids'] === 'true') {
			doc = db_body['types']
			object = doc[msg.name[3]]
		} else if (typeof params['blob'] !== 'undefined' && params['blob'] === 'false') {
			doc = db_body['types']
			object = doc[msg.name[3]]
			data = db_body['data']
			for (key in object) {
				if (data.hasOwnProperty(object[key])) {
					object[key] = data[object[key]]
				}
			}
		} else if (typeof params['blob'] !== 'undefined' && params['blob'] === 'true') {
			doc = db_body['types']
			object = doc[msg.name[3]]
			var blobs = db_body['blobs']
			data = db_body['data']
			var blobkeys = Object.keys(blobs)
			for (key in object) {
				if (typeof key !== 'undefined') {
					console.log(blobkeys.indexOf(data[object[key]]))
					if (data.hasOwnProperty(object[key]) && (blobkeys.indexOf(data[object[key]]) !== -1)) {
						object[key] = blobs[data[object[key]]]
					}
				}
			}
		}

		return object
	})
}

var putAction = function(msg, mongPush){

   dbc.hasMember(msg, 'name')
   dbc.hasMember(msg, 'data')

   var db = nano.use('db');
   db.insert(msg.data, msg.name, function(err){
      var status = zmq.status.OK_200
      var body   = {}
      if(err){
         status = zmq.status.INTERNAL_SERVER_ERROR_500
         body['result'] = 'failure'
         body['error']  = 'Error: ' + err['Error'];
      }
      else{
         body['result'] = 'success'
      }
      var response  = zmq.Response(status, zmq.header_json, body)
      mongPush.publish(msg.uuid, msg.connId, response)
   })
}

var getAction = function(msg, mongPush){

	var db = nano.use('db')
	dbc.hasMember(msg, 'name')
	db.get(msg.name[1], {revs_info: false}, function(err, db_body) {
		var status = zmq.status.OK_200
		var body = {}
		var doc
		var object

		//console.log(doc)
		switch (msg.name.length) {
		case 2:
			object = db_body[msg.name[0]]
			break
		case 3:
			doc = db_body[msg.name[0]]
			object = doc[msg.name[2]]
			break
		case 4:
			if (msg.name[2] === 'type') {
				if (typeof msg.query !== 'undefined') {
					object = handleTypeParameters(db, msg)
				}
				else
				{
					doc = db_body['types']
					object = doc[msg.name[3]]
				}
			} else {
				doc = db_body['data']
				var field = doc[msg.name[2]]
				object = field[msg.name[3]]
			}
			break
		default:
			object = db_body
			break
		}

		if (err) {
			status = zmq.status.INTERNAL_SERVER_ERROR_500
			body['result'] = 'failure'
			body['error'] = 'Error getting data from datastore'
		} else {
			body['result'] = 'success'
			body['value'] = object
		}
		var response = zmq.Response(status, zmq.header_json, body)
		mongPush.publish(msg.uuid, msg.connId, response)
	})
}

var bindToQs = function(){

	var dataPush = zmq.bindToPushQ(     {spec:'tcp://127.0.0.1:49995'})
	var mongPush = zmq.bindToMong2PubQ({spec:'tcp://127.0.0.1:49996', id:'dao_conn'})

	var echo = 0

	zmq.bindToPullQ( {spec:'tcp://127.0.0.1:49994', id:'data_api'}, function( msg )
	{
		dbc.assert    (null !== msg, 'Message cannot be null')
		dbc.hasMember(msg, 'action')
		dbc.hasMember(msg, 'uuid'  )
		dbc.hasMember(msg, 'connId')
		console.log(msg)

		switch(msg.action){
		case 'PUT':
			putAction(msg, mongPush)
			break

		case 'GET':
			getAction(msg, mongPush)
			break

		case 'ECHO':
			msg.body = {echo: echo++}
			dataPush.push(msg)
			break

		default:
			var body        = {error : 'Action parameter is required'}
			var response  = zmq.Response(zmq.status.BAD_REQUEST_400, zmq.header_json, body)
			mongPush.publish(msg.uuid, msg.connId, response)
			break
		}
	})
}

initDB(function(){
	console.log('Database Destroyed and Created')
	bindToQs()
})

module.exports.getAction			= getAction
module.exports.initDB				= initDB
module.exports.handleTypeParameters	= handleTypeParameters

<commit_msg> Get Action refactoring<commit_after>/*
 * DAO
 *
 *
 * Copyright (c) 2013 dconway, dmccarthy
 * Licensed under the MIT license.
 */

'use strict';

var zmq = require('m2nodehandler')
var nano = require('nano')('http://localhost:5984');
var dbc = require('dbc');
var flag = false;

var initDB = function(callback) {
	if (flag) {
		return
	}
	nano.db.destroy('db', function() {
		nano.db.create('db', function() {
			flag = true;
			callback()
		})
	})
}

var handleTypeParameters = function(db_body, msg) {
	var query = msg.query
	var params = {}
	query.replace(/([^=&]+)=([^&]*)/gi, function(m, key, value) {
		params[key] = value
	})
	var object = null
	var doc, data, key
	if (typeof params['oids'] !== 'undefined' && params['oids'] === 'true') {
		doc = db_body['types']
		object = doc[msg.name[3]]
	} else if (typeof params['blob'] !== 'undefined' && params['blob'] === 'false') {
		doc = db_body['types']
		object = doc[msg.name[3]]
		data = db_body['data']
		for (key in object) {
			if (data.hasOwnProperty(object[key])) {
				object[key] = data[object[key]]
			}
		}
	} else if (typeof params['blob'] !== 'undefined' && params['blob'] === 'true') {
		doc = db_body['types']
		object = doc[msg.name[3]]
		var blobs = db_body['blobs']
		data = db_body['data']
		var blobkeys = Object.keys(blobs)
		for (key in object) {
			if (typeof key !== 'undefined') {
				console.log(blobkeys.indexOf(data[object[key]]))
				if (data.hasOwnProperty(object[key]) && (blobkeys.indexOf(data[object[key]]) !== -1)) {
					object[key] = blobs[data[object[key]]]
				}
			}
		}
	}

	return object

}

var putAction = function(msg, mongPush) {

	dbc.hasMember(msg, 'name')
	dbc.hasMember(msg, 'data')

	var db = nano.use('db');
	db.insert(msg.data, msg.name, function(err) {
		var status = zmq.status.OK_200
		var body = {}
		if (err) {
			status = zmq.status.INTERNAL_SERVER_ERROR_500
			body['result'] = 'failure'
			body['error'] = 'Error: ' + err['Error'];
		} else {
			body['result'] = 'success'
		}
		var response = zmq.Response(status, zmq.header_json, body)
		mongPush.publish(msg.uuid, msg.connId, response)
	})
}

var getAction = function(msg, mongPush) {

	var db = nano.use('db')
	dbc.hasMember(msg, 'name')
	db.get(msg.name[1], {revs_info: false}, function(err, db_body) {
		var status = zmq.status.OK_200
		var body = {}
		var doc
		var object

		//console.log(doc)
		switch (msg.name.length) {
			case 2:
				object = db_body[msg.name[0]]
				break
			case 3:
				doc = db_body[msg.name[0]]
				object = doc[msg.name[2]]
				break
			case 4:
				if (msg.name[2] === 'type') {
					if (typeof msg.query !== 'undefined') {
						object = handleTypeParameters(db_body, msg)
					} else {
						doc = db_body['types']
						object = doc[msg.name[3]]
					}
				} else {
					doc = db_body['data']
					var field = doc[msg.name[2]]
					object = field[msg.name[3]]
				}
				break
			default:
				object = db_body
				break
		}

		if (err) {
			status = zmq.status.INTERNAL_SERVER_ERROR_500
			body['result'] = 'failure'
			body['error'] = 'Error getting data from datastore'
		} else {
			body['result'] = 'success'
			body['value'] = object
		}
		var response = zmq.Response(status, zmq.header_json, body)
		mongPush.publish(msg.uuid, msg.connId, response)
	})
}

var bindToQs = function() {

	var dataPush = zmq.bindToPushQ({
		spec: 'tcp://127.0.0.1:49995'
	})
	var mongPush = zmq.bindToMong2PubQ({
		spec: 'tcp://127.0.0.1:49996',
		id: 'dao_conn'
	})

	var echo = 0

	zmq.bindToPullQ({
		spec: 'tcp://127.0.0.1:49994',
		id: 'data_api'
	}, function(msg) {
		dbc.assert(null !== msg, 'Message cannot be null')
		dbc.hasMember(msg, 'action')
		dbc.hasMember(msg, 'uuid')
		dbc.hasMember(msg, 'connId')
		console.log(msg)

		switch (msg.action) {
			case 'PUT':
				putAction(msg, mongPush)
				break

			case 'GET':
				getAction(msg, mongPush)
				break

			case 'ECHO':
				msg.body = {
					echo: echo++
				}
				dataPush.push(msg)
				break

			default:
				var body = {
					error: 'Action parameter is required'
				}
				var response = zmq.Response(zmq.status.BAD_REQUEST_400, zmq.header_json, body)
				mongPush.publish(msg.uuid, msg.connId, response)
				break
		}
	})
}

initDB(function() {
	console.log('Database Destroyed and Created')
	bindToQs()
})

module.exports.getAction = getAction
module.exports.initDB = initDB
module.exports.handleTypeParameters = handleTypeParameters<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';
import packageConfig from './config-schema.json';

export default {
	config: packageConfig,
	events: null,
	timer1: null,

	activate(state) {
		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.workspace.observeTextEditors(this.callback.bind(this))
		);
		this.subscriptions.add(
			atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this))
		);
	},

	callback(editor) {
		var changeGrammarIfAlreadySet = atom.config.get('shebang-set-grammar.changeGrammarIfAlreadySet');
		if (! changeGrammarIfAlreadySet) {
			if (editor.getGrammar() != atom.grammars.nullGrammar) {
				return;
			}
		}

		var bin_name = this.read_shebang(editor);
		if (bin_name == null) {
			return;
		}

		var aliases = atom.config.get('shebang-set-grammar.aliases');
		try {
			aliases = JSON.parse(aliases);
		}
		catch(err) {
			console.error("Inavlid \"aliases\" setting value: "+err);
			aliases = {};
		}

		if (bin_name in aliases) {
			bin_name = aliases[bin_name];
		}

		grammar = atom.grammars.grammarForScopeName('source.'+bin_name);
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	read_shebang(editor) {
		/*
			#! /usr/bin/python 	=> python
			#! /usr/bin/python -e 	=> python
			#! /bin/bash 			=> bash
			#! perl -e 			=> perl
		*/

		var line = editor.lineTextForBufferRow(0);
		if (! line.startsWith('#!')) {
			return null;
		}

		line = line.substr(2).trim();

		var parts = line.split(/[\s\t]+/);
		for (var i=parts.length-1; i>0; i--) {
			if (parts[i].startsWith("-")) {
				continue;
			}

			return parts[i];
		}

		parts = parts[0].split("/");
		return parts[parts.length - 1];
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.events != null) {
			this.events.dispose();
			this.events = null;
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<commit_msg>Fix config name<commit_after>'use babel';

import { CompositeDisposable } from 'atom';
import packageConfig from './config-schema.json';

export default {
	config: packageConfig,
	events: null,
	timer1: null,

	activate(state) {
		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.workspace.observeTextEditors(this.callback.bind(this))
		);
		this.subscriptions.add(
			atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this))
		);
	},

	callback(editor) {
		var changeNonPlainText = atom.config.get('shebang-set-grammar.changeNonPlainText');
		if (! changeNonPlainText) {
			if (editor.getGrammar() != atom.grammars.nullGrammar) {
				return;
			}
		}

		var bin_name = this.read_shebang(editor);
		if (bin_name == null) {
			return;
		}

		var aliases = atom.config.get('shebang-set-grammar.aliases');
		try {
			aliases = JSON.parse(aliases);
		}
		catch(err) {
			console.error("Inavlid \"aliases\" setting value: "+err);
			aliases = {};
		}

		if (bin_name in aliases) {
			bin_name = aliases[bin_name];
		}

		grammar = atom.grammars.grammarForScopeName('source.'+bin_name);
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	read_shebang(editor) {
		/*
			#! /usr/bin/python 	=> python
			#! /usr/bin/python -e 	=> python
			#! /bin/bash 			=> bash
			#! perl -e 			=> perl
		*/

		var line = editor.lineTextForBufferRow(0);
		if (! line.startsWith('#!')) {
			return null;
		}

		line = line.substr(2).trim();

		var parts = line.split(/[\s\t]+/);
		for (var i=parts.length-1; i>0; i--) {
			if (parts[i].startsWith("-")) {
				continue;
			}

			return parts[i];
		}

		parts = parts[0].split("/");
		return parts[parts.length - 1];
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.events != null) {
			this.events.dispose();
			this.events = null;
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<|endoftext|>


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

"use strict";

// Declare fmComponents module if it doesn't exist.
try { angular.module( "fm.components" ); } catch( ignored ) { angular.module( "fm.components", [] ); }

angular.module( "fm.components" )
  .directive( "fmSearchboxButton",
              function() {
                return {
                  replace  : false,
                  restrict : "A",
                  link     : function postLink( scope, element, attributes, controller ) {
                    if( scope.actionButtons ) {
                      scope.actionButtons.push( element.get( 0 ) );
                    }

                    var action = attributes[ "action" ];
                    var blockInput = (attributes[ "blockInput" ]) ? true : false;
                    element.bind( "click", function() {
                      scope.triggerActionState( blockInput, attributes[ "placeholder" ], attributes[ "cancelTitle" ] );

                      var actionEvent = scope.$emit( action );
                      if( actionEvent.defaultPrevented ) {
                        scope.endActionState();
                      }
                    } );
                  }
                }
              } )

  .directive( "fmSearchboxButtonCancel",
              function() {
                return {
                  template : "<button type='button' class='btn btn-default hideFocus'>" +
                             "  " +
                             "</button>",
                  replace  : true,
                  restrict : "E",
                  link     : function postLink( scope, element, attributes, controller ) {
                    scope.cancelButton = element.get( 0 );

                    element.on( "click", function() {
                      scope.ngModel = "";
                      scope.$emit( "cancelled" );
                    } );
                  }
                }
              } )

  .directive( "fmSearchbox", [
    "$compile", "$timeout", function( $compile, $timeout ) {
      return {
        replace  : false,
        restrict : "E",
        scope    : {
          ngModel           : "=",
          placeholder       : "@",
          searchText        : "@",
          actionCancelTitle : "@",
          actionPlaceholder : "@"
        },
        link     : function postLink( scope, element, attributes, controller ) {
          var header = "
";
          var footer = "
" +
                       "   <input type='text' class='form-control' placeholder={{placeholder}} ng-model=ngModel ng-disabled=!inputEnabled>" +
                       "   " +
                       "     <fm-searchbox-button-cancel />" +
                       "   " +
                       "
" +
                       "
";

          var children = element.children();
          var childrenHTML = "";
          if( 0 != children.length ) {
            angular.forEach( children, function( child ) {
              childrenHTML += child.outerHTML;
            } );
          }
          // Construct the final template
          var template = header + childrenHTML + footer;

          // Create a new DOM element from the template.
          var newElement = angular.element( template );
          // Copy attributes from original element.
          var attributeSource = element[0].attributes;
          for( var attributeIndex = 0; attributeIndex < attributeSource.length; ++attributeIndex ) {
            newElement.attr( attributeSource.item( attributeIndex ).nodeName, attributeSource.item( attributeIndex ).nodeValue );
          }

          // Replace the original element with our new element.
          element.replaceWith( newElement );
          // Angularize the DOM element.
          // Prepare an array for searchbox buttons to register themselves into.
          scope.actionButtons = [];
          // Compilation will invoke all fmSearchboxButtons to be linked again.
          $compile( newElement )( scope );
          element = newElement;

          scope.actionPending = false;
          scope.inputEnabled = true;

          var textbox = element.find( "input" );
          var actionButtons = $( scope.actionButtons );
          var cancelButton = $( scope.cancelButton );

          actionButtons.hide();

          textbox.on( "focus", function() {
            actionButtons.show();
          } );
          textbox.on( "blur", function() {
            $timeout(
              function() {
                // Hide the button if we're not searching right now.
                // Otherwise, we probably lost focus because the button was clicked.
                if( scope.actionPending != true ) {
                  actionButtons.fadeOut();
                }
              }, 1000
            );
          } );

          scope.$on( "cancelled", function() {
            scope.endActionState();
            scope.$$phase || scope.$apply();
          } );

          $timeout(
            function() {
              actionButtons.tooltip( { placement : "top" } );
              actionButtons.removeClass( "actionButton" );
            }
          );

          scope.triggerActionState = function( isBlocking, placeholder, cancelTitle ) {
            scope.wasBlocking = isBlocking;

            actionButtons.fadeOut();
            actionButtons.tooltip( "hide" );
            if( isBlocking ) {
              scope.inputEnabled = false;
            }

            cancelButton.tooltip( "destroy" );
            if( cancelTitle ) {
              cancelButton.tooltip( { placement : "top", title : cancelTitle, container : "body" } );
            }

            if( !element.originalPlaceholder ) element.originalPlaceholder = scope.placeholder;
            scope.actionPending = true;
            if( placeholder ) {
              scope.placeholder = placeholder;
            }
          };

          scope.endActionState = function() {
            scope.wasBlocking = void(0);

            scope.inputEnabled = true;
            cancelButton.tooltip( "destroy" );

            actionButtons.hide();
            scope.actionPending = false;
            if( element.originalPlaceholder ) {
              scope.placeholder = element.originalPlaceholder;
              element.originalPlaceholder = null;
            }
          };

          scope.$watch( "actionPending", function( newValue, oldValue ) {
            if( newValue == true ) scope.triggerActionState( scope.wasBlocking );
            if( newValue == false ) scope.endActionState();
          } );

        }
      };
    } ] );<commit_msg>Moving tooltips to bottom by default<commit_after>/**
 * Copyright (C) 2014, Oliver Salzburg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Created: 2014-01-13 20:34
 *
 * @author Oliver Salzburg
 * @copyright Copyright (C) 2014, Oliver Salzburg, HARTWIG Communication & Events
 * @license http://opensource.org/licenses/mit-license.php MIT License
 */

"use strict";

// Declare fmComponents module if it doesn't exist.
try { angular.module( "fm.components" ); } catch( ignored ) { angular.module( "fm.components", [] ); }

angular.module( "fm.components" )
  .directive( "fmSearchboxButton",
              function() {
                return {
                  replace  : false,
                  restrict : "A",
                  link     : function postLink( scope, element, attributes, controller ) {
                    if( scope.actionButtons ) {
                      scope.actionButtons.push( element.get( 0 ) );
                    }

                    var action = attributes[ "action" ];
                    var blockInput = (attributes[ "blockInput" ]) ? true : false;
                    element.bind( "click", function() {
                      scope.triggerActionState( blockInput, attributes[ "placeholder" ], attributes[ "cancelTitle" ] );

                      var actionEvent = scope.$emit( action );
                      if( actionEvent.defaultPrevented ) {
                        scope.endActionState();
                      }
                    } );
                  }
                }
              } )

  .directive( "fmSearchboxButtonCancel",
              function() {
                return {
                  template : "<button type='button' class='btn btn-default hideFocus'>" +
                             "  " +
                             "</button>",
                  replace  : true,
                  restrict : "E",
                  link     : function postLink( scope, element, attributes, controller ) {
                    scope.cancelButton = element.get( 0 );

                    element.on( "click", function() {
                      scope.ngModel = "";
                      scope.$emit( "cancelled" );
                    } );
                  }
                }
              } )

  .directive( "fmSearchbox", [
    "$compile", "$timeout", function( $compile, $timeout ) {
      return {
        replace  : false,
        restrict : "E",
        scope    : {
          ngModel           : "=",
          placeholder       : "@",
          searchText        : "@",
          actionCancelTitle : "@",
          actionPlaceholder : "@"
        },
        link     : function postLink( scope, element, attributes, controller ) {
          var header = "
";
          var footer = "
" +
                       "   <input type='text' class='form-control' placeholder={{placeholder}} ng-model=ngModel ng-disabled=!inputEnabled>" +
                       "   " +
                       "     <fm-searchbox-button-cancel />" +
                       "   " +
                       "
" +
                       "
";

          var children = element.children();
          var childrenHTML = "";
          if( 0 != children.length ) {
            angular.forEach( children, function( child ) {
              childrenHTML += child.outerHTML;
            } );
          }
          // Construct the final template
          var template = header + childrenHTML + footer;

          // Create a new DOM element from the template.
          var newElement = angular.element( template );
          // Copy attributes from original element.
          var attributeSource = element[0].attributes;
          for( var attributeIndex = 0; attributeIndex < attributeSource.length; ++attributeIndex ) {
            newElement.attr( attributeSource.item( attributeIndex ).nodeName, attributeSource.item( attributeIndex ).nodeValue );
          }

          // Replace the original element with our new element.
          element.replaceWith( newElement );
          // Angularize the DOM element.
          // Prepare an array for searchbox buttons to register themselves into.
          scope.actionButtons = [];
          // Compilation will invoke all fmSearchboxButtons to be linked again.
          $compile( newElement )( scope );
          element = newElement;

          scope.actionPending = false;
          scope.inputEnabled = true;

          var textbox = element.find( "input" );
          var actionButtons = $( scope.actionButtons );
          var cancelButton = $( scope.cancelButton );

          actionButtons.hide();

          textbox.on( "focus", function() {
            actionButtons.show();
          } );
          textbox.on( "blur", function() {
            $timeout(
              function() {
                // Hide the button if we're not searching right now.
                // Otherwise, we probably lost focus because the button was clicked.
                if( scope.actionPending != true ) {
                  actionButtons.fadeOut();
                }
              }, 1000
            );
          } );

          scope.$on( "cancelled", function() {
            scope.endActionState();
            scope.$$phase || scope.$apply();
          } );

          $timeout(
            function() {
              actionButtons.tooltip( { placement : "bottom" } );
              actionButtons.removeClass( "actionButton" );
            }
          );

          scope.triggerActionState = function( isBlocking, placeholder, cancelTitle ) {
            scope.wasBlocking = isBlocking;

            actionButtons.fadeOut();
            actionButtons.tooltip( "hide" );
            if( isBlocking ) {
              scope.inputEnabled = false;
            }

            cancelButton.tooltip( "destroy" );
            if( cancelTitle ) {
              cancelButton.tooltip( { placement : "bottom", title : cancelTitle, container : "body" } );
            }

            if( !element.originalPlaceholder ) element.originalPlaceholder = scope.placeholder;
            scope.actionPending = true;
            if( placeholder ) {
              scope.placeholder = placeholder;
            }
          };

          scope.endActionState = function() {
            scope.wasBlocking = void(0);

            scope.inputEnabled = true;
            cancelButton.tooltip( "destroy" );

            actionButtons.hide();
            scope.actionPending = false;
            if( element.originalPlaceholder ) {
              scope.placeholder = element.originalPlaceholder;
              element.originalPlaceholder = null;
            }
          };

          scope.$watch( "actionPending", function( newValue, oldValue ) {
            if( newValue == true ) scope.triggerActionState( scope.wasBlocking );
            if( newValue == false ) scope.endActionState();
          } );

        }
      };
    } ] );<|endoftext|>


<commit_before>'use strict';

function PublicationShowController($anchorScroll, $controller, $location, $routeParams, $scope, $sce, $q, $http, $mdDialog,
  NpolarLang, NpolarApiSecurity, npdcAppConfig, NpdcCitationModel, SherpaRomeo,
  Dataset, Project, Publication) {

  'ngInject';

  //let self = this;

  $controller('NpolarBaseController', {$scope: $scope});
  $scope.resource = Publication;
  $scope.error = null;
  $scope.lang = NpolarLang;

  $scope.isOpen = (publication) => {
    return Publication.isOpen(publication);
  };

  $scope.license = 'cc-by';

  $scope.attachmentUri = (file) => {
    let uri = file.uri;
    function key() {
      let system = NpolarApiSecurity.getSystem('read', $scope.resource.path);
      let key;
      if (system.key) {
        key=system.key;
      }
      return key;
    }
    // Show key when logged in
    //if (false === (/^http/).test($scope.document.license)) {
    //  uri += '?key='+key();
    //}
    if (key()) {
      uri += '?key='+key();
    }
    return uri;
  };

  $scope.isNpolar = (author) => {
    if (author.organisation) {
      return (/Norwegian Polar Institute$/).test(author.organisation);
    } else if (author && author.email) {
      return (/@npolar\.no$/).test(author.email);
    }
    return false;

  };

  $scope.names = (people) => {
    return people.map(p => p.first_name +' '+ p.last_name);
  };

  let show = function() {
    $scope.show().$promise.then(publication => {

      $scope.uri = Publication.uri(publication);

      $scope.citations = Publication.citationList(publication);

      $scope.metadata = Publication.metadata(publication, $scope.resource);

      $scope.citation = $scope.citations[0];

      if (publication.journal && publication.journal.name) {
        SherpaRomeo.jtitle(publication.journal.name).then(romeo => {
          $scope.romeo = { publisher: romeo.romeoapi.publishers[0].publisher };
        });
      }

      if (publication.people instanceof Array) {
        $scope.authors = publication.people.filter(p => ( (p.roles||[]).includes('author')) );
        $scope.coauthors = publication.people.filter(p => ( (p.roles||[]).includes('co-author')) );
        $scope.editors = publication.people.filter(p => ( (p.roles||[]).includes('editor')) );
      }

      if (publication.organisations instanceof Array) {
        $scope.publishers = publication.organisations.filter(o => ( (o.roles||[]).includes('publisher')) );
      }

      if (publication.links instanceof Array) {
        $scope.links = publication.links.filter(l => {
          return (l.rel !== "alternate" && l.rel !== "parent" && l.rel !== "edit" && l.rel !== "attachment" &&
            l.rel.toLowerCase() !== "doi" &&
            false === (/doi[.]org/).test(l.href) );
        });

        if ($scope.isOpen(publication) || $scope.security.hasSystem($scope.resource.path)) {
          $scope.links = $scope.links.concat(publication.links.filter(l => l.rel === "attachment"));
        }

        //$scope.doi = extractDOILink(publication.links);
        $scope.alternate = publication.links.filter(l => ( ( l.rel === "alternate" && l.type !== "text/html") || l.rel === "edit" ));

        if (publication.publication_type === 'in-report' && publication.journal && publication.journal.name) {
          $scope.parent = {
            title: publication.journal.name
          };
        }

        let parent = publication.links.find(l => l.rel === "parent");
        if (parent) {

          let uri = parent.href.replace(/\/\/data\./, '//api.');
          $http.get(uri).then(r =>  {
            $scope.parent = r.data;
          }, (e) => {
            $scope.parent = { title: parent.title, href: parent.href };
          });
        }
      }

      Publication.array({ q: '', fields: 'id,title', limit: 'all',
        'filter-links.rel': 'parent',
        'filter-links.href': `https://data.npolar.no/publication/${publication.id}`
      }).$promise.then(children => {
        let child_links = children.sort((a,b) => { return (a.title > b.title); }).map(c => {
          return { rel: "child", title: c.title, href: `https://data.npolar.no/publication/${c.id}`};
        });
        if (child_links) {
          $scope.links = ($scope.links||[]).concat(child_links);
        }
      });

      let relatedQuery = { q: publication.title, fields: 'id,title,collection,published,publication_type', score: true, limit: 5 };

      let relatedPublications = Publication.array(Object.assign({}, relatedQuery, {'not-id': publication.id })).$promise.then(arr => {
        return arr.map(p => {
          p.title += ` [${p.publication_type}] (${p.published.split('-')[0] })`;
          return p;
        });
      });

      let relatedDatasets = Dataset.array(relatedQuery).$promise;

      let relatedProjects = Project.array(relatedQuery).$promise;

      $q.all([relatedDatasets, relatedPublications, relatedProjects]).then((related) => {
        $scope.related = related;
      });

    }, (errorData) => {
      $scope._error = errorData.statusText;
    });

  };

  show();
}

module.exports = PublicationShowController;<commit_msg>I shall never, ever have tri-state logic in templates every again<commit_after>'use strict';

function PublicationShowController($anchorScroll, $controller, $location, $routeParams, $scope, $sce, $q, $http, $mdDialog,
  NpolarLang, NpolarApiSecurity, npdcAppConfig, NpdcCitationModel, SherpaRomeo,
  Dataset, Project, Publication) {

  'ngInject';

  //let self = this;

  $controller('NpolarBaseController', {$scope: $scope});
  $scope.resource = Publication;
  $scope.error = null;
  $scope.lang = NpolarLang;

  $scope.isOpen = (publication) => {
    return Publication.isOpen(publication);
  };

  $scope.isDownloadable = (publication) => {
    let isOpen = Publication.isOpen(publication);
    if (true === isOpen) {
      return true;
    }
    if ($scope.security.hasSystem($scope.resource.path)) {
      return true;
    }
    return false;
  };

  $scope.license = 'cc-by';

  $scope.attachmentUri = (file) => {
    let uri = file.uri;
    function key() {
      let system = NpolarApiSecurity.getSystem('read', $scope.resource.path);
      let key;
      if (system.key) {
        key=system.key;
      }
      return key;
    }
    // Show key when logged in
    //if (false === (/^http/).test($scope.document.license)) {
    //  uri += '?key='+key();
    //}
    if (key()) {
      uri += '?key='+key();
    }
    return uri;
  };

  $scope.isNpolar = (author) => {
    if (author.organisation) {
      return (/Norwegian Polar Institute$/).test(author.organisation);
    } else if (author && author.email) {
      return (/@npolar\.no$/).test(author.email);
    }
    return false;

  };

  $scope.names = (people) => {
    return people.map(p => p.first_name +' '+ p.last_name);
  };

  let show = function() {
    $scope.show().$promise.then(publication => {

      $scope.uri = Publication.uri(publication);

      $scope.citations = Publication.citationList(publication);

      $scope.metadata = Publication.metadata(publication, $scope.resource);

      $scope.citation = $scope.citations[0];

      if (publication.journal && publication.journal.name) {
        SherpaRomeo.jtitle(publication.journal.name).then(romeo => {
          $scope.romeo = { publisher: romeo.romeoapi.publishers[0].publisher };
        });
      }

      if (publication.people instanceof Array) {
        $scope.authors = publication.people.filter(p => ( (p.roles||[]).includes('author')) );
        $scope.coauthors = publication.people.filter(p => ( (p.roles||[]).includes('co-author')) );
        $scope.editors = publication.people.filter(p => ( (p.roles||[]).includes('editor')) );
      }

      if (publication.organisations instanceof Array) {
        $scope.publishers = publication.organisations.filter(o => ( (o.roles||[]).includes('publisher')) );
      }

      if (publication.links instanceof Array) {
        $scope.links = publication.links.filter(l => {
          return (l.rel !== "alternate" && l.rel !== "parent" && l.rel !== "edit" && l.rel !== "attachment" &&
            l.rel.toLowerCase() !== "doi" &&
            false === (/doi[.]org/).test(l.href) );
        });

        if ($scope.isOpen(publication) || $scope.security.hasSystem($scope.resource.path)) {
          $scope.links = $scope.links.concat(publication.links.filter(l => l.rel === "attachment"));
        }

        //$scope.doi = extractDOILink(publication.links);
        $scope.alternate = publication.links.filter(l => ( ( l.rel === "alternate" && l.type !== "text/html") || l.rel === "edit" ));

        if (publication.publication_type === 'in-report' && publication.journal && publication.journal.name) {
          $scope.parent = {
            title: publication.journal.name
          };
        }

        let parent = publication.links.find(l => l.rel === "parent");
        if (parent) {

          let uri = parent.href.replace(/\/\/data\./, '//api.');
          $http.get(uri).then(r =>  {
            $scope.parent = r.data;
          }, (e) => {
            $scope.parent = { title: parent.title, href: parent.href };
          });
        }
      }

      Publication.array({ q: '', fields: 'id,title', limit: 'all',
        'filter-links.rel': 'parent',
        'filter-links.href': `https://data.npolar.no/publication/${publication.id}`
      }).$promise.then(children => {
        let child_links = children.sort((a,b) => { return (a.title > b.title); }).map(c => {
          return { rel: "child", title: c.title, href: `https://data.npolar.no/publication/${c.id}`};
        });
        if (child_links) {
          $scope.links = ($scope.links||[]).concat(child_links);
        }
      });

      let relatedQuery = { q: publication.title, fields: 'id,title,collection,published,publication_type', score: true, limit: 5 };

      let relatedPublications = Publication.array(Object.assign({}, relatedQuery, {'not-id': publication.id })).$promise.then(arr => {
        return arr.map(p => {
          p.title += ` [${p.publication_type}] (${p.published.split('-')[0] })`;
          return p;
        });
      });

      let relatedDatasets = Dataset.array(relatedQuery).$promise;

      let relatedProjects = Project.array(relatedQuery).$promise;

      $q.all([relatedDatasets, relatedPublications, relatedProjects]).then((related) => {
        $scope.related = related;
      });

    }, (errorData) => {
      $scope._error = errorData.statusText;
    });

  };

  show();
}

module.exports = PublicationShowController;<|endoftext|>


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

"use strict";

// Declare fmComponents module if it doesn't exist.
try { angular.module( "fm.components" ); } catch( ignored ) { angular.module( "fm.components", [] ); }

angular.module( "fm.components" )
  .directive( "fmSearchboxButton",
              function() {
                return {
                  replace  : false,
                  restrict : "A",
                  link     : function postLink( scope, element, attributes, controller ) {
                    if( scope.actionButtons ) {
                      scope.actionButtons.push( element.get( 0 ) );
                    }

                    var action = attributes[ "action" ];
                    var blockInput = (attributes[ "blockInput" ]) ? true : false;
                    element.bind( "click", function() {
                      scope.triggerActionState( blockInput, attributes[ "placeholder" ], attributes[ "cancelTitle" ] );

                      var actionEvent = scope.$emit( action );
                      if( actionEvent.defaultPrevented ) {
                        scope.endActionState();
                      }
                    } );
                  }
                }
              } )

  .directive( "fmSearchboxButtonCancel",
              function() {
                return {
                  template : "<button type='button' class='btn btn-default hideFocus'>" +
                             "  " +
                             "</button>",
                  replace  : true,
                  restrict : "E",
                  link     : function postLink( scope, element, attributes, controller ) {
                    scope.cancelButton = element.get( 0 );

                    element.on( "click", function() {
                      scope.ngModel = "";
                      scope.$emit( "cancelled" );
                    } );
                  }
                }
              } )

  .directive( "fmSearchbox", [
    "$compile", "$timeout", function( $compile, $timeout ) {
      return {
        replace  : false,
        restrict : "E",
        scope    : {
          ngModel           : "=",
          placeholder       : "@",
          searchText        : "@",
          actionCancelTitle : "@",
          actionPlaceholder : "@"
        },
        link     : function postLink( scope, element, attributes, controller ) {
          var header = "
";
          var footer = "
" +
                       "   <input type='text' class='form-control' placeholder={{placeholder}} ng-model=ngModel ng-disabled=!inputEnabled>" +
                       "   " +
                       "     <fm-searchbox-button-cancel />" +
                       "   " +
                       "
" +
                       "
";

          var children = element.children();
          var childrenHTML = "";
          if( 0 != children.length ) {
            angular.forEach( children, function( child ) {
              childrenHTML += child.outerHTML;
            } );
          }
          // Construct the final template
          var template = header + childrenHTML + footer;

          // Create a new DOM element from the template.
          var newElement = angular.element( template );
          // Copy attributes from original element.
          var attributeSource = element[0].attributes;
          for( var attributeIndex = 0; attributeIndex < attributeSource.length; ++attributeIndex ) {
            newElement.attr( attributeSource.item( attributeIndex ).nodeName, attributeSource.item( attributeIndex ).nodeValue );
          }

          // Replace the original element with our new element.
          element.replaceWith( newElement );
          // Angularize the DOM element.
          // Prepare an array for searchbox buttons to register themselves into.
          scope.actionButtons = [];
          // Compilation will invoke all fmSearchboxButtons to be linked again.
          $compile( newElement )( scope );
          element = newElement;

          scope.actionPending = false;
          scope.inputEnabled = true;

          var textbox = element.find( "input" );
          var actionButtons = $( scope.actionButtons );
          var cancelButton = $( scope.cancelButton );

          actionButtons.hide();

          textbox.on( "focus", function() {
            actionButtons.show();
          } );
          textbox.on( "blur", function() {
            $timeout(
              function() {
                // Hide the button if we're not searching right now.
                // Otherwise, we probably lost focus because the button was clicked.
                if( scope.actionPending != true ) {
                  actionButtons.fadeOut();
                }
              }, 1000
            );
          } );

          scope.$on( "cancelled", function() {
            scope.endActionState();
            scope.$$phase || scope.$apply();
          } );

          $timeout(
            function() {
              actionButtons.tooltip( { placement : "top" } );
              actionButtons.removeClass( "actionButton" );
            }
          );

          scope.triggerActionState = function( isBlocking, placeholder, cancelTitle ) {
            scope.wasBlocking = isBlocking;

            actionButtons.fadeOut();
            actionButtons.tooltip( "hide" );
            if( isBlocking ) {
              scope.inputEnabled = false;
            }

            cancelButton.tooltip( "destroy" );
            if( cancelTitle ) {
              cancelButton.tooltip( { placement : "top", title : cancelTitle } );
            }

            if( !element.originalPlaceholder ) element.originalPlaceholder = scope.placeholder;
            scope.actionPending = true;
            if( placeholder ) {
              scope.placeholder = placeholder;
            }
          };

          scope.endActionState = function() {
            scope.wasBlocking = void(0);

            scope.inputEnabled = true;
            cancelButton.tooltip( "destroy" );

            actionButtons.hide();
            scope.actionPending = false;
            if( element.originalPlaceholder ) {
              scope.placeholder = element.originalPlaceholder;
              element.originalPlaceholder = null;
            }
          };

          scope.$watch( "actionPending", function( newValue, oldValue ) {
            if( newValue == true ) scope.triggerActionState( scope.wasBlocking );
            if( newValue == false ) scope.endActionState();
          } );

        }
      };
    } ] );<commit_msg>Tooltip is now attached to body<commit_after>/**
 * Copyright (C) 2014, Oliver Salzburg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Created: 2014-01-13 20:34
 *
 * @author Oliver Salzburg
 * @copyright Copyright (C) 2014, Oliver Salzburg, HARTWIG Communication & Events
 * @license http://opensource.org/licenses/mit-license.php MIT License
 */

"use strict";

// Declare fmComponents module if it doesn't exist.
try { angular.module( "fm.components" ); } catch( ignored ) { angular.module( "fm.components", [] ); }

angular.module( "fm.components" )
  .directive( "fmSearchboxButton",
              function() {
                return {
                  replace  : false,
                  restrict : "A",
                  link     : function postLink( scope, element, attributes, controller ) {
                    if( scope.actionButtons ) {
                      scope.actionButtons.push( element.get( 0 ) );
                    }

                    var action = attributes[ "action" ];
                    var blockInput = (attributes[ "blockInput" ]) ? true : false;
                    element.bind( "click", function() {
                      scope.triggerActionState( blockInput, attributes[ "placeholder" ], attributes[ "cancelTitle" ] );

                      var actionEvent = scope.$emit( action );
                      if( actionEvent.defaultPrevented ) {
                        scope.endActionState();
                      }
                    } );
                  }
                }
              } )

  .directive( "fmSearchboxButtonCancel",
              function() {
                return {
                  template : "<button type='button' class='btn btn-default hideFocus'>" +
                             "  " +
                             "</button>",
                  replace  : true,
                  restrict : "E",
                  link     : function postLink( scope, element, attributes, controller ) {
                    scope.cancelButton = element.get( 0 );

                    element.on( "click", function() {
                      scope.ngModel = "";
                      scope.$emit( "cancelled" );
                    } );
                  }
                }
              } )

  .directive( "fmSearchbox", [
    "$compile", "$timeout", function( $compile, $timeout ) {
      return {
        replace  : false,
        restrict : "E",
        scope    : {
          ngModel           : "=",
          placeholder       : "@",
          searchText        : "@",
          actionCancelTitle : "@",
          actionPlaceholder : "@"
        },
        link     : function postLink( scope, element, attributes, controller ) {
          var header = "
";
          var footer = "
" +
                       "   <input type='text' class='form-control' placeholder={{placeholder}} ng-model=ngModel ng-disabled=!inputEnabled>" +
                       "   " +
                       "     <fm-searchbox-button-cancel />" +
                       "   " +
                       "
" +
                       "
";

          var children = element.children();
          var childrenHTML = "";
          if( 0 != children.length ) {
            angular.forEach( children, function( child ) {
              childrenHTML += child.outerHTML;
            } );
          }
          // Construct the final template
          var template = header + childrenHTML + footer;

          // Create a new DOM element from the template.
          var newElement = angular.element( template );
          // Copy attributes from original element.
          var attributeSource = element[0].attributes;
          for( var attributeIndex = 0; attributeIndex < attributeSource.length; ++attributeIndex ) {
            newElement.attr( attributeSource.item( attributeIndex ).nodeName, attributeSource.item( attributeIndex ).nodeValue );
          }

          // Replace the original element with our new element.
          element.replaceWith( newElement );
          // Angularize the DOM element.
          // Prepare an array for searchbox buttons to register themselves into.
          scope.actionButtons = [];
          // Compilation will invoke all fmSearchboxButtons to be linked again.
          $compile( newElement )( scope );
          element = newElement;

          scope.actionPending = false;
          scope.inputEnabled = true;

          var textbox = element.find( "input" );
          var actionButtons = $( scope.actionButtons );
          var cancelButton = $( scope.cancelButton );

          actionButtons.hide();

          textbox.on( "focus", function() {
            actionButtons.show();
          } );
          textbox.on( "blur", function() {
            $timeout(
              function() {
                // Hide the button if we're not searching right now.
                // Otherwise, we probably lost focus because the button was clicked.
                if( scope.actionPending != true ) {
                  actionButtons.fadeOut();
                }
              }, 1000
            );
          } );

          scope.$on( "cancelled", function() {
            scope.endActionState();
            scope.$$phase || scope.$apply();
          } );

          $timeout(
            function() {
              actionButtons.tooltip( { placement : "top" } );
              actionButtons.removeClass( "actionButton" );
            }
          );

          scope.triggerActionState = function( isBlocking, placeholder, cancelTitle ) {
            scope.wasBlocking = isBlocking;

            actionButtons.fadeOut();
            actionButtons.tooltip( "hide" );
            if( isBlocking ) {
              scope.inputEnabled = false;
            }

            cancelButton.tooltip( "destroy" );
            if( cancelTitle ) {
              cancelButton.tooltip( { placement : "top", title : cancelTitle, container : "body" } );
            }

            if( !element.originalPlaceholder ) element.originalPlaceholder = scope.placeholder;
            scope.actionPending = true;
            if( placeholder ) {
              scope.placeholder = placeholder;
            }
          };

          scope.endActionState = function() {
            scope.wasBlocking = void(0);

            scope.inputEnabled = true;
            cancelButton.tooltip( "destroy" );

            actionButtons.hide();
            scope.actionPending = false;
            if( element.originalPlaceholder ) {
              scope.placeholder = element.originalPlaceholder;
              element.originalPlaceholder = null;
            }
          };

          scope.$watch( "actionPending", function( newValue, oldValue ) {
            if( newValue == true ) scope.triggerActionState( scope.wasBlocking );
            if( newValue == false ) scope.endActionState();
          } );

        }
      };
    } ] );<|endoftext|>


<commit_before>// JavaScript Document
(function( window, undefined ) {

// Use the correct document accordingly with window argument (sandbox)
var document = window.document,
	navigator = window.navigator,
	location = window.location;
//make local copy
var cookie = function(key, value, options) {
         // if key and value are given set cookie (deleteing and setting cookie are the same)
        if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) {
            options = extend({}, options);
			//if value is null set expires to a past date to delete it
            if (value === null || value === undefined) {
                options.expires = -1;
            }
			//if expires is provided caclulate date
            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires = new Date();
                t.setDate(t.getDate() + days);
            }
			//make sure value is a string
            value = String(value);
			//set cookie with options
            return (document.cookie = [
                encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
                options.path    ? '; path=' + options.path : '',
                options.domain  ? '; domain=' + options.domain : '',
                options.secure  ? '; secure' : ''
            ].join(''));
        }
        // if just a key or key and options passed retreive cookie.
        options = value || {};
        var decode = options.raw ? function(s) { return s; } : decodeURIComponent;
        var pairs = document.cookie.split('; ');
		//loop through current cookies
        for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) {
			//check if the cookie is the one we are looking for if so return value
            if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined
        }
        return null;
      };
//asign to global
window.cookie = cookie;

})( window );<commit_msg>Add license banner<commit_after>/*! Cookie.js | (c) Alexander Schmitz | https://raw.githubusercontent.com/arschmitz/cookie/master/LICENSE */
// JavaScript Document
(function( window, undefined ) {

// Use the correct document accordingly with window argument (sandbox)
var document = window.document,
	navigator = window.navigator,
	location = window.location;
//make local copy
var cookie = function(key, value, options) {
         // if key and value are given set cookie (deleteing and setting cookie are the same)
        if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) {
            options = extend({}, options);
			//if value is null set expires to a past date to delete it
            if (value === null || value === undefined) {
                options.expires = -1;
            }
			//if expires is provided caclulate date
            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires = new Date();
                t.setDate(t.getDate() + days);
            }
			//make sure value is a string
            value = String(value);
			//set cookie with options
            return (document.cookie = [
                encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value),
                options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
                options.path    ? '; path=' + options.path : '',
                options.domain  ? '; domain=' + options.domain : '',
                options.secure  ? '; secure' : ''
            ].join(''));
        }
        // if just a key or key and options passed retreive cookie.
        options = value || {};
        var decode = options.raw ? function(s) { return s; } : decodeURIComponent;
        var pairs = document.cookie.split('; ');
		//loop through current cookies
        for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) {
			//check if the cookie is the one we are looking for if so return value
            if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined
        }
        return null;
      };
//asign to global
window.cookie = cookie;

})( window );
<|endoftext|>


<commit_before>'use babel'

import InfoView from './info-view'
import StatusView from './status-view'
import MoieClient from './moie-client'
import * as util from './util'
import * as provider from './suggestion-provider'
//import * as config from './config.js'
import { CompositeDisposable, BufferedProcess } from 'atom'

export default {

  config: require('./config.js'),

  infoView: null,
  statusView: null,
  infoPanel: null,
  statusTile: null,
  subscriptions: null,
  projectJson: "moie-project.json",
  triedAsSubprocess: false,

  provide() {
    return provider
  },

  consumeStatusBar(statusBar) {
    this.statusView = new StatusView()
    this.statusTile = statusBar.addLeftTile({'item': this.statusView.getRoot(), 'priority': 1})
  },

  activate(state) {
    this.infoView = new InfoView(state.infoViewState)
    this.infoPanel = atom.workspace.addBottomPanel({
      item: this.infoView.getRoot(),
      visible: false
    })

    this.client = new MoieClient(
        atom.config.get('Moie.interface'),
        atom.config.get('Moie.port'))
    this.markers = []

    provider.setClient(this.client)

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable()

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'moie:connect': () => this.connect(),
      'moie:disconnect': () => this.disconnect(),
      'moie:compile-Project': () => {
        util.saveAllFiles()
        this.compileProject()
      },
      'moie:compile-Script': () => this.compileScript(),
      'moie:stop-Server': () => this.stopServer(),
      'moie:check-model': () => this.checkModel(),
    }))

    //before closing the window; send a disconnect
    window.onbeforeunload = (e) => {
      this.disconnect()
    }

    atom.workspace.observeTextEditors(editor => {
      const self = this
      editor.onDidSave( (ev) => {
        if(atom.config.get('Moie.compileMode') == 'while typing')
          self.userSavedFile(ev)
      })
    })
  },

  deactivate() {
    this.modalPanel.destroy()
    this.subscriptions.dispose()
    this.moieView.destroy()
  },

  serialize() {
    return {
      infoViewState: this.infoView.serialize(),
      statusViewState: this.statusView.serialize(),
    }
  },

  connect() {
    const self = this
    const projects = atom.project.getDirectories()
    if(projects.length == 1) {
      const projectPath = projects[0]
      this.readProjectFileContent(projectPath, json => {
        this.client.connect(json, () => {
          atom.notifications.addSuccess("Mo|E connected")
          this.statusView.success("connected")
        }, (status,error) => {
          if(atom.config.get('Moie.startServer') && !this.triedAsSubprocess) {
            util.startMoieServer(() => this.connect())
            this.triedAsSubprocess = true
          } else {
            atom.notifications.addError(`Couldn't connect Mo|E to the server!`)
            this.statusView.error("disconnected")
          }
        })
      })
    } else {
      atom.notifications.addError("Can't handle multiple project-roots.\nUse 1 project")
      this.statusView.error("error")
    }
  },

  readProjectFileContent(rootDir, onSuccess) {
    const file = rootDir.getFile(this.projectJson)
    if(file.existsSync() && file.isFile()) {
      file.read(true).then(onSuccess)
    } else {
      atom.notifications.addError(`Please generate a ${this.projectJson} in\n${rootDir.path}`)
    }
  },

  disconnect() {
    console.log("disconnect pressed")
    this.client.disconnect(() => {
      atom.notifications.addSuccess("Mo|E disconnected!")
      this.statusView.success("disconnected")
      this.infoPanel.hide()
    })
    this.clearErrors()
    this.triedAsSubprocess = false
  },

  stopServer() {
    this.client.stopServer(() => {
      this.statusView.success("disconnected")
      atom.notifications.addSuccess("Server stopped")
    })
    this.clearErrors()
  },

  compileProject() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    } else {
      this.client.compile(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    }
  },

  compileScript() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    } else {
      this.client.compileScript(
        undefined,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    }
  },

  checkModel() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaFile(filepath)) {
      this.client.checkModel(filepath, (data) => {
        console.log(data)
        console.log("success")
        atom.notifications.addInfo(data)
      }, (xhr, status, error) => {
        console.log(xhr, status, error)
        atom.notifications.addError(xhr.responseText)
        this.statusView.error("server-error")
      })
    } else {
      atom.notifications.addError("Can't check a non-modelica file!")
    }
  },

  onCompileSuccess(data) {
    const lines = data.map(util.createErrorLine)
    this.infoView.addLines(lines)
    if(lines.length != 0) {
      data.
        filter(x => x.file !== "").
        forEach(errorObj => {
        const errorRange = [[errorObj.start.line-1,errorObj.start.column],
        [errorObj.end.line-1,errorObj.end.column]]
        const promise = atom.workspace.open(errorObj.file, {activatePane:false})
        promise.then(this.highlightErrors(errorRange).bind(this))
      })
      this.infoPanel.show()
    }
    else
      this.statusView.success("compiled")
  },

  highlightErrors(range) {
    return (editor) => {
      const rangeMarker = editor.markBufferRange(range, {invalidate:'never'})
      this.markers.push(rangeMarker)
      editor.decorateMarker(rangeMarker, {type: 'line-number', class: 'line-number-error'})
      this.statusView.error("compile errors")
    }
  },

  onCompileError(xhr, status,error) {
    if(xhr.status == 500) {
      atom.notifications.addError("InternalServerError.. Restart server and try agin.")
      this.statusView.error("server-error")
    } else if(xhr.status == 404) {
      atom.notifications.addError(xhr.responseText)
      this.statusView.error("server-error")
    }
    else {
      atom.notifications.addError("Can't contact server.. Restart server and try agin.")
      this.statusView.error("disconnected")
    }
  },

  clearErrors() {
    this.markers.forEach(marker => marker.destroy())
    this.infoPanel.hide()
  },

  userSavedFile(ev) {
    const path = ev.path
    if(util.isModelicaFile(path)) {
      this.compileProject()
    }
  }
}
<commit_msg>Add comment about status-bar<commit_after>'use babel'

import InfoView from './info-view'
import StatusView from './status-view'
import MoieClient from './moie-client'
import * as util from './util'
import * as provider from './suggestion-provider'
//import * as config from './config.js'
import { CompositeDisposable, BufferedProcess } from 'atom'

export default {

  config: require('./config.js'),

  infoView: null,
  statusView: null,
  infoPanel: null,
  statusTile: null,
  subscriptions: null,
  projectJson: "moie-project.json",
  triedAsSubprocess: false,

  provide() {
    return provider
  },

  consumeStatusBar(statusBar) {
    //called from 'status-bar' package (https://github.com/atom/status-bar)
    this.statusView = new StatusView()
    this.statusTile = statusBar.addLeftTile({'item': this.statusView.getRoot(), 'priority': 1})
  },

  activate(state) {
    this.infoView = new InfoView(state.infoViewState)
    this.infoPanel = atom.workspace.addBottomPanel({
      item: this.infoView.getRoot(),
      visible: false
    })

    this.client = new MoieClient(
        atom.config.get('Moie.interface'),
        atom.config.get('Moie.port'))
    this.markers = []

    provider.setClient(this.client)

    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable()

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'moie:connect': () => this.connect(),
      'moie:disconnect': () => this.disconnect(),
      'moie:compile-Project': () => {
        util.saveAllFiles()
        this.compileProject()
      },
      'moie:compile-Script': () => this.compileScript(),
      'moie:stop-Server': () => this.stopServer(),
      'moie:check-model': () => this.checkModel(),
    }))

    //before closing the window; send a disconnect
    window.onbeforeunload = (e) => {
      this.disconnect()
    }

    atom.workspace.observeTextEditors(editor => {
      const self = this
      editor.onDidSave( (ev) => {
        if(atom.config.get('Moie.compileMode') == 'while typing')
          self.userSavedFile(ev)
      })
    })
  },

  deactivate() {
    this.modalPanel.destroy()
    this.subscriptions.dispose()
    this.moieView.destroy()
  },

  serialize() {
    return {
      infoViewState: this.infoView.serialize(),
      statusViewState: this.statusView.serialize(),
    }
  },

  connect() {
    const self = this
    const projects = atom.project.getDirectories()
    if(projects.length == 1) {
      const projectPath = projects[0]
      this.readProjectFileContent(projectPath, json => {
        this.client.connect(json, () => {
          atom.notifications.addSuccess("Mo|E connected")
          this.statusView.success("connected")
        }, (status,error) => {
          if(atom.config.get('Moie.startServer') && !this.triedAsSubprocess) {
            util.startMoieServer(() => this.connect())
            this.triedAsSubprocess = true
          } else {
            atom.notifications.addError(`Couldn't connect Mo|E to the server!`)
            this.statusView.error("disconnected")
          }
        })
      })
    } else {
      atom.notifications.addError("Can't handle multiple project-roots.\nUse 1 project")
      this.statusView.error("error")
    }
  },

  readProjectFileContent(rootDir, onSuccess) {
    const file = rootDir.getFile(this.projectJson)
    if(file.existsSync() && file.isFile()) {
      file.read(true).then(onSuccess)
    } else {
      atom.notifications.addError(`Please generate a ${this.projectJson} in\n${rootDir.path}`)
    }
  },

  disconnect() {
    console.log("disconnect pressed")
    this.client.disconnect(() => {
      atom.notifications.addSuccess("Mo|E disconnected!")
      this.statusView.success("disconnected")
      this.infoPanel.hide()
    })
    this.clearErrors()
    this.triedAsSubprocess = false
  },

  stopServer() {
    this.client.stopServer(() => {
      this.statusView.success("disconnected")
      atom.notifications.addSuccess("Server stopped")
    })
    this.clearErrors()
  },

  compileProject() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    } else {
      this.client.compile(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    }
  },

  compileScript() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaScript(filepath)) {
      this.client.compileScript(
        filepath,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this)
      )
    } else {
      this.client.compileScript(
        undefined,
        this.onCompileSuccess.bind(this),
        this.onCompileError.bind(this))
    }
  },

  checkModel() {
    this.clearErrors()
    const filepath = util.currentFile()
    if(util.isModelicaFile(filepath)) {
      this.client.checkModel(filepath, (data) => {
        console.log(data)
        console.log("success")
        atom.notifications.addInfo(data)
      }, (xhr, status, error) => {
        console.log(xhr, status, error)
        atom.notifications.addError(xhr.responseText)
        this.statusView.error("server-error")
      })
    } else {
      atom.notifications.addError("Can't check a non-modelica file!")
    }
  },

  onCompileSuccess(data) {
    const lines = data.map(util.createErrorLine)
    this.infoView.addLines(lines)
    if(lines.length != 0) {
      data.
        filter(x => x.file !== "").
        forEach(errorObj => {
        const errorRange = [[errorObj.start.line-1,errorObj.start.column],
        [errorObj.end.line-1,errorObj.end.column]]
        const promise = atom.workspace.open(errorObj.file, {activatePane:false})
        promise.then(this.highlightErrors(errorRange).bind(this))
      })
      this.infoPanel.show()
    }
    else
      this.statusView.success("compiled")
  },

  highlightErrors(range) {
    return (editor) => {
      const rangeMarker = editor.markBufferRange(range, {invalidate:'never'})
      this.markers.push(rangeMarker)
      editor.decorateMarker(rangeMarker, {type: 'line-number', class: 'line-number-error'})
      this.statusView.error("compile errors")
    }
  },

  onCompileError(xhr, status,error) {
    if(xhr.status == 500) {
      atom.notifications.addError("InternalServerError.. Restart server and try agin.")
      this.statusView.error("server-error")
    } else if(xhr.status == 404) {
      atom.notifications.addError(xhr.responseText)
      this.statusView.error("server-error")
    }
    else {
      atom.notifications.addError("Can't contact server.. Restart server and try agin.")
      this.statusView.error("disconnected")
    }
  },

  clearErrors() {
    this.markers.forEach(marker => marker.destroy())
    this.infoPanel.hide()
  },

  userSavedFile(ev) {
    const path = ev.path
    if(util.isModelicaFile(path)) {
      this.compileProject()
    }
  }
}
<|endoftext|>


<commit_before>"use strict";

var Node = module.exports = function(opts, methods, done){

  var self = this;

  if(typeof opts !== 'object'){
    return done (new Error('Bad node definition'));
  }

  if(typeof opts.id !== 'string'){
    return done (new Error('Bad node id'));
  }

  if(typeof methods === 'function'){
    done = methods;
    methods = {};
  }

  if(typeof methods === 'undefined'){
    methods = {};
  }

  this.id = opts.id;
  this.attrs = {};
  this.latchedMessage = undefined;

  ['init','stop'].forEach(function(method){
    if(methods.hasOwnProperty(method)){
      self[method] = methods[method];
    }
  });

  // This will be wrapped so we can capture the output message
  if(methods.hasOwnProperty('process')){
    self.processHandler = methods.process;
  }

  for(var k in opts){
    if(opts.hasOwnProperty(k) && k !== 'id'){
      this.attrs[k] = opts[k];
    }
  }

  this.init.call(this, function(){
    process.nextTick(done);
  });

  return this;

};

Node.prototype.init = function(done){
  done();
};

Node.prototype.stop = function(done){
  return done();
};

// this will most likely be overridden whern creating the node
Node.prototype.processHandler = function(msg, done){
  // default is to just pass thru;
  done(null, msg);
};

Node.prototype.process = function(msg, done){
  var self = this;
  this.processHandler(msg, function(err, msg){
    // latch most recent message
    self.latchedMessage = msg;
    done(null, msg);
  });
};

Node.prototype.inspect = function(){
  return {
    id: this.id,
    message: this.latchedMessage,
    attrs: this.attrs
  };
};

Node.prototype.message = function(){
  return this.latchedMessage;
};

Node.prototype.get = function(key){
  if(typeof(key) === 'undefined'){
    return this.attrs;
  }
  return this.attrs[key];
};

Node.prototype.set = function(key, val){

  if(!key){
    return this;
  }

  var attrs, k;

  if(typeof key === 'object'){
    attrs = key;
  } else {
    attrs = {};
    attrs[key] = val;
  }

  for (k in attrs){
    this.attrs[k] = attrs[k];
  }

};
<commit_msg>Binding scope to process method<commit_after>"use strict";

var Node = module.exports = function(opts, methods, done){

  var self = this;

  if(typeof opts !== 'object'){
    return done (new Error('Bad node definition'));
  }

  if(typeof opts.id !== 'string'){
    return done (new Error('Bad node id'));
  }

  if(typeof methods === 'function'){
    done = methods;
    methods = {};
  }

  if(typeof methods === 'undefined'){
    methods = {};
  }

  this.id = opts.id;
  this.attrs = {};
  this.latchedMessage = undefined;

  ['init','stop'].forEach(function(method){
    if(methods.hasOwnProperty(method)){
      self[method] = methods[method];
    }
  });

  // This will be wrapped so we can capture the output message
  if(methods.hasOwnProperty('process')){
    this.processHandler = methods.process;
  }

  for(var k in opts){
    if(opts.hasOwnProperty(k) && k !== 'id'){
      this.attrs[k] = opts[k];
    }
  }

  this.init.call(this, function(){
    process.nextTick(done);
  });

  return this;

};

Node.prototype.init = function(done){
  done();
};

Node.prototype.stop = function(done){
  done();
};

// this will most likely be overridden whern creating the node
Node.prototype.processHandler = function(msg, done){
  // default is to just pass thru;
  done(null, msg);
};

Node.prototype.process = function(msg, done){
  var self = this;
  this.processHandler.call(this, msg, function(err, processedMsg){
    // latch most recent message
    self.latchedMessage = processedMsg;
    done(null, processedMsg);
  });
};

Node.prototype.inspect = function(){
  return {
    id: this.id,
    message: this.latchedMessage,
    attrs: this.attrs
  };
};

Node.prototype.message = function(){
  return this.latchedMessage;
};

Node.prototype.get = function(key){
  if(typeof(key) === 'undefined'){
    return this.attrs;
  }
  return this.attrs[key];
};

Node.prototype.set = function(key, val){

  if(!key){
    return this;
  }

  var attrs, k;

  if(typeof key === 'object'){
    attrs = key;
  } else {
    attrs = {};
    attrs[key] = val;
  }

  for (k in attrs){
    this.attrs[k] = attrs[k];
  }

};
<|endoftext|>


<commit_before>/* -*- Mode: Javascript; js-indent-level: 2 -*- */

var async = require('async');
var naan = require('naan');
var _ = require('underscore');
var util = require('util');

var node = module.exports = {
  /**
   * Save or update an object. If a new object is passed, the callback will
   * return a copy of that object with the <options.id> key set to the id of the
   * created object.
   */
  save: function(obj, key, val, callback) {
    if (typeof key == 'function') {
      callback = key;
      key = val = undefined;
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [key, val];
      async.map(obj, naan.ecurry(txn.save, args, [1, 2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(obj, !key);

    if (!this._isValidId(id)) {
      if (typeof obj !== 'object') {
        return callback(new Error("No data to save"));
      }

      this.node._create(obj, callback);
    } else {
      if (key) this.node._updateProp(obj, key, val, callback);
      else this.node._update(obj, callback);
    }
  },

  /**
   * Create a new object. Maps to /node.
   */
  _create: function(obj, callback) {
    var op = this.operation('node', obj);
    this.call(op, function(err, body, location) {
      if (err) {
        return callback(err);
      }

      var result = _.clone(obj);
      result[this.options.id] = this._extractId(location);

      callback(null, result);
    });
  },

  /**
   * Save the properties of an object. Maps to PUT /node/{id}/properties.
   */
  _update: function(obj, callback) {
    var id = this._getId(obj, true);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var untouchedObj = _.clone(obj);
    if (untouchedObj[this.options.id] != null) {
      delete untouchedObj[this.options.id];
    }

    var endpoint = util.format('%s/properties', this._nodeRoot(id));
    var op = this.operation(endpoint, 'PUT', untouchedObj);
    this.call(op, function(err, body, response) {
      if (err) {
        return callback(err);
      } else {
        return callback(null, obj);
      }
    });
  },

  _updateProp: function(obj, key, val, callback) {
    var id = this._getId(obj);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint = util.format('%s/properties/%s', this._nodeRoot(id), key);
    var op = this.operation(endpoint, 'PUT', val);
    this.call(op, function(err, body) {
      if (err) return callback(err);
      else {
        obj = _.clone(obj);
        obj[key] = val;
        callback(null, obj);
      }
    });
  },

  /**
   * Read an object's properties. Maps to GET node/{id}/properties.
   */
  read: function(id, property, callback) {
    if (typeof property == 'function') {
      callback = property;
      property = null;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.read, property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint;
    if (!property)
      endpoint = util.format('%s/properties', this._nodeRoot(id));
    else
      endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
    var op = this.operation(endpoint);
    this.call(op, function(err, body) {
      if (err) {
        return callback(err);
      } else {
        if (!property) body[this.options.id] = id;
        return callback(null, body);
      }
    });
  },

  /**
   * Delete an object. Maps to DELETE node/{id}
   * If force is truthy, delete node and all its relations.
   */
  delete: function(id, force, callback) {
    var property = null;
    if (typeof force == 'function') {
      callback = force;
      force = false;
    } else if (typeof force == 'string') {
      property = force;
      force = false;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.node.delete, force || property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    var object = id;
    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    if (!force) {
      var endpoint;
      if (!property) endpoint = this._nodeRoot(id);
      else
        endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
      var op = this.operation(endpoint, 'DELETE');
      this.call(op, function(err) {
        if (!property || err || typeof object != 'object') callback(err);
        else {
          delete object[property];
          callback(null, object);
        }
      });
    } else {
      this.query([
        'START node=node({root})',
        'MATCH node-[rel?]-()',
        'DELETE node, rel'
      ].join(' '), {root: id}, function(err) { callback(err) });
    }
  },

  /**
   * Relate objects together. maps to POST node/{first}/relationships
   */
  relate: function(startNode, type, endNode, properties, callback) {
    this.rel.create(startNode, type, endNode, properties, callback);
  },

  /**
   * Retrieve a set of relationships for the given node. Optionally specify the
   * direction and relationship name.
   *
   * `direction` must be one of "all", "in" or "out".
   *
   * db.relationships(obj|id, [direction, [relationshipName]], callback)
   */
  relationships: function(obj, direction, relName, callback) {
    if (typeof direction === 'function') {
      callback = direction;
      direction = 'all';
      relName = '';
    } else {
      if (typeof relName === 'function') {
        callback = relName;
        relName = '';
      }

      if (typeof direction !== 'string') {
        return callback(new Error('Invalid direction - ' + direction));
      } else if (typeof relName !== 'string') {
        return callback(new Error('Invalid relationship name - ' + relName));
      }

      direction = direction.toLowerCase();
      if (['in', 'all', 'out'].indexOf(direction) === -1) {
        return callback(new Error('Invalid direction - ' + direction));
      }
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [ direction, relName ];
      var rels = naan.ecurry(txn.node.relationships, args, [1, 2]);
      async.map(obj, rels, callback);
      return this._safeBatchCommit(txn);
    }

    var objId = this._getId(obj);

    if (!this._isValidId(objId)) {
      callback(new Error("Invalid ID"));
    }

    var nodeRoot = this._nodeRoot(objId);
    var endpoint = util.format('%s/relationships/%s', nodeRoot, direction);
    if (relName) {
      endpoint += "/" + relName;
    }
    var op = this.operation(endpoint);
    var self = this;
    this.call(op, function(err, rels) {
      if (err) {
        callback(err);
      } else {
        rels = rels.map(function(rel) {
          return self._createRelationshipObject(rel);
        });
        callback(null, rels);
      }
    });
  },

  /**
   * Perform a query based on a predicate. The predicate is translated to a
   * cypher query.
   */
  find: function(predicate, any, start, callback) {
    if (typeof any === 'function') {
      callback = any;
      any = false;
      start = 'node(*)';
    } else if (typeof start === 'function') {
      callback = start;
      start = 'node(*)';
    }

    if (Array.isArray(predicate)) {
      var txn = this._safeBatch();
      var finder = naan.ecurry(txn.node.find, [any, start], [1, 2]);
      async.map(predicate, finder, callback);
      return this._safeBatchCommit(txn);
    }

    if (typeof predicate !== 'object') {
      callback(new Error('Invalid Predicate'));
    }

    var matchers = Object.keys(predicate).reduce(function(matchers, key) {
      return matchers.push(util.format(" (n.%s = {%s}) ", key, key)), matchers;
    }, []);

    var cypher = [
      "START n = " + start + " WHERE",
      matchers.join(any ? "or" : "and"),
      "RETURN n"
    ].join(" ");

    var self = this;
    this.query(cypher, predicate, callback);
  },

  saveUnique: function(node, index, key, value, returnExistingOnConflict, cb) {
    if (typeof returnExistingOnConflict == 'function') {
      cb = returnExistingOnConflict;
      returnExistingOnConflict = false;
    }

    if (returnExistingOnConflict) {
      this.node.index.getOrSaveUnique(node, index, key, value, cb);
    } else {
      this.node.index.saveUniqueOrFail(node, index, key, value, cb);
    }
  },

  label: function(node, label, replace, callback) {
    if (typeof replace == 'function') {
      callback = replace;
      replace = false;
    }

    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, naan.ecurry(txn.label, [label, replace], [1,2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));

    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, replace ? 'PUT' : 'POST', label);
    this.call(op, function(err) {
      if (err) callback(err);
      else callback();
    });
  },

  nodesWithLabel: function(label, callback) {
    var endpoint = util.format('label/%s/nodes', label);
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, nodes) {
      if (err || !nodes) return callback(err, nodes);
      nodes = nodes.map(this._createNodeObject);
      callback(null, nodes);
    });
  },

  readLabels: function(node, callback) {
    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, txn.readLabels, function(err, allLabels) {
        if (err) return callback(err);
        callback(null, _.uniq(_.flatten(allLabels)));
      });
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));
    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, labels) {
      if (err) callback(err);
      else callback(null, labels);
    });
  }
}
<commit_msg>labels: implement reading all labels<commit_after>/* -*- Mode: Javascript; js-indent-level: 2 -*- */

var async = require('async');
var naan = require('naan');
var _ = require('underscore');
var util = require('util');

var node = module.exports = {
  /**
   * Save or update an object. If a new object is passed, the callback will
   * return a copy of that object with the <options.id> key set to the id of the
   * created object.
   */
  save: function(obj, key, val, callback) {
    if (typeof key == 'function') {
      callback = key;
      key = val = undefined;
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [key, val];
      async.map(obj, naan.ecurry(txn.save, args, [1, 2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(obj, !key);

    if (!this._isValidId(id)) {
      if (typeof obj !== 'object') {
        return callback(new Error("No data to save"));
      }

      this.node._create(obj, callback);
    } else {
      if (key) this.node._updateProp(obj, key, val, callback);
      else this.node._update(obj, callback);
    }
  },

  /**
   * Create a new object. Maps to /node.
   */
  _create: function(obj, callback) {
    var op = this.operation('node', obj);
    this.call(op, function(err, body, location) {
      if (err) {
        return callback(err);
      }

      var result = _.clone(obj);
      result[this.options.id] = this._extractId(location);

      callback(null, result);
    });
  },

  /**
   * Save the properties of an object. Maps to PUT /node/{id}/properties.
   */
  _update: function(obj, callback) {
    var id = this._getId(obj, true);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var untouchedObj = _.clone(obj);
    if (untouchedObj[this.options.id] != null) {
      delete untouchedObj[this.options.id];
    }

    var endpoint = util.format('%s/properties', this._nodeRoot(id));
    var op = this.operation(endpoint, 'PUT', untouchedObj);
    this.call(op, function(err, body, response) {
      if (err) {
        return callback(err);
      } else {
        return callback(null, obj);
      }
    });
  },

  _updateProp: function(obj, key, val, callback) {
    var id = this._getId(obj);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint = util.format('%s/properties/%s', this._nodeRoot(id), key);
    var op = this.operation(endpoint, 'PUT', val);
    this.call(op, function(err, body) {
      if (err) return callback(err);
      else {
        obj = _.clone(obj);
        obj[key] = val;
        callback(null, obj);
      }
    });
  },

  /**
   * Read an object's properties. Maps to GET node/{id}/properties.
   */
  read: function(id, property, callback) {
    if (typeof property == 'function') {
      callback = property;
      property = null;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.read, property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    var endpoint;
    if (!property)
      endpoint = util.format('%s/properties', this._nodeRoot(id));
    else
      endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
    var op = this.operation(endpoint);
    this.call(op, function(err, body) {
      if (err) {
        return callback(err);
      } else {
        if (!property) body[this.options.id] = id;
        return callback(null, body);
      }
    });
  },

  /**
   * Delete an object. Maps to DELETE node/{id}
   * If force is truthy, delete node and all its relations.
   */
  delete: function(id, force, callback) {
    var property = null;
    if (typeof force == 'function') {
      callback = force;
      force = false;
    } else if (typeof force == 'string') {
      property = force;
      force = false;
    }

    if (Array.isArray(id)) {
      var txn = this._safeBatch();
      async.map(id, naan.ncurry(txn.node.delete, force || property, 1), callback);
      return this._safeBatchCommit(txn);
    }

    var object = id;
    id = this._getId(id);
    if (!this._isValidId(id)) {
      return callback(new Error("Invalid ID"));
    }

    if (!force) {
      var endpoint;
      if (!property) endpoint = this._nodeRoot(id);
      else
        endpoint = util.format('%s/properties/%s', this._nodeRoot(id), property);
      var op = this.operation(endpoint, 'DELETE');
      this.call(op, function(err) {
        if (!property || err || typeof object != 'object') callback(err);
        else {
          delete object[property];
          callback(null, object);
        }
      });
    } else {
      this.query([
        'START node=node({root})',
        'MATCH node-[rel?]-()',
        'DELETE node, rel'
      ].join(' '), {root: id}, function(err) { callback(err) });
    }
  },

  /**
   * Relate objects together. maps to POST node/{first}/relationships
   */
  relate: function(startNode, type, endNode, properties, callback) {
    this.rel.create(startNode, type, endNode, properties, callback);
  },

  /**
   * Retrieve a set of relationships for the given node. Optionally specify the
   * direction and relationship name.
   *
   * `direction` must be one of "all", "in" or "out".
   *
   * db.relationships(obj|id, [direction, [relationshipName]], callback)
   */
  relationships: function(obj, direction, relName, callback) {
    if (typeof direction === 'function') {
      callback = direction;
      direction = 'all';
      relName = '';
    } else {
      if (typeof relName === 'function') {
        callback = relName;
        relName = '';
      }

      if (typeof direction !== 'string') {
        return callback(new Error('Invalid direction - ' + direction));
      } else if (typeof relName !== 'string') {
        return callback(new Error('Invalid relationship name - ' + relName));
      }

      direction = direction.toLowerCase();
      if (['in', 'all', 'out'].indexOf(direction) === -1) {
        return callback(new Error('Invalid direction - ' + direction));
      }
    }

    if (Array.isArray(obj)) {
      var txn = this._safeBatch();
      var args = [ direction, relName ];
      var rels = naan.ecurry(txn.node.relationships, args, [1, 2]);
      async.map(obj, rels, callback);
      return this._safeBatchCommit(txn);
    }

    var objId = this._getId(obj);

    if (!this._isValidId(objId)) {
      callback(new Error("Invalid ID"));
    }

    var nodeRoot = this._nodeRoot(objId);
    var endpoint = util.format('%s/relationships/%s', nodeRoot, direction);
    if (relName) {
      endpoint += "/" + relName;
    }
    var op = this.operation(endpoint);
    var self = this;
    this.call(op, function(err, rels) {
      if (err) {
        callback(err);
      } else {
        rels = rels.map(function(rel) {
          return self._createRelationshipObject(rel);
        });
        callback(null, rels);
      }
    });
  },

  /**
   * Perform a query based on a predicate. The predicate is translated to a
   * cypher query.
   */
  find: function(predicate, any, start, callback) {
    if (typeof any === 'function') {
      callback = any;
      any = false;
      start = 'node(*)';
    } else if (typeof start === 'function') {
      callback = start;
      start = 'node(*)';
    }

    if (Array.isArray(predicate)) {
      var txn = this._safeBatch();
      var finder = naan.ecurry(txn.node.find, [any, start], [1, 2]);
      async.map(predicate, finder, callback);
      return this._safeBatchCommit(txn);
    }

    if (typeof predicate !== 'object') {
      callback(new Error('Invalid Predicate'));
    }

    var matchers = Object.keys(predicate).reduce(function(matchers, key) {
      return matchers.push(util.format(" (n.%s = {%s}) ", key, key)), matchers;
    }, []);

    var cypher = [
      "START n = " + start + " WHERE",
      matchers.join(any ? "or" : "and"),
      "RETURN n"
    ].join(" ");

    var self = this;
    this.query(cypher, predicate, callback);
  },

  saveUnique: function(node, index, key, value, returnExistingOnConflict, cb) {
    if (typeof returnExistingOnConflict == 'function') {
      cb = returnExistingOnConflict;
      returnExistingOnConflict = false;
    }

    if (returnExistingOnConflict) {
      this.node.index.getOrSaveUnique(node, index, key, value, cb);
    } else {
      this.node.index.saveUniqueOrFail(node, index, key, value, cb);
    }
  },

  label: function(node, label, replace, callback) {
    if (typeof replace == 'function') {
      callback = replace;
      replace = false;
    }

    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, naan.ecurry(txn.label, [label, replace], [1,2]), callback);
      return this._safeBatchCommit(txn);
    }

    var id = this._getId(node);
    if (!this._isValidId(id)) return callback(new Error("Invalid ID"));

    var endpoint = util.format('%s/labels', this._nodeRoot(id));
    var op = this.operation(endpoint, replace ? 'PUT' : 'POST', label);
    this.call(op, function(err) {
      if (err) callback(err);
      else callback();
    });
  },

  nodesWithLabel: function(label, callback) {
    var endpoint = util.format('label/%s/nodes', label);
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, nodes) {
      if (err || !nodes) return callback(err, nodes);
      nodes = nodes.map(this._createNodeObject);
      callback(null, nodes);
    });
  },

  readLabels: function(node, callback) {
    if (typeof node == 'function') {
      callback = node;
      node = undefined;
    }

    if (Array.isArray(node)) {
      var txn = this._safeBatch();
      async.map(node, txn.readLabels, function(err, allLabels) {
        if (err) return callback(err);
        callback(null, _.uniq(_.flatten(allLabels)));
      });
      return this._safeBatchCommit(txn);
    }

    var endpoint;
    if (node) {
      var id = this._getId(node);
      if (!this._isValidId(id)) return callback(new Error("Invalid ID"));
      endpoint = util.format('%s/labels', this._nodeRoot(id));
    } else {
      endpoint = 'labels';
    }
    var op = this.operation(endpoint, 'GET');
    this.call(op, function(err, labels) {
      if (err) callback(err);
      else callback(null, labels);
    });
  }
}
<|endoftext|>