<commit_before>var config = require('./config');
var manageUsers = require('./manageUsers.js');
var request = require('request');
//var mongo = require('./mongoFunctions.js');
var adm1 = config.adm1;
var adm2 = config.adm2;

function createMessage(type, id, content, callback){
	var message = {
		recipient: {
		  id: id
		}
	};
	console.log(content);
	if(type == 'text'){
		message['message']={text: content};
	}else{
		message['message']={
			attachment:{
				type: 'template',
				payload:{
					template_type: 'button',
					text: content['text'],
					buttons: [
						content['buttons']
					]
				}
			}
		}
	}
	setImmediate(function(){
		callback(message);
	});
}

function createButtons(tab, callback){
	var buttons;
	for(var i = 0; i < tab.length; i++){
		var btn = tab[i];
		var singleBTN={
			type: btn[1],
			title: btn[3]
		}
		if(btn[1]=='web_url'){
			singleBTN['url']=btn[2];
		} else if(btn[1]=='postback'){
			singleBTN['payload']=btn[2];
		}
		buttons+=singleBTN;
	}
	setImmediate(function(){
		callback(buttons);
	});
}

function sendSubstitutions(senderID, message){
	var admMessage1 = {
		recipient: {
		  id: adm1
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var admMessage2 = {
		recipient: {
		  id: adm2
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var body = {
		'mode': 'classList',
		'param': 'today'
	};
	message = message.toLowerCase();
	var opt = message[0];
	var help = 'Dostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';
	var messageData = {
		recipient: {
		  id: senderID
		},
		message: {
		  text: help
		}
	};
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	switch(opt){
		case '0':
			break;
		case '1':
			body['param']='tomorrow'; //masz else, więc jak dam dobrze to przejdzie, chociaż ty piszesz źle
			break;
		case '2':
			body['mode']='NO';
			var messageData = {
				recipient: {
				  id: senderID
				},
				message: {
				  text: 'Skontaktujemy się aby odpowiedzieć na pytanie.'
				}
			};
			callSendAPI(messageData);
			callSendAPI(admMessage1);
			callSendAPI(admMessage2);
			break;
		case '3':
			body['mode']='NO';
			createMessage('text', senderID, 'Jakiś tekst - test', function(message){
				callSendAPI(message);
			});
			createButtons([['web_url', 'https://google.com', 'TEST LINK'],['postback', 'payload', 'POSTBACK - TEST']], function(btns){
				console.log(JSON.stringify(btns));
			});
			break;
		default:
			body['mode']='NO';
			callSendAPI(messageData);
			break;
	};
	if(body['mode'] != 'NO'){
		manageUsers.postCall('0000', body, function(classes){
			var is = 0;
			for(var i = 0; i < classes.length; i++){
				if(classes[i] == reqClass){
					is++;
				}
			}
			if(is > 0){
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  attachment:{
						  type: 'template',
						  payload:{
								template_type: 'button',
								text: 'zastępstwa',
								buttons:[
									{
										type: 'web_url',
										url: 'https://domek.emadar.eu',
										title: 'Sprawdź na stronie'
									},
									{
										type: 'postback',
										title: 'Send on chat',
										payload: message
									}
								]
						  }
					  }
					}
				};
			} else {
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  text: 'brak zastępstw'
					}
				};
			}
			callSendAPI(messageData);
		});
	}
}

function sendList(senderID, message){
	var body = {
		'mode': 'getChanges',
		'param': 'today'
	};
	if(message[0]=='1'){
		body['param']='tomorrow';
	}
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	manageUsers.postCall('0000', body, function(obj){
		var subs = obj['substitution'];
		var msg = "";
		for(var i = 0; i < subs.length; i++){
			var oneSub = subs[i];
			if(oneSub.classes == reqClass){
				var changes = oneSub['changes'];
				if(oneSub.cancelled[0]){
					msg+='anulowanie';
				} else {
					msg+='Typ: ' + oneSub.substitution_types;
				}
				msg+='\nLekcja: ' + oneSub.periods;
				msg+='\nNauczyciel: ' + oneSub.teachers;
				if(changes){
					if(changes.teachers){
						msg+=' => ' + changes.teachers;
					}
				}
				msg+='\nPrzedmiot: ' + oneSub.subjects;
				if(changes){
					if(changes.subjects){
						msg+= ' => ' + changes.subjects;
					}
				}
				msg+='\nSala: ' + oneSub.classrooms;
				if(changes){
					if(changes.classrooms){
						msg+=' => ' + changes.classrooms;
					}
				}
				if(oneSub.groupnames){
					if(oneSub.groupnames != ""){
						msg+='\nGrupa: ' + oneSub.groupnames;
					}
				}
				if(oneSub.note){
					if(oneSub.note != ""){
						msg+='\nKomentarz: '  + oneSub.note;
					}
				}
				var messageData = {
					recipient: {
						id: senderID
					},
					message: {
						text: msg
					}
				};
				callSendAPI(messageData);
				msg='';
			}
		}
	});
}

function receivedPostback(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfPostback = event.timestamp;

	// The 'payload' param is a developer-defined field which is set in a postback
	// button for Structured Messages.
	var payload = event.postback.payload;

	console.log("Received postback for user %d and page %d with payload '%s' " +
	"at %d", senderID, recipientID, payload, timeOfPostback);

	sendList(senderID, payload);
}

function receivedMessage(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfMessage = event.timestamp;
	var message = event.message;

	console.log("Received message for user %d and page %d at %d with message:",
	senderID, recipientID, timeOfMessage);
	console.log(JSON.stringify(message));

	var messageId = message.mid;

	var messageText = message.text;
	var messageAttachments = message.attachments;
	var help = 'NIE PRZYJMUJEMY ZAŁĄCZNIKÓW\nDostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';

	if (messageText) {
		sendSubstitutions(senderID, messageText);
	} else {
		var messageData = {
			recipient: {
			  id: senderID
			},
			message: {
			  text: help
			}
		};
		callSendAPI(messageData);
	}
}

function callSendAPI(messageData) {
  request({
    uri: 'https://graph.facebook.com/v2.7/me/messages',
    qs: { access_token: config.pageToken },
    method: 'POST',
    json: messageData

  }, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var recipientId = body.recipient_id;
      var messageId = body.message_id;

      console.log("Successfully sent generic message with id %s to recipient %s",
        messageId, recipientId);
    } else {
      console.error("Unable to send message.");
      console.error(response);
      console.error(error);
    }
  });
}

/*function sendTextMessage(recipientId, messageText) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      text: messageText
    }
  };

  callSendAPI(messageData);
}*/

/*function sendGenericMessage(recipientId) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      attachment: {
        type: "template",
        payload: {
          template_type: "generic",
          elements: [{
            title: "rift",
            subtitle: "Next-generation virtual reality",
            item_url: "https://www.oculus.com/en-us/rift/",
            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/rift/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for first bubble",
            }],
          }, {
            title: "touch",
            subtitle: "Your Hands, Now in VR",
            item_url: "https://www.oculus.com/en-us/touch/",
            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/touch/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for second bubble",
            }]
          }]
        }
      }
    }
  };

  callSendAPI(messageData);
}*/

exports.receivedPostback=receivedPostback;
exports.receivedMessage=receivedMessage;
<commit_msg>debugging<commit_after>var config = require('./config');
var manageUsers = require('./manageUsers.js');
var request = require('request');
//var mongo = require('./mongoFunctions.js');
var adm1 = config.adm1;
var adm2 = config.adm2;

function createMessage(type, id, content, callback){
	var message = {
		recipient: {
		  id: id
		}
	};
	console.log(content);
	if(type == 'text'){
		message['message']={text: content};
	}else{
		message['message']={
			attachment:{
				type: 'template',
				payload:{
					template_type: 'button',
					text: content['text'],
					buttons: [
						content['buttons']
					]
				}
			}
		}
	}
	setImmediate(function(){
		callback(message);
	});
}

function createButtons(tab, callback){
	var buttons;
	for(var i = 0; i < tab.length; i++){
		var btn = tab[i];
		var singleBTN={
			type: btn[1],
			title: btn[3]
		}
		if(btn[1]=='web_url'){
			singleBTN['url']=btn[2];
		} else if(btn[1]=='postback'){
			singleBTN['payload']=btn[2];
		}
		console.log(JSON.stringify(singleBTN));
		buttons+=singleBTN;
	}
	setImmediate(function(){
		callback(buttons);
	});
}

function sendSubstitutions(senderID, message){
	var admMessage1 = {
		recipient: {
		  id: adm1
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var admMessage2 = {
		recipient: {
		  id: adm2
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var body = {
		'mode': 'classList',
		'param': 'today'
	};
	message = message.toLowerCase();
	var opt = message[0];
	var help = 'Dostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';
	var messageData = {
		recipient: {
		  id: senderID
		},
		message: {
		  text: help
		}
	};
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	switch(opt){
		case '0':
			break;
		case '1':
			body['param']='tomorrow'; //masz else, więc jak dam dobrze to przejdzie, chociaż ty piszesz źle
			break;
		case '2':
			body['mode']='NO';
			var messageData = {
				recipient: {
				  id: senderID
				},
				message: {
				  text: 'Skontaktujemy się aby odpowiedzieć na pytanie.'
				}
			};
			callSendAPI(messageData);
			callSendAPI(admMessage1);
			callSendAPI(admMessage2);
			break;
		case '3':
			body['mode']='NO';
			createMessage('text', senderID, 'Jakiś tekst - test', function(message){
				callSendAPI(message);
			});
			createButtons([['web_url', 'https://google.com', 'TEST LINK'],['postback', 'payload', 'POSTBACK - TEST']], function(btns){
				console.log(JSON.stringify(btns));
			});
			break;
		default:
			body['mode']='NO';
			callSendAPI(messageData);
			break;
	};
	if(body['mode'] != 'NO'){
		manageUsers.postCall('0000', body, function(classes){
			var is = 0;
			for(var i = 0; i < classes.length; i++){
				if(classes[i] == reqClass){
					is++;
				}
			}
			if(is > 0){
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  attachment:{
						  type: 'template',
						  payload:{
								template_type: 'button',
								text: 'zastępstwa',
								buttons:[
									{
										type: 'web_url',
										url: 'https://domek.emadar.eu',
										title: 'Sprawdź na stronie'
									},
									{
										type: 'postback',
										title: 'Send on chat',
										payload: message
									}
								]
						  }
					  }
					}
				};
			} else {
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  text: 'brak zastępstw'
					}
				};
			}
			callSendAPI(messageData);
		});
	}
}

function sendList(senderID, message){
	var body = {
		'mode': 'getChanges',
		'param': 'today'
	};
	if(message[0]=='1'){
		body['param']='tomorrow';
	}
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	manageUsers.postCall('0000', body, function(obj){
		var subs = obj['substitution'];
		var msg = "";
		for(var i = 0; i < subs.length; i++){
			var oneSub = subs[i];
			if(oneSub.classes == reqClass){
				var changes = oneSub['changes'];
				if(oneSub.cancelled[0]){
					msg+='anulowanie';
				} else {
					msg+='Typ: ' + oneSub.substitution_types;
				}
				msg+='\nLekcja: ' + oneSub.periods;
				msg+='\nNauczyciel: ' + oneSub.teachers;
				if(changes){
					if(changes.teachers){
						msg+=' => ' + changes.teachers;
					}
				}
				msg+='\nPrzedmiot: ' + oneSub.subjects;
				if(changes){
					if(changes.subjects){
						msg+= ' => ' + changes.subjects;
					}
				}
				msg+='\nSala: ' + oneSub.classrooms;
				if(changes){
					if(changes.classrooms){
						msg+=' => ' + changes.classrooms;
					}
				}
				if(oneSub.groupnames){
					if(oneSub.groupnames != ""){
						msg+='\nGrupa: ' + oneSub.groupnames;
					}
				}
				if(oneSub.note){
					if(oneSub.note != ""){
						msg+='\nKomentarz: '  + oneSub.note;
					}
				}
				var messageData = {
					recipient: {
						id: senderID
					},
					message: {
						text: msg
					}
				};
				callSendAPI(messageData);
				msg='';
			}
		}
	});
}

function receivedPostback(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfPostback = event.timestamp;

	// The 'payload' param is a developer-defined field which is set in a postback
	// button for Structured Messages.
	var payload = event.postback.payload;

	console.log("Received postback for user %d and page %d with payload '%s' " +
	"at %d", senderID, recipientID, payload, timeOfPostback);

	sendList(senderID, payload);
}

function receivedMessage(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfMessage = event.timestamp;
	var message = event.message;

	console.log("Received message for user %d and page %d at %d with message:",
	senderID, recipientID, timeOfMessage);
	console.log(JSON.stringify(message));

	var messageId = message.mid;

	var messageText = message.text;
	var messageAttachments = message.attachments;
	var help = 'NIE PRZYJMUJEMY ZAŁĄCZNIKÓW\nDostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';

	if (messageText) {
		sendSubstitutions(senderID, messageText);
	} else {
		var messageData = {
			recipient: {
			  id: senderID
			},
			message: {
			  text: help
			}
		};
		callSendAPI(messageData);
	}
}

function callSendAPI(messageData) {
  request({
    uri: 'https://graph.facebook.com/v2.7/me/messages',
    qs: { access_token: config.pageToken },
    method: 'POST',
    json: messageData

  }, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var recipientId = body.recipient_id;
      var messageId = body.message_id;

      console.log("Successfully sent generic message with id %s to recipient %s",
        messageId, recipientId);
    } else {
      console.error("Unable to send message.");
      console.error(response);
      console.error(error);
    }
  });
}

/*function sendTextMessage(recipientId, messageText) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      text: messageText
    }
  };

  callSendAPI(messageData);
}*/

/*function sendGenericMessage(recipientId) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      attachment: {
        type: "template",
        payload: {
          template_type: "generic",
          elements: [{
            title: "rift",
            subtitle: "Next-generation virtual reality",
            item_url: "https://www.oculus.com/en-us/rift/",
            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/rift/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for first bubble",
            }],
          }, {
            title: "touch",
            subtitle: "Your Hands, Now in VR",
            item_url: "https://www.oculus.com/en-us/touch/",
            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/touch/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for second bubble",
            }]
          }]
        }
      }
    }
  };

  callSendAPI(messageData);
}*/

exports.receivedPostback=receivedPostback;
exports.receivedMessage=receivedMessage;
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
var api = {};
(function(api) {
    var log = new Log('lifecycle_api');
    var getAsset = function(options, am) {
        var asset;
        try {
            asset = am.get(options.id);
        } catch (e) {
            log.warn('Unable to locate the asset with id: ' + options.id);
        }
        return asset;
    };
    var validateOptions = function(options, req, res, session) {
        if (!options.type) {
            log.error('Unable to obtain state information without knowing the type of asset ' + options.id);
            throw 'Unable to obtain state information without knowing the type of asset ' + options.id;
        }
    };
    var validateAsset = function(asset, options) {
        if (!asset) {
            log.error('Unable to locate asset information of ' + options.id);
            throw 'Unable to locate asset information of ' + options.id;
        }
        if (!asset.lifecycle) {
            log.error('The asset ' + options.id + ' does not have an associated lifecycle');
            throw 'The asset ' + options.id + ' does not have an associated lifecycle';
        }
        if (!asset.lifecycleState) {
            log.error('The asset ' + options.id + ' does not have a lifecycle state.');
            throw 'The asset ' + options.id + ' does not have a lifecycle state.';
        }
    };
    api.changeState = function(options, req, res, session) {
        var success = false;
        validateOptions(options, req, res, session);
        if (!options.nextState) {
            log.warn('A next state has not been provided');
            return success;
        }
        //Obtain the tenantId
        var server = require('store').server;
        var asset = require('rxt').asset;
        var user = server.current(session);
        var tenantId = user.tenantId;
        var am = asset.createUserAssetManager(session, options.type);
        var asset = getAsset(options, am);
        validateAsset(asset, options)
        //Obtain the lifecycle
        var lcApi = require('lifecycle').api;
        var lifecycle = lcApi.getLifecycle(asset.lifecycle, tenantId);
        var action = lifecycle.transitionAction(asset.lifecycleState, options.nextState);
        if (!action) {
            log.warn('It is not possible to reach ' + options.nextState + ' from ' + asset.lifecycleState);
            return success;
        }
        success = am.invokeLcAction(asset, action);
        return success;
    };
    var isDeletable = function(assetState, deletableStates) {
        var assetState = assetState ? assetState.toLowerCase() : assetState;
        for (var index in deletableStates) {
            if (deletableStates[index].toLowerCase() == assetState) {
                return true;
            }
        }
        return false;
    };
    var setCheckItemState = function(checkItems, asset, am) {
        //Obtain the check item states for the asset
        //TODO; This method throws an exception if check list items are not present
        var assetCheckItems = am.getLifecycleCheckItems(asset);
        var item;
        for (var index in assetCheckItems) {
            item = assetCheckItems[index];
            if (checkItems[index]) {
                checkItems[index].checked = item.checked;
            }
        }
        return checkItems;
    };
    /**
     * The function changes the state of a single check item
     * @param  {[type]} checkItemIndex      The index of the check item to be changed
     * @param  {[type]} checkItemIndexState The new state of the check item
     * @param  {[type]} asset               The asset on which the operation needs to be performed
     * @param  {[type]} state               The state information of the current asset
     * @param  {[type]} am                  [description]
     */
    var updateCheckItemState = function(checkItemIndex, checkItemIndexState, asset, state, am) {
        //Check if the index provided is valid
        if ((checkItemIndex > 0) && (checkItemIndex < state.checkItems.length)) {
            log.warn('Unable to change the state of the check item as the index does not point to a valid check item.The check item index must be between 0 and ' + state.checkItems.length);
            return;
        }
        //Check if the check item state is the same as the next state
        if (state.checkItems[checkItemIndex].checked == checkItemIndexState) {
            log.warn('The state of the check item at index ' + checkItemIndex + ' was not changed as it is already ' + checkItemIndexState);
        }
        //Invoke the state change
        try {
            am.invokeLifecycleCheckItem(asset, checkItemIndex, checkItemIndexState);
        } catch (e) {
            log.error(e);
            log.warn('Unable to change the state of check item ' + checkItemIndex + ' to ' + checkItemIndexState);
        }
    };
    /**
     * The function updates the check items for a given asset
     * @param  {[type]} options [description]
     * @param  {[type]} asset   [description]
     * @param  {[type]} am      [description]
     * @param  {[type]} state   [description]
     * @return {[type]}         [description]
     */
    var updateCheckItemStates = function(options, asset, am, state) {
        var success = false;
        //Check if the current state has any check items
        if ((state.checkItems) && (state.checkItems.length > 0)) {
            log.warn('Unable to change the state of the check item as the current state(' + state.id + ') does not have any check items');
            return success;
        }
        //Check if the check items has been provided
        if (!options.checkItems) {
            log.warn('Unable to update check items as no check items have been provided.');
            success = true;
            return success;
        }
        //Assume checking items will succeed
        success = true;
        var checkItemIndex;
        var checkItemIndexState;
        var checkItem;
        //Go through each check item in the check items
        for (var index in options.checkItems) {
            checkItem = options.checkItems[index];
            checkItemIndex = checkItem.index;
            checkItemIndexState = checkItem.checked;
            if ((checkItemIndex) && (checkItemIndexState)) {
                updateCheckItemState(checkItemIndex, checkItemIndexState, asset, state, am);
            }
        }
        return success;
    };
    api.getState = function(options, req, res, session) {
        var state = {};
        validateOptions(options, req, res, session);
        var assetApi = require('rxt').asset;
        var coreApi = require('rxt').core;
        var am = assetApi.createUserAssetManager(session, options.type);
        var server = require('store').server;
        var user = server.current(session);
        var tenantId = user.tenantId;
        var asset = getAsset(options, am);
        validateAsset(asset, options);
        var lcApi = require('lifecycle').api;
        var lifecycle = lcApi.getLifecycle(asset.lifecycle, tenantId);
        var rxtManager = coreApi.rxtManager(tenantId);
        //Obtain the state data
        state = lifecycle.state(asset.lifecycleState);
        if (!state) {
            throw 'Unable to locate state information for ' + asset.lifecycleState;
        }
        //Obtain the deletable states
        state.deletableStates = rxtManager.getDeletableStates(options.type);
        //Determine if the current state is a deletable state
        state.isDeletable = isDeletable(asset.lifecycleState, state.deletableStates);
        //Update the state of the check items
        state.checkItems = setCheckItemState(state.checkItems, asset, am);
        return state;
    };
    /**
     * The function will obtain the definition of the requested lifecycle
     * @return {[type]}         A JSON object definining the structure of the lifecycle
     */
    api.getLifecycle = function(options, req, res, session) {
        var lcApi = require('lifecycle').api;
        var server = require('store').server;
        var lifecycle = null;
        var user = server.current(session);
        if (!options.name) {
            log.warn('Unable to locate lifecycle definition as a name has not been provided.Please invoke the API with a lifecycle name');
            return lifecycle;
        }
        lifecycle = lcApi.getLifecycle(options.name, user.tenantId);
        if (!lifecycle) {
            log.warn('A lifecycle was not located for the lifecycle name: ' + options.name);
        }
        return lifecycle;
    };
    /**
     * The function will return a list of all available lifecycles for the currently logged in user's tenant.
     * @return {[type]}         An array of strings with the names of the lifecycles
     */
    api.getLifecycles = function(options, req, res, session) {
        var lcApi = require('lifecycle').api;
        var lifecycles = lcApi.getLifecycles();
        return lifecycles;
    };
    /**
     * The function changes the state of a set of check items sent as an array with each element been  { index: number , checked:true}
     * @return {[type]}         A boolean value indicating the success of the operation
     */
    api.checkItems = function(options, req, res, session) {
        var success = false;
        validateOptions(options, req, res, session);
        var assetApi = require('rxt').asset;
        var am = assetApi.createUserAssetManager(session, options.type);
        var asset = getAsset(options, am);
        validateAsset(asset, options);
        var state = this.getState(options, req, res, session);
        success = changeCheckitemState(options, asset, am, state, true);
        return success;
    };
}(api));<commit_msg>update-lifecycle-api-with checklist items<commit_after>/*
 *  Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  WSO2 Inc. licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
var api = {};
(function(api) {
    var log = new Log('lifecycle_api');
    /**
     * get asset by id
     * @param  {json} options {options.id:<asset-id>}
     * @param  {[type]} am      [description]
     */
    var getAsset = function(options, am) {
        var asset;
        try {
            asset = am.get(options.id);
        } catch (e) {
            log.warn('Unable to locate the asset with id: ' + options.id);
        }
        return asset;
    };

    /**
     * Validate asset type
     * @param  {JSON} options [options.id:<asset-id>,options.type:<asset-type>]
     * @param  {[type]} req     [description]
     * @param  {[type]} res     [description]
     * @param  {[type]} session [description]
     */
    var validateOptions = function(options, req, res, session) {
        if (!options.type) {
            log.error('Unable to obtain state information without knowing the type of asset ' + options.id);
            throw 'Unable to obtain state information without knowing the type of asset ' + options.id;
        }
    };
    /**
     * Validate asset by id, check whether a asset is available by id
     * @param  {[type]} asset   [description]
     * @param  {[type]} options [description]
     * @return {[type]}         [description]
     */
    var validateAsset = function(asset, options) {
        if (!asset) {
            log.error('Unable to locate asset information of ' + options.id);
            throw 'Unable to locate asset information of ' + options.id;
        }
        if (!asset.lifecycle) {
            log.error('The asset ' + options.id + ' does not have an associated lifecycle');
            throw 'The asset ' + options.id + ' does not have an associated lifecycle';
        }
        if (!asset.lifecycleState) {
            log.error('The asset ' + options.id + ' does not have a lifecycle state.');
            throw 'The asset ' + options.id + ' does not have a lifecycle state.';
        }
    };
    /**
     * change the state of an asset
     * @param  {Json} options [description]
     * @param  {[type]} req     [description]
     * @param  {[type]} res     [description]
     * @param  {[type]} session [description]
     */
    api.changeState = function(options, req, res, session) {
        var success = false;
        validateOptions(options, req, res, session);
        if (!options.nextState) {
            log.warn('A next state has not been provided');
            throw 'A next state has not been provided';
            return success;
        }
        //Obtain the tenantId
        var server = require('store').server;
        var asset = require('rxt').asset;
        var user = server.current(session);//get current user
        var tenantId = user.tenantId;//get tenant
        var am = asset.createUserAssetManager(session, options.type);//get asset manager
        var asset = getAsset(options, am);//get asset
        validateAsset(asset, options)//validate asset
        //Obtain the lifecycle
        var lcApi = require('lifecycle').api;
        var lifecycle = lcApi.getLifecycle(asset.lifecycle, tenantId);//get lifecycle binded with asset
        var action = lifecycle.transitionAction(asset.lifecycleState, options.nextState);//
        if (!action) {
            log.warn('It is not possible to reach ' + options.nextState + ' from ' + asset.lifecycleState);
            throw 'It is not possible to reach ' + options.nextState + ' from ' + asset.lifecycleState;
            return success;
        }
        success = am.invokeLcAction(asset, action);
        return success;
    };

    api.invokeStateTransition = function(options, req, res, session) {
        var stateReq = req.getAllParameters('UTF-8');
        log.info(stateReq);
        var successChecking = false;
        var successStateChange = false;
        var msg = '';

        if(stateReq.checkItems){
            try{
                options.checkItems = JSON.parse(stateReq.checkItems);
                successChecking = checkItems(options, req, res, session);
                if(successChecking){
                    msg = msg+'Checklist items checked successfully! ';
                }else{
                    msg = msg+'Checklist items checking failed! ';
                }

            }catch(e){
                 msg =msg +' '+e;
                 throw msg;
            }
        }
        if(stateReq.nextState){
            var isCommentsEnabled = true;
            if(isCommentsEnabled){
                if(stateReq.comment){
                    options.comment = stateReq.comment;
                }else{
                    msg = msg+' Please provide a comment for this state transition!';
                    throw msg;
                }
            }
            options.nextState = stateReq.nextState;
            try{
                successStateChange = this.changeState(options, req, res, session);
                if(successStateChange){
                    msg = msg+' State changed successfully to '+options.nextState+'!';
                }else{
                    msg = msg+' An error occured while changing state to '+options.nextState+'!';
                }
            }catch(e){
                msg =msg+ ' '+e;
                throw msg;
            }
        }
        if(!stateReq.checkItems && !stateReq.nextState){
             msg = 'Checklist items or next state is not provided!';
             throw msg;
        }

        return msg;
    };

    var isDeletable = function(assetState, deletableStates) {
        var assetState = assetState ? assetState.toLowerCase() : assetState;
        for (var index in deletableStates) {
            if (deletableStates[index].toLowerCase() == assetState) {
                return true;
            }
        }
        return false;
    };
    var setCheckItemState = function(checkItems, asset, am) {
        //Obtain the check item states for the asset
        //TODO; This method throws an exception if check list items are not present
        var assetCheckItems = am.getLifecycleCheckItems(asset);
        var item;
        for (var index in assetCheckItems) {
            item = assetCheckItems[index];
            if (checkItems[index]) {
                checkItems[index].checked = item.checked;
            }
        }
        return checkItems;
    };
    /**
     * The function changes the state of a single check item
     * @param  {[type]} checkItemIndex      The index of the check item to be changed
     * @param  {[type]} checkItemIndexState The new state of the check item
     * @param  {[type]} asset               The asset on which the operation needs to be performed
     * @param  {[type]} state               The state information of the current asset
     * @param  {[type]} am                  [description]
     */
    var updateCheckItemState = function(checkItemIndex, checkItemIndexState, asset, state, am) {
        //Check if the index provided is valid
        var msg;
        if ((checkItemIndex < 0) || (checkItemIndex > state.checkItems.length-1)) {
            msg = 'Unable to change the state of the check item as the index does not point to a valid check item.The check item index must be between 0 and ' + state.checkItems.length+'.';
            log.warn(msg);
            throw msg;
            //return;
        }
        //Check if the check item state is the same as the next state
        if (state.checkItems[checkItemIndex].checked == checkItemIndexState) {
            msg= 'The state of the check item at index ' + checkItemIndex + ' was not changed as it is already ' + checkItemIndexState;
            log.warn(msg);
            //throw msg;
        }
        //Invoke the state change
        try {
            am.invokeLifecycleCheckItem(asset, checkItemIndex, checkItemIndexState);
        } catch (e) {
            log.error(e);
            msg = 'Unable to change the state of check item ' + checkItemIndex + ' to ' + checkItemIndexState;
            throw msg;
        }
    };

    /**
     * The function updates the check items for a given asset
     * @param  {[type]} options [description]
     * @param  {[type]} asset   [description]
     * @param  {[type]} am      [description]
     * @param  {[type]} state   [description]
     * @return {[type]}         [description]
     */
    var updateCheckItemStates = function(options, asset, am, state) {
        var success = false;
        var msg = '';
        //Check if the current state has any check items
        if ((state.checkItems) && (state.checkItems.length < 1)) {
            msg = 'Unable to change the state of the check item as the current state(' + state.id + ') does not have any check items';
            log.warn(msg);
            throw  msg;
        }
        //Check if the check items has been provided
        if (!options.checkItems) {
            log.warn('Unable to update check items as no check items have been provided.');
            success = true;
            return success;
        }
        //Assume checking items will succeed
        success = true;
        var checkItemsList = options.checkItems;
        var checkItemIndex;
        var checkItemIndexState;
        var checkItem;
        //Go through each check item in the check items
        for (var index in checkItemsList) {
            checkItem = checkItemsList[index];
            checkItemIndex = checkItem.index;
            checkItemIndexState = checkItem.checked;
            if ((checkItemIndex!=null) && (checkItemIndexState==true || checkItemIndexState==false)) {
                updateCheckItemState(checkItemIndex, checkItemIndexState, asset, state, am);
            }
        }
        return success;
    };

    /**
     * The function will obtain the state detail of a asset
     * @return {[type]}         A JSON object definining the structure of the lifecycle
     * @param  {[type]} options :options.id=<asset-id>
     * @param  {[type]} req     :request
     * @param  {[type]} res     :response
     * @param  {[type]} session  : sessionID
     */
    api.getState = function(options, req, res, session) {
        var state = {};
        validateOptions(options, req, res, session);
        var assetApi = require('rxt').asset;
        var coreApi = require('rxt').core;
        var am = assetApi.createUserAssetManager(session, options.type);
        var server = require('store').server;
        var user = server.current(session);
        var tenantId = user.tenantId;
        var asset = getAsset(options, am);
        validateAsset(asset, options);
        var lcApi = require('lifecycle').api;
        var lifecycle = lcApi.getLifecycle(asset.lifecycle, tenantId);
        var rxtManager = coreApi.rxtManager(tenantId);
        //Obtain the state data
        state = lifecycle.state(asset.lifecycleState);
        if (!state) {
            throw 'Unable to locate state information for ' + asset.lifecycleState;
        }
        //Obtain the deletable states
        state.deletableStates = rxtManager.getDeletableStates(options.type);
        //Determine if the current state is a deletable state
        state.isDeletable = isDeletable(asset.lifecycleState, state.deletableStates);
        //Update the state of the check items
        state.checkItems = setCheckItemState(state.checkItems, asset, am);
        return state;
    };

    /**
     * The function will obtain the definition of the requested lifecycle
     * @return {[type]}         A JSON object definining the structure of the lifecycle
     */
    api.getLifecycle = function(options, req, res, session) {
        var lcApi = require('lifecycle').api;
        var server = require('store').server;
        var lifecycle = null;
        var user = server.current(session);
        if (!options.name) {
            log.warn('Unable to locate lifecycle definition as a name has not been provided.Please invoke the API with a lifecycle name');
            return lifecycle;
        }
        lifecycle = lcApi.getLifecycle(options.name, user.tenantId);
        if (!lifecycle) {
            log.warn('A lifecycle was not located for the lifecycle name: ' + options.name);
        }
        return lifecycle;
    };
    /**
     * The function will return a list of all available lifecycles for the currently logged in user's tenant.
     * @return {[type]}         An array of strings with the names of the lifecycles
     */
    api.getLifecycles = function(options, req, res, session) {
        var lcApi = require('lifecycle').api;
        var server = require('store').server;
        var lifecycle = null;
        var user = server.current(session);
        var lifecycles = lcApi.getLifecycleList(user.tenantId);
        return lifecycles;
    };

    /**
     * The function changes the state of a set of check items sent as an array with each element been  { index: number , checked:true}
     * @return {[type]}         A boolean value indicating the success of the operation
     */
    api.checkItems = function(options, req, res, session) {
        var success = false;
        validateOptions(options, req, res, session);
        var assetApi = require('rxt').asset;
        var am = assetApi.createUserAssetManager(session, options.type);
        var asset = getAsset(options, am);
        validateAsset(asset, options);
        var state = this.getState(options, req, res, session);
        success = changeCheckitemState(options, asset, am, state, true);
        return success;
    };
}(api));<|endoftext|>


<commit_before>Seed({
		name : "Mold.Lib.VDom.ValueNode",
		dna : "static"
	},
	function(){

		return function ValueNode(config){
			//°include Mold.Lib.VDom.ProtoNode

			this.type = VALUE_NODE;
			this.domPointer = false;

			this.clone = function(){
				var newNode =  new ValueNode({
					name : this.name,
					data : this.data,
					isString : this.isString
				});

				return newNode;
			}

			this.render = function(){
				if(!this.domPointer){
					this.domPointer = _doc.createTextNode(this.data);
				}else{
					this.domPointer.nodeValue = this.data;
				}
				this.state = STATE_NO_CHANGES;
				return this.domPointer;
			}

			this.renderString = function(){
				return this.data;
			}

			this.setDataAndRender = function(data){
				this.setData(data);

				if(this.isString){
					//console.log("this is string", this.parent)
					if(this.parent.type === ATTRIBUTE_NODE){
						this.parent.renderAttribute();
					}else{
						this.parent.render();
					}
				}else{
					this.render();
					this.domPointer.nodeValue = this.data;
				}
				//this.parent.render();

			}
		}
	}
)<commit_msg>check if data is set<commit_after>Seed({
		name : "Mold.Lib.VDom.ValueNode",
		dna : "static"
	},
	function(){

		return function ValueNode(config){
			//°include Mold.Lib.VDom.ProtoNode

			this.type = VALUE_NODE;
			this.domPointer = false;

			this.clone = function(){
				var newNode =  new ValueNode({
					name : this.name,
					data : this.data,
					isString : this.isString
				});

				return newNode;
			}

			this.render = function(){
				if(!this.domPointer){
					this.domPointer = _doc.createTextNode(this.data);
				}else{
					this.domPointer.nodeValue = this.data;
				}
				this.state = STATE_NO_CHANGES;
				return this.domPointer;
			}

			this.renderString = function(){
				return this.data;
			}

			this.setDataAndRender = function(data){
				if(data){
					this.setData(data);
				}
				if(this.isString){
					//console.log("this is string", this.parent)
					if(this.parent.type === ATTRIBUTE_NODE){
						this.parent.renderAttribute();
					}else{
						this.parent.render();
					}
				}else{
					this.render();
					this.domPointer.nodeValue = this.data;
				}
				//this.parent.render();

			}
		}
	}
)<|endoftext|>


<commit_before>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Tool = this.Tool = Base.extend({
	/** @lends Tool# */

	beans: true,

	/**
	 * Initializes the tool's settings, so a new tool can be assigned to it
	 */
	initialize: function(handlers, scope) {
		this._scope = scope;
		this._firstMove = true;
		this._count = 0;
		this._downCount = 0;
		for (var i in handlers)
			this[i] = handlers[i];
	},

	/**
	 * The fixed time delay between each call to the {@link #onMouseDrag}
	 * event. Setting this to an interval means the {@link #onMouseDrag}
	 * event is called repeatedly after the initial {@link #onMouseDown}
	 * until the user releases the mouse.
	 *
	 * @return the interval time in milliseconds
	 */
	eventInterval: null,

	/**
	 * The minimum distance the mouse has to drag before firing the onMouseDrag
	 * event, since the last onMouseDrag event.
	 */
	getMinDistance: function() {
		return this._minDistance;
	},

	setMinDistance: function(minDistance) {
		this._minDistance = minDistance;
		if (this._minDistance != null && this._maxDistance != null
				&& this._minDistance > this._maxDistance) {
			this._maxDistance = this._minDistance;
		}
	},

	getMaxDistance: function() {
		return this._maxDistance;
	},

	setMaxDistance: function(maxDistance) {
		this._maxDistance = maxDistance;
		if (this._minDistance != null && this._maxDistance != null
				&& this._maxDistance < this._minDistance) {
			this._minDistance = maxDistance;
		}
	},

	getFixedDistance: function() {
		return this._minDistance == this._maxDistance
			? this._minDistance : null;
	},

	setFixedDistance: function(distance) {
		this._minDistance = distance;
		this._maxDistance = distance;
	},

	updateEvent: function(type, pt, minDistance, maxDistance, start,
			needsChange, matchMaxDistance) {
		if (!start) {
			if (minDistance != null || maxDistance != null) {
				var minDist = minDistance != null ? minDistance : 0;
				var vector = pt.subtract(this._point);
				var distance = vector.getLength();
				if (distance < minDist)
					return false;
				// Produce a new point on the way to pt if pt is further away
				// than maxDistance
				var maxDist = maxDistance != null ? maxDistance : 0;
				if (maxDist != 0) {
					if (distance > maxDist) {
						pt = this._point.add(vector.normalize(maxDist));
					} else if (matchMaxDistance) {
						return false;
					}
				}
			}
			if (needsChange && pt.equals(this._point))
				return false;
		}
		this._lastPoint = this._point;
		this._point = pt;
		switch (type) {
		case 'mousedown':
			this._lastPoint = this._downPoint;
			this._downPoint = this._point;
			this._downCount++;
			break;
		case 'mouseup':
			// Mouse up events return the down point for last point, so delta is
			// spanning over the whole drag.
			this._lastPoint = this._downPoint;
			break;
		}
		this._count = start ? 0 : this._count + 1;
		return true;
	},

	onHandleEvent: function(type, pt, event) {
		paper = this._scope;
		switch (type) {
		case 'mousedown':
			this.updateEvent(type, pt, null, null, true, false, false);
			if (this.onMouseDown)
				this.onMouseDown(new ToolEvent(this, type, event));
			break;
		case 'mousedrag':
			// In order for idleInterval drag events to work, we need to not
			// check the first call for a change of position. Subsequent calls
			// required by min/maxDistance functionality will require it,
			// otherwise this might loop endlessly.
			var needsChange = false,
			// If the mouse is moving faster than maxDistance, do not produce
			// events for what is left after the first event is generated in
			// case it is shorter than maxDistance, as this would produce weird
			// results. matchMaxDistance controls this.
				matchMaxDistance = false;
			while (this.updateEvent(type, pt, this.minDistance,
					this.maxDistance, false, needsChange, matchMaxDistance)) {
				if (this.onMouseDrag)
					this.onMouseDrag(new ToolEvent(this, type, event));
				needsChange = true;
				matchMaxDistance = true;
			}
			break;
		case 'mouseup':
			// If the last mouse drag happened in a different place, call mouse
			// drag first, then mouse up.
			if ((this._point.x != pt.x || this._point.y != pt.y)
					&& this.updateEvent('mousedrag', pt, this.minDistance,
							this.maxDistance, false, false, false)) {
				if (this.onMouseDrag)
					this.onMouseDrag(new ToolEvent(this, type, event));
			}
			this.updateEvent(type, pt, null, this.maxDistance, false,
					false, false);
			if (this.onMouseUp)
				this.onMouseUp(new ToolEvent(this, type, event));
			// Start with new values for 'mousemove'
			this.updateEvent(type, pt, null, null, true, false, false);
			this._firstMove = true;
			break;
		case 'mousemove':
			while (this.updateEvent(type, pt, this.minDistance,
					this.maxDistance, this._firstMove, true, false)) {
				if (this.onMouseMove)
					this.onMouseMove(new ToolEvent(this, type, event));
				this._firstMove = false;
			}
			break;
		}
	}
});
<commit_msg>Add Tool documentation.<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * All rights reserved.
 */

var Tool = this.Tool = Base.extend({
	/** @lends Tool# */

	beans: true,

	// DOCS: rewrite Tool constructor explanation
	/**
	 * Initializes the tool's settings, so a new tool can be assigned to it
	 *
	 * @constructs Tool
	 *
	 * @class The Tool object refers to a script that the user can interact with
	 * by using the mouse and keyboard and can be accessed through the global
	 * {@code tool} variable. All its properties are also available in the paper
	 * scope.
	 *
	 * The global {@code tool} variable only exists in scripts that contain mouse
	 * handler functions ({@link #onMouseMove}, {@link #onMouseDown},
	 * {@link #onMouseDrag}, {@link #onMouseUp}) or a keyboard handler
	 * function ({@link #onKeyDown}, {@link #onKeyUp}).
	 *
	 * @example
	 * var path;
	 *
	 * // Only execute onMouseDrag when the mouse
	 * // has moved at least 10 points:
	 * tool.distanceThreshold = 10;
	 *
	 * function onMouseDown(event) {
	 * 	// Create a new path every time the mouse is clicked
	 * 	path = new Path();
	 * 	path.strokeColor = 'black';
	 * }
	 *
	 * function onMouseDrag(event) {
	 * 	// Add a point to the path every time the mouse is dragged
	 * 	path.lineTo(event.point);
	 * }
	 */
	initialize: function(handlers, scope) {
		this._scope = scope;
		this._firstMove = true;
		this._count = 0;
		this._downCount = 0;
		for (var i in handlers)
			this[i] = handlers[i];
	},

	/**
	 * The fixed time delay in milliseconds between each call to the
	 * {@link #onMouseDrag} event. Setting this to an interval means the
	 * {@link #onMouseDrag} event is called repeatedly after the initial
	 * {@link #onMouseDown} until the user releases the mouse.
	 *
	 * @type number
	 */
	eventInterval: null,

	/**
	 * The minimum distance the mouse has to drag before firing the onMouseDrag
	 * event, since the last onMouseDrag event.
	 *
	 * @type number
	 * @bean
	 */
	getMinDistance: function() {
		return this._minDistance;
	},

	setMinDistance: function(minDistance) {
		this._minDistance = minDistance;
		if (this._minDistance != null && this._maxDistance != null
				&& this._minDistance > this._maxDistance) {
			this._maxDistance = this._minDistance;
		}
	},

	/**
	 * The maximum distance the mouse has to drag before firing the onMouseDrag
	 * event, since the last onMouseDrag event.
	 *
	 * @type number
	 * @bean
	 */
	getMaxDistance: function() {
		return this._maxDistance;
	},

	setMaxDistance: function(maxDistance) {
		this._maxDistance = maxDistance;
		if (this._minDistance != null && this._maxDistance != null
				&& this._maxDistance < this._minDistance) {
			this._minDistance = maxDistance;
		}
	},

	// DOCS: document Tool#fixedDistance
	/**
	 * @type number
	 * @bean
	 */
	getFixedDistance: function() {
		return this._minDistance == this._maxDistance
			? this._minDistance : null;
	},

	setFixedDistance: function(distance) {
		this._minDistance = distance;
		this._maxDistance = distance;
	},

	/**
	 * {@grouptitle Mouse Event Handlers}
	 *
	 * The function to be called when the mouse button is pushed down. The
	 * function receives a {@link ToolEvent} object which contains information
	 * about the mouse event.
	 *
	 * @example
	 * function onMouseDown(event) {
	 * 	// the position of the mouse in project coordinates:
	 * 	console.log(event.point);
	 * }
	 *
	 * @name Tool#onMouseDown
	 * @property
	 * @type function
	 */

	/**
	 * The function to be called when the mouse position changes while the mouse
	 * is being dragged. The function receives a {@link ToolEvent} object which
	 * contains information about the mouse event.
	 *
	 * This function can also be called periodically while the mouse doesn't
	 * move by setting the {@link #eventInterval}
	 *
	 * @example
	 * function onMouseDrag(event) {
	 * 	// the position of the mouse in project coordinates
	 * 	console.log(event.point);
	 * }
	 *
	 * @name Tool#onMouseDrag
	 * @property
	 * @type function
	 */

	/**
	 * The function to be called when the tool is selected and the mouse moves
	 * within the document. The function receives a {@link ToolEvent} object
	 * which contains information about the mouse event.
	 *
	 * @example
	 * function onMouseMove(event) {
	 * 	// the position of the mouse in project coordinates
	 * 	console.log(event.point);
	 * }
	 * @name Tool#onMouseMove
	 * @property
	 * @type function
	 */

	/**
	 * The function to be called when the mouse button is released. The function
	 * receives a {@link ToolEvent} object which contains information about the
	 * mouse event.
	 *
	 * @example
	 * function onMouseUp(event) {
	 * 	// the position of the mouse in project coordinates
	 * 	console.log(event.point);
	 * }
	 * @name Tool#onMouseUp
	 * @property
	 * @type function
	 */

	// DOCS: document Tool#onKeyDown
	/**
	 * @name Tool#onKeyDown
	 * @property
	 * @type function
	 */

	// DOCS: document Tool#onKeyUp
	/**
	 * @name Tool#onKeyUp
	 * @property
	 * @type function
	 */

	updateEvent: function(type, pt, minDistance, maxDistance, start,
			needsChange, matchMaxDistance) {
		if (!start) {
			if (minDistance != null || maxDistance != null) {
				var minDist = minDistance != null ? minDistance : 0;
				var vector = pt.subtract(this._point);
				var distance = vector.getLength();
				if (distance < minDist)
					return false;
				// Produce a new point on the way to pt if pt is further away
				// than maxDistance
				var maxDist = maxDistance != null ? maxDistance : 0;
				if (maxDist != 0) {
					if (distance > maxDist) {
						pt = this._point.add(vector.normalize(maxDist));
					} else if (matchMaxDistance) {
						return false;
					}
				}
			}
			if (needsChange && pt.equals(this._point))
				return false;
		}
		this._lastPoint = this._point;
		this._point = pt;
		switch (type) {
		case 'mousedown':
			this._lastPoint = this._downPoint;
			this._downPoint = this._point;
			this._downCount++;
			break;
		case 'mouseup':
			// Mouse up events return the down point for last point, so delta is
			// spanning over the whole drag.
			this._lastPoint = this._downPoint;
			break;
		}
		this._count = start ? 0 : this._count + 1;
		return true;
	},

	onHandleEvent: function(type, pt, event) {
		paper = this._scope;
		switch (type) {
		case 'mousedown':
			this.updateEvent(type, pt, null, null, true, false, false);
			if (this.onMouseDown)
				this.onMouseDown(new ToolEvent(this, type, event));
			break;
		case 'mousedrag':
			// In order for idleInterval drag events to work, we need to not
			// check the first call for a change of position. Subsequent calls
			// required by min/maxDistance functionality will require it,
			// otherwise this might loop endlessly.
			var needsChange = false,
			// If the mouse is moving faster than maxDistance, do not produce
			// events for what is left after the first event is generated in
			// case it is shorter than maxDistance, as this would produce weird
			// results. matchMaxDistance controls this.
				matchMaxDistance = false;
			while (this.updateEvent(type, pt, this.minDistance,
					this.maxDistance, false, needsChange, matchMaxDistance)) {
				if (this.onMouseDrag)
					this.onMouseDrag(new ToolEvent(this, type, event));
				needsChange = true;
				matchMaxDistance = true;
			}
			break;
		case 'mouseup':
			// If the last mouse drag happened in a different place, call mouse
			// drag first, then mouse up.
			if ((this._point.x != pt.x || this._point.y != pt.y)
					&& this.updateEvent('mousedrag', pt, this.minDistance,
							this.maxDistance, false, false, false)) {
				if (this.onMouseDrag)
					this.onMouseDrag(new ToolEvent(this, type, event));
			}
			this.updateEvent(type, pt, null, this.maxDistance, false,
					false, false);
			if (this.onMouseUp)
				this.onMouseUp(new ToolEvent(this, type, event));
			// Start with new values for 'mousemove'
			this.updateEvent(type, pt, null, null, true, false, false);
			this._firstMove = true;
			break;
		case 'mousemove':
			while (this.updateEvent(type, pt, this.minDistance,
					this.maxDistance, this._firstMove, true, false)) {
				if (this.onMouseMove)
					this.onMouseMove(new ToolEvent(this, type, event));
				this._firstMove = false;
			}
			break;
		}
	}
});
<|endoftext|>


<commit_before>goog.provide('ol.reproj.Triangulation');

goog.require('goog.asserts');
goog.require('goog.math');
goog.require('ol.extent');
goog.require('ol.proj');

/**
 * Single triangle; consists of 3 source points and 3 target points.
 *
 * @typedef {{source: Array.<ol.Coordinate>,
 *            target: Array.<ol.Coordinate>}}
 */
ol.reproj.Triangle;

/**
 * @classdesc
 * Class containing triangulation of the given target extent.
 * Used for determining source data and the reprojection itself.
 *
 * @param {ol.proj.Projection} sourceProj Source projection.
 * @param {ol.proj.Projection} targetProj Target projection.
 * @param {ol.Extent} targetExtent Target extent to triangulate.
 * @param {ol.Extent} maxSourceExtent Maximal source extent that can be used.
 * @param {number} errorThreshold Acceptable error (in source units).
 * @constructor
 */
ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent,
    maxSourceExtent, errorThreshold) {

  /**
   * @type {ol.proj.Projection}
   * @private
   */
  this.sourceProj_ = sourceProj;

  /**
   * @type {ol.proj.Projection}
   * @private
   */
  this.targetProj_ = targetProj;

  /** @type {!Object.<string, ol.Coordinate>} */
  var transformInvCache = {};
  var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_);

  /**
   * @param {ol.Coordinate} c
   * @return {ol.Coordinate}
   * @private
   */
  this.transformInv_ = function(c) {
    var key = c[0] + '/' + c[1];
    return transformInvCache[key] || (transformInvCache[key] = transformInv(c));
  };

  /**
   * @type {ol.Extent}
   * @private
   */
  this.maxSourceExtent_ = maxSourceExtent;

  /**
   * @type {number}
   * @private
   */
  this.errorThresholdSquared_ = errorThreshold * errorThreshold;

  /**
   * @type {Array.<ol.reproj.Triangle>}
   * @private
   */
  this.triangles_ = [];

  /**
   * Indicates that source coordinates have to be shifted during reprojection.
   * This is needed when the triangulation crosses
   * edge of the source projection (dateline).
   * @type {boolean}
   * @private
   */
  this.wrapsXInSource_ = false;

  /**
   * @type {boolean}
   * @private
   */
  this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
      !!maxSourceExtent &&
      !!this.sourceProj_.getExtent() &&
      (ol.extent.getWidth(maxSourceExtent) ==
       ol.extent.getWidth(this.sourceProj_.getExtent()));

  /**
   * @type {?number}
   * @private
   */
  this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
      ol.extent.getWidth(this.sourceProj_.getExtent()) : null;

  /**
   * @type {?number}
   * @private
   */
  this.targetWorldWidth_ = this.targetProj_.getExtent() ?
      ol.extent.getWidth(this.targetProj_.getExtent()) : null;

  var destinationTopLeft = ol.extent.getTopLeft(targetExtent);
  var destinationTopRight = ol.extent.getTopRight(targetExtent);
  var destinationBottomRight = ol.extent.getBottomRight(targetExtent);
  var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent);
  var sourceTopLeft = this.transformInv_(destinationTopLeft);
  var sourceTopRight = this.transformInv_(destinationTopRight);
  var sourceBottomRight = this.transformInv_(destinationBottomRight);
  var sourceBottomLeft = this.transformInv_(destinationBottomLeft);

  this.addQuad_(
      destinationTopLeft, destinationTopRight,
      destinationBottomRight, destinationBottomLeft,
      sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
      ol.RASTER_REPROJECTION_MAX_SUBDIVISION);

  if (this.wrapsXInSource_) {
    // Fix coordinates (ol.proj returns wrapped coordinates, "unwrap" here).
    // This significantly simplifies the rest of the reprojection process.

    goog.asserts.assert(this.sourceWorldWidth_ !== null);
    var leftBound = Infinity;
    this.triangles_.forEach(function(triangle, i, arr) {
      leftBound = Math.min(leftBound,
          triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
    });

    // Shift triangles to be as close to `leftBound` as possible
    // (if the distance is more than `worldWidth / 2` it can be closer.
    this.triangles_.forEach(function(triangle) {
      if (Math.max(triangle.source[0][0], triangle.source[1][0],
          triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
        var newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
                           [triangle.source[1][0], triangle.source[1][1]],
                           [triangle.source[2][0], triangle.source[2][1]]];
        if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[0][0] -= this.sourceWorldWidth_;
        }
        if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[1][0] -= this.sourceWorldWidth_;
        }
        if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[2][0] -= this.sourceWorldWidth_;
        }

        // Rarely (if the extent contains both the dateline and prime meridian)
        // the shift can in turn break some triangles.
        // Detect this here and don't shift in such cases.
        var minX = Math.min(
            newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
        var maxX = Math.max(
            newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
        if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
          triangle.source = newTriangle;
        }
      }
    }, this);
  }

  transformInvCache = {};
};

/**
 * Adds triangle to the triangulation.
 * @param {ol.Coordinate} a
 * @param {ol.Coordinate} b
 * @param {ol.Coordinate} c
 * @param {ol.Coordinate} aSrc
 * @param {ol.Coordinate} bSrc
 * @param {ol.Coordinate} cSrc
 * @private
 */
ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c,
    aSrc, bSrc, cSrc) {
  this.triangles_.push({
    source: [aSrc, bSrc, cSrc],
    target: [a, b, c]
  });
};

/**
 * Adds quad (points in clock-wise order) to the triangulation
 * (and reprojects the vertices) if valid.
 * Performs quad subdivision if needed to increase precision.
 *
 * @param {ol.Coordinate} a
 * @param {ol.Coordinate} b
 * @param {ol.Coordinate} c
 * @param {ol.Coordinate} d
 * @param {ol.Coordinate} aSrc
 * @param {ol.Coordinate} bSrc
 * @param {ol.Coordinate} cSrc
 * @param {ol.Coordinate} dSrc
 * @param {number} maxSubdivision Maximal allowed subdivision of the quad.
 * @private
 */
ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d,
    aSrc, bSrc, cSrc, dSrc, maxSubdivision) {

  var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]);
  var sourceCoverageX = this.sourceWorldWidth_ ?
      ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;

  // when the quad is wrapped in the source projection
  // it covers most of the projection extent, but not fully
  var wrapsX = this.sourceProj_.canWrapX() &&
               sourceCoverageX > 0.5 && sourceCoverageX < 1;

  var needsSubdivision = false;

  if (maxSubdivision > 0) {
    if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
      var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]);
      var targetCoverageX =
          ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_;
      needsSubdivision |=
          targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
    }
    if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
      needsSubdivision |=
          sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
    }
  }

  if (!needsSubdivision && this.maxSourceExtent_) {
    if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) {
      // whole quad outside source projection extent -> ignore
      return;
    }
  }

  if (!needsSubdivision) {
    if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
        !isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
        !isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
        !isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
      if (maxSubdivision > 0) {
        needsSubdivision = true;
      } else {
        return;
      }
    }
  }

  if (maxSubdivision > 0) {
    if (!needsSubdivision) {
      var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
      var centerSrc = this.transformInv_(center);

      var dx;
      if (wrapsX) {
        goog.asserts.assert(this.sourceWorldWidth_);
        var centerSrcEstimX =
            (goog.math.modulo(aSrc[0], this.sourceWorldWidth_) +
             goog.math.modulo(cSrc[0], this.sourceWorldWidth_)) / 2;
        dx = centerSrcEstimX -
            goog.math.modulo(centerSrc[0], this.sourceWorldWidth_);
      } else {
        dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
      }
      var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
      var centerSrcErrorSquared = dx * dx + dy * dy;
      needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
    }
    if (needsSubdivision) {
      if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
        // split horizontally (top & bottom)
        var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
        var bcSrc = this.transformInv_(bc);
        var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
        var daSrc = this.transformInv_(da);

        this.addQuad_(
            a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
        this.addQuad_(
            da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
      } else {
        // split vertically (left & right)
        var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
        var abSrc = this.transformInv_(ab);
        var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
        var cdSrc = this.transformInv_(cd);

        this.addQuad_(
            a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
        this.addQuad_(
            ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
      }
      return;
    }
  }

  if (wrapsX) {
    if (!this.canWrapXInSource_) {
      return;
    }
    this.wrapsXInSource_ = true;
  }

  this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
  this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
};

/**
 * Calculates extent of the 'source' coordinates from all the triangles.
 *
 * @return {ol.Extent} Calculated extent.
 */
ol.reproj.Triangulation.prototype.calculateSourceExtent = function() {
  var extent = ol.extent.createEmpty();

  this.triangles_.forEach(function(triangle, i, arr) {
    var src = triangle.source;
    ol.extent.extendCoordinate(extent, src[0]);
    ol.extent.extendCoordinate(extent, src[1]);
    ol.extent.extendCoordinate(extent, src[2]);
  });

  return extent;
};

/**
 * @return {Array.<ol.reproj.Triangle>} Array of the calculated triangles.
 */
ol.reproj.Triangulation.prototype.getTriangles = function() {
  return this.triangles_;
};
<commit_msg>Avoid assignment in return statement<commit_after>goog.provide('ol.reproj.Triangulation');

goog.require('goog.asserts');
goog.require('goog.math');
goog.require('ol.extent');
goog.require('ol.proj');

/**
 * Single triangle; consists of 3 source points and 3 target points.
 *
 * @typedef {{source: Array.<ol.Coordinate>,
 *            target: Array.<ol.Coordinate>}}
 */
ol.reproj.Triangle;

/**
 * @classdesc
 * Class containing triangulation of the given target extent.
 * Used for determining source data and the reprojection itself.
 *
 * @param {ol.proj.Projection} sourceProj Source projection.
 * @param {ol.proj.Projection} targetProj Target projection.
 * @param {ol.Extent} targetExtent Target extent to triangulate.
 * @param {ol.Extent} maxSourceExtent Maximal source extent that can be used.
 * @param {number} errorThreshold Acceptable error (in source units).
 * @constructor
 */
ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent,
    maxSourceExtent, errorThreshold) {

  /**
   * @type {ol.proj.Projection}
   * @private
   */
  this.sourceProj_ = sourceProj;

  /**
   * @type {ol.proj.Projection}
   * @private
   */
  this.targetProj_ = targetProj;

  /** @type {!Object.<string, ol.Coordinate>} */
  var transformInvCache = {};
  var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_);

  /**
   * @param {ol.Coordinate} c
   * @return {ol.Coordinate}
   * @private
   */
  this.transformInv_ = function(c) {
    var key = c[0] + '/' + c[1];
    if (!transformInvCache[key]) {
      transformInvCache[key] = transformInv(c);
    }
    return transformInvCache[key];
  };

  /**
   * @type {ol.Extent}
   * @private
   */
  this.maxSourceExtent_ = maxSourceExtent;

  /**
   * @type {number}
   * @private
   */
  this.errorThresholdSquared_ = errorThreshold * errorThreshold;

  /**
   * @type {Array.<ol.reproj.Triangle>}
   * @private
   */
  this.triangles_ = [];

  /**
   * Indicates that source coordinates have to be shifted during reprojection.
   * This is needed when the triangulation crosses
   * edge of the source projection (dateline).
   * @type {boolean}
   * @private
   */
  this.wrapsXInSource_ = false;

  /**
   * @type {boolean}
   * @private
   */
  this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
      !!maxSourceExtent &&
      !!this.sourceProj_.getExtent() &&
      (ol.extent.getWidth(maxSourceExtent) ==
       ol.extent.getWidth(this.sourceProj_.getExtent()));

  /**
   * @type {?number}
   * @private
   */
  this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
      ol.extent.getWidth(this.sourceProj_.getExtent()) : null;

  /**
   * @type {?number}
   * @private
   */
  this.targetWorldWidth_ = this.targetProj_.getExtent() ?
      ol.extent.getWidth(this.targetProj_.getExtent()) : null;

  var destinationTopLeft = ol.extent.getTopLeft(targetExtent);
  var destinationTopRight = ol.extent.getTopRight(targetExtent);
  var destinationBottomRight = ol.extent.getBottomRight(targetExtent);
  var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent);
  var sourceTopLeft = this.transformInv_(destinationTopLeft);
  var sourceTopRight = this.transformInv_(destinationTopRight);
  var sourceBottomRight = this.transformInv_(destinationBottomRight);
  var sourceBottomLeft = this.transformInv_(destinationBottomLeft);

  this.addQuad_(
      destinationTopLeft, destinationTopRight,
      destinationBottomRight, destinationBottomLeft,
      sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
      ol.RASTER_REPROJECTION_MAX_SUBDIVISION);

  if (this.wrapsXInSource_) {
    // Fix coordinates (ol.proj returns wrapped coordinates, "unwrap" here).
    // This significantly simplifies the rest of the reprojection process.

    goog.asserts.assert(this.sourceWorldWidth_ !== null);
    var leftBound = Infinity;
    this.triangles_.forEach(function(triangle, i, arr) {
      leftBound = Math.min(leftBound,
          triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
    });

    // Shift triangles to be as close to `leftBound` as possible
    // (if the distance is more than `worldWidth / 2` it can be closer.
    this.triangles_.forEach(function(triangle) {
      if (Math.max(triangle.source[0][0], triangle.source[1][0],
          triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
        var newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
                           [triangle.source[1][0], triangle.source[1][1]],
                           [triangle.source[2][0], triangle.source[2][1]]];
        if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[0][0] -= this.sourceWorldWidth_;
        }
        if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[1][0] -= this.sourceWorldWidth_;
        }
        if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
          newTriangle[2][0] -= this.sourceWorldWidth_;
        }

        // Rarely (if the extent contains both the dateline and prime meridian)
        // the shift can in turn break some triangles.
        // Detect this here and don't shift in such cases.
        var minX = Math.min(
            newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
        var maxX = Math.max(
            newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
        if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
          triangle.source = newTriangle;
        }
      }
    }, this);
  }

  transformInvCache = {};
};

/**
 * Adds triangle to the triangulation.
 * @param {ol.Coordinate} a
 * @param {ol.Coordinate} b
 * @param {ol.Coordinate} c
 * @param {ol.Coordinate} aSrc
 * @param {ol.Coordinate} bSrc
 * @param {ol.Coordinate} cSrc
 * @private
 */
ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c,
    aSrc, bSrc, cSrc) {
  this.triangles_.push({
    source: [aSrc, bSrc, cSrc],
    target: [a, b, c]
  });
};

/**
 * Adds quad (points in clock-wise order) to the triangulation
 * (and reprojects the vertices) if valid.
 * Performs quad subdivision if needed to increase precision.
 *
 * @param {ol.Coordinate} a
 * @param {ol.Coordinate} b
 * @param {ol.Coordinate} c
 * @param {ol.Coordinate} d
 * @param {ol.Coordinate} aSrc
 * @param {ol.Coordinate} bSrc
 * @param {ol.Coordinate} cSrc
 * @param {ol.Coordinate} dSrc
 * @param {number} maxSubdivision Maximal allowed subdivision of the quad.
 * @private
 */
ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d,
    aSrc, bSrc, cSrc, dSrc, maxSubdivision) {

  var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]);
  var sourceCoverageX = this.sourceWorldWidth_ ?
      ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;

  // when the quad is wrapped in the source projection
  // it covers most of the projection extent, but not fully
  var wrapsX = this.sourceProj_.canWrapX() &&
               sourceCoverageX > 0.5 && sourceCoverageX < 1;

  var needsSubdivision = false;

  if (maxSubdivision > 0) {
    if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
      var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]);
      var targetCoverageX =
          ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_;
      needsSubdivision |=
          targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
    }
    if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
      needsSubdivision |=
          sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
    }
  }

  if (!needsSubdivision && this.maxSourceExtent_) {
    if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) {
      // whole quad outside source projection extent -> ignore
      return;
    }
  }

  if (!needsSubdivision) {
    if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
        !isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
        !isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
        !isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
      if (maxSubdivision > 0) {
        needsSubdivision = true;
      } else {
        return;
      }
    }
  }

  if (maxSubdivision > 0) {
    if (!needsSubdivision) {
      var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
      var centerSrc = this.transformInv_(center);

      var dx;
      if (wrapsX) {
        goog.asserts.assert(this.sourceWorldWidth_);
        var centerSrcEstimX =
            (goog.math.modulo(aSrc[0], this.sourceWorldWidth_) +
             goog.math.modulo(cSrc[0], this.sourceWorldWidth_)) / 2;
        dx = centerSrcEstimX -
            goog.math.modulo(centerSrc[0], this.sourceWorldWidth_);
      } else {
        dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
      }
      var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
      var centerSrcErrorSquared = dx * dx + dy * dy;
      needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
    }
    if (needsSubdivision) {
      if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
        // split horizontally (top & bottom)
        var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
        var bcSrc = this.transformInv_(bc);
        var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
        var daSrc = this.transformInv_(da);

        this.addQuad_(
            a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
        this.addQuad_(
            da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
      } else {
        // split vertically (left & right)
        var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
        var abSrc = this.transformInv_(ab);
        var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
        var cdSrc = this.transformInv_(cd);

        this.addQuad_(
            a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
        this.addQuad_(
            ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
      }
      return;
    }
  }

  if (wrapsX) {
    if (!this.canWrapXInSource_) {
      return;
    }
    this.wrapsXInSource_ = true;
  }

  this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
  this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
};

/**
 * Calculates extent of the 'source' coordinates from all the triangles.
 *
 * @return {ol.Extent} Calculated extent.
 */
ol.reproj.Triangulation.prototype.calculateSourceExtent = function() {
  var extent = ol.extent.createEmpty();

  this.triangles_.forEach(function(triangle, i, arr) {
    var src = triangle.source;
    ol.extent.extendCoordinate(extent, src[0]);
    ol.extent.extendCoordinate(extent, src[1]);
    ol.extent.extendCoordinate(extent, src[2]);
  });

  return extent;
};

/**
 * @return {Array.<ol.reproj.Triangle>} Array of the calculated triangles.
 */
ol.reproj.Triangulation.prototype.getTriangles = function() {
  return this.triangles_;
};
<|endoftext|>


<commit_before>
module.exports = (function(){

  // Flag bad practises
  'use strict';

  // ------------------------------------
  // Basic Setup
  // ------------------------------------

  /**
   * @class TreeNode
   * @classdesc Represents a node in the tree.
   * @constructor
   * @param {object} data - that is to be stored in a node
   */
  function TreeNode(data){

    /**
     * Represents the parent node
     *
     * @property _parentNode
     * @type {object}
     * @default "null"
     */
    this._parentNode = null;

    /**
     * Represents the child nodes
     *
     * @property _childNodes
     * @type {array}
     * @default "[]"
     */
    this._childNodes = [];

    /**
     * Represents the data node has
     *
     * @property _data
     * @type {object}
     * @default "null"
     */
    this._data = data;

    /**
     * Depth of the node represents level in hierarchy
     *
     * @property _depth
     * @type {number}
     * @default -1
     */
    this._depth = -1;

  }

  // ------------------------------------
  // Getters and Setters
  // ------------------------------------

  /**
   * Returns a parent node of current node
   *
   * @method parentNode
   * @memberof TreeNode
   * @instance
   * @return {TreeNode} - parent of current node
   */
  TreeNode.prototype.parentNode = function(){
    return this._parentNode;
  };

  /**
   * Returns an array of child nodes
   *
   * @method childNodes
   * @memberof TreeNode
   * @instance
   * @return {array} - array of child nodes
   */
  TreeNode.prototype.childNodes = function(){
    return this._childNodes;
  };

  /**
   * Sets or gets the data belonging to this node. Data is what user sets using `insert` and `insertTo` methods.
   *
   * @method data
   * @memberof TreeNode
   * @instance
   * @param {object | array | string | number | null} _data - data which is to be stored
   * @return {object | array | string | number | null} - data belonging to this node
   */
  TreeNode.prototype.data = function(_data){
    if(arguments.length > 0){
      this._data = data;
    } else {
      return this._data;
    }
  };

  /**
   * Depth of the node. Indicates the level at which node lies in a tree.
   *
   * @method depth
   * @memberof TreeNode
   * @instance
   * @return {number} - depth of node
   */
  TreeNode.prototype.depth = function(){
    return this._depth;
  };

  // ------------------------------------
  // Methods
  // ------------------------------------

  /**
   * Indicates whether this node matches the specified criteria. It triggers a callback criteria function that returns something.
   *
   * @method matchCriteria
   * @memberof TreeNode
   * @instance
   * @param {function} callback - Callback function that specifies some criteria. It receives {@link TreeNode#_data} in parameter and expects different values in different scenarios.
   * `matchCriteria` is used by following functions and expects:
   * 1. {@link Tree#searchBFS} - {boolean} in return indicating whether given node satisfies criteria.
   * 2. {@link Tree#searchDFS} - {boolean} in return indicating whether given node satisfies criteria.
   * 3. {@link Tree#export} - {object} in return indicating formatted data object.
   */
  TreeNode.prototype.matchCriteria = function(criteria){
    return criteria(this._data);
  };

  /**
   * get sibling nodes.
   *
   * @method siblings
   * @memberof TreeNode
   * @instance
   * @return {array} - array of instances of {@link TreeNode}
   */
  TreeNode.prototype.siblings = function(){
    var thiss = this;
    return !this._parentNode ? [] : this._parentNode._childNodes.filter(function(_child){
      return _child !== thiss;
    });
  };

  /**
   * Finds distance of node from root node
   *
   * @method distanceToRoot
   * @memberof TreeNode
   * @instance
   * @return {array} - array of instances of {@link TreeNode}
   */
  TreeNode.prototype.distanceToRoot = function(){

    // Initialize Distance and Node
    var distance = 0,
        node = this;

    // Loop Over Ancestors
    while(node.parentNode()){
      distance++;
      node = node.parentNode();
    }

    // Return
    return distance;

  };

  /**
   * Gets an array of all ancestor nodes including current node
   *
   * @method getAncestry
   * @memberof TreeNode
   * @instance
   * @return {Array} - array of ancestor nodes
   */
  TreeNode.prototype.getAncestry = function(){

    // Initialize empty array and node
    var ancestors = [this],
        node = this;

    // Loop over ancestors and add them in array
    while(node.parentNode()){
      ancestors.push(node.parentNode());
      node = node.parentNode();
    }

    // Return
    return ancestors;

  };

  /**
   * Exports the node data in format specified. It maintains herirachy by adding
   * additional "children" property to returned value of `criteria` callback.
   *
   * @method export
   * @memberof TreeNode
   * @instance
   * @param {TreeNode~criteria} criteria - Callback function that receives data in parameter
   * and MUST return a formatted data that has to be exported. A new property "children" is added to object returned
   * that maintains the heirarchy of nodes.
   * @return {object} - {@link TreeNode}.
   * @example
   *
   * var rootNode = tree.insert({
   *   key: '#apple',
   *   value: { name: 'Apple', color: 'Red'}
   * });
   *
   * tree.insert({
   *   key: '#greenapple',
   *   value: { name: 'Green Apple', color: 'Green'}
   * });
   *
   * tree.insertToNode(rootNode,  {
   *  key: '#someanotherapple',
   *  value: { name: 'Some Apple', color: 'Some Color' }
   * });
   *
   * // Export the tree
   * var exported = rootNode.export(function(data){
   *  return { name: data.value.name };
   * });
   *
   * // Result in `exported`
   * {
   * "name": "Apple",
   * "children": [
   *   {
   *     "name": "Green Apple",
   *     "children": []
   *   },
   *   {
   *     "name": "Some Apple",
   *     "children": []
   *  }
   * ]
   *}
   *
   */
  TreeNode.prototype.export = function(criteria){

    // Check if criteria is specified
    if(!criteria || typeof criteria !== 'function')
      throw new Error('Export criteria not specified');

    // Export every node recursively
    var exportRecur = function(node){
      var exported = node.matchCriteria(criteria);
      if(!exported || typeof exported !== 'object'){
        throw new Error('Export criteria should always return an object and it cannot be null.');
      } else {
        exported.children = [];
        node._childNodes.forEach(function(_child){
          exported.children.push(exportRecur(_child));
        });

        return exported;
      }
    };

    return exportRecur(this);
  };

  // ------------------------------------
  // Export
  // ------------------------------------

  return TreeNode;

}());
<commit_msg>fixed typo in TreeNode.data setter<commit_after>
module.exports = (function(){

  // Flag bad practises
  'use strict';

  // ------------------------------------
  // Basic Setup
  // ------------------------------------

  /**
   * @class TreeNode
   * @classdesc Represents a node in the tree.
   * @constructor
   * @param {object} data - that is to be stored in a node
   */
  function TreeNode(data){

    /**
     * Represents the parent node
     *
     * @property _parentNode
     * @type {object}
     * @default "null"
     */
    this._parentNode = null;

    /**
     * Represents the child nodes
     *
     * @property _childNodes
     * @type {array}
     * @default "[]"
     */
    this._childNodes = [];

    /**
     * Represents the data node has
     *
     * @property _data
     * @type {object}
     * @default "null"
     */
    this._data = data;

    /**
     * Depth of the node represents level in hierarchy
     *
     * @property _depth
     * @type {number}
     * @default -1
     */
    this._depth = -1;

  }

  // ------------------------------------
  // Getters and Setters
  // ------------------------------------

  /**
   * Returns a parent node of current node
   *
   * @method parentNode
   * @memberof TreeNode
   * @instance
   * @return {TreeNode} - parent of current node
   */
  TreeNode.prototype.parentNode = function(){
    return this._parentNode;
  };

  /**
   * Returns an array of child nodes
   *
   * @method childNodes
   * @memberof TreeNode
   * @instance
   * @return {array} - array of child nodes
   */
  TreeNode.prototype.childNodes = function(){
    return this._childNodes;
  };

  /**
   * Sets or gets the data belonging to this node. Data is what user sets using `insert` and `insertTo` methods.
   *
   * @method data
   * @memberof TreeNode
   * @instance
   * @param {object | array | string | number | null} data - data which is to be stored
   * @return {object | array | string | number | null} - data belonging to this node
   */
  TreeNode.prototype.data = function(data){
    if(arguments.length > 0){
      this._data = data;
    } else {
      return this._data;
    }
  };

  /**
   * Depth of the node. Indicates the level at which node lies in a tree.
   *
   * @method depth
   * @memberof TreeNode
   * @instance
   * @return {number} - depth of node
   */
  TreeNode.prototype.depth = function(){
    return this._depth;
  };

  // ------------------------------------
  // Methods
  // ------------------------------------

  /**
   * Indicates whether this node matches the specified criteria. It triggers a callback criteria function that returns something.
   *
   * @method matchCriteria
   * @memberof TreeNode
   * @instance
   * @param {function} callback - Callback function that specifies some criteria. It receives {@link TreeNode#_data} in parameter and expects different values in different scenarios.
   * `matchCriteria` is used by following functions and expects:
   * 1. {@link Tree#searchBFS} - {boolean} in return indicating whether given node satisfies criteria.
   * 2. {@link Tree#searchDFS} - {boolean} in return indicating whether given node satisfies criteria.
   * 3. {@link Tree#export} - {object} in return indicating formatted data object.
   */
  TreeNode.prototype.matchCriteria = function(criteria){
    return criteria(this._data);
  };

  /**
   * get sibling nodes.
   *
   * @method siblings
   * @memberof TreeNode
   * @instance
   * @return {array} - array of instances of {@link TreeNode}
   */
  TreeNode.prototype.siblings = function(){
    var thiss = this;
    return !this._parentNode ? [] : this._parentNode._childNodes.filter(function(_child){
      return _child !== thiss;
    });
  };

  /**
   * Finds distance of node from root node
   *
   * @method distanceToRoot
   * @memberof TreeNode
   * @instance
   * @return {array} - array of instances of {@link TreeNode}
   */
  TreeNode.prototype.distanceToRoot = function(){

    // Initialize Distance and Node
    var distance = 0,
        node = this;

    // Loop Over Ancestors
    while(node.parentNode()){
      distance++;
      node = node.parentNode();
    }

    // Return
    return distance;

  };

  /**
   * Gets an array of all ancestor nodes including current node
   *
   * @method getAncestry
   * @memberof TreeNode
   * @instance
   * @return {Array} - array of ancestor nodes
   */
  TreeNode.prototype.getAncestry = function(){

    // Initialize empty array and node
    var ancestors = [this],
        node = this;

    // Loop over ancestors and add them in array
    while(node.parentNode()){
      ancestors.push(node.parentNode());
      node = node.parentNode();
    }

    // Return
    return ancestors;

  };

  /**
   * Exports the node data in format specified. It maintains herirachy by adding
   * additional "children" property to returned value of `criteria` callback.
   *
   * @method export
   * @memberof TreeNode
   * @instance
   * @param {TreeNode~criteria} criteria - Callback function that receives data in parameter
   * and MUST return a formatted data that has to be exported. A new property "children" is added to object returned
   * that maintains the heirarchy of nodes.
   * @return {object} - {@link TreeNode}.
   * @example
   *
   * var rootNode = tree.insert({
   *   key: '#apple',
   *   value: { name: 'Apple', color: 'Red'}
   * });
   *
   * tree.insert({
   *   key: '#greenapple',
   *   value: { name: 'Green Apple', color: 'Green'}
   * });
   *
   * tree.insertToNode(rootNode,  {
   *  key: '#someanotherapple',
   *  value: { name: 'Some Apple', color: 'Some Color' }
   * });
   *
   * // Export the tree
   * var exported = rootNode.export(function(data){
   *  return { name: data.value.name };
   * });
   *
   * // Result in `exported`
   * {
   * "name": "Apple",
   * "children": [
   *   {
   *     "name": "Green Apple",
   *     "children": []
   *   },
   *   {
   *     "name": "Some Apple",
   *     "children": []
   *  }
   * ]
   *}
   *
   */
  TreeNode.prototype.export = function(criteria){

    // Check if criteria is specified
    if(!criteria || typeof criteria !== 'function')
      throw new Error('Export criteria not specified');

    // Export every node recursively
    var exportRecur = function(node){
      var exported = node.matchCriteria(criteria);
      if(!exported || typeof exported !== 'object'){
        throw new Error('Export criteria should always return an object and it cannot be null.');
      } else {
        exported.children = [];
        node._childNodes.forEach(function(_child){
          exported.children.push(exportRecur(_child));
        });

        return exported;
      }
    };

    return exportRecur(this);
  };

  // ------------------------------------
  // Export
  // ------------------------------------

  return TreeNode;

}());
<|endoftext|>


<commit_before>"use strict";

/**
 * The default route map maps `/` URL to the `index()` method on the `main` controller.
 */
module.exports = {
  'GET /': 'main.index'
};

<commit_msg>Remove comment<commit_after>"use strict";

module.exports = {
  'GET /': 'main.index'
};

<|endoftext|>


<commit_before>$(document).ready(function(){
  var user1, user2, currentSession, startTime, endTime, currentInterval, totalTimeWorking, pairingDurationMs, timer;
  var sessions = [];

  $('button.duration-button').on('click', function(event){
    event.preventDefault();
    if($('input.duration').val() !== "" ){
      currentInterval = parseInt($('input.duration').val());
      pairingDurationMs = currentInterval * 60000;
    } else {
      alert("Please Enter A Real Number");
    }
  });

  $('button.start-pairing').on('click', function(){
    var date = new Date();
    startTime = date.getTime();
    currentSession = sessions.push(PairingSession(user1, user2));
    startTimerCount();
  });

  $('.pause_button').on('click', function(){
    if(timer){
      window.clearInterval(timer);
      timer = null;
    } else {
      startTimerCount();
    }
  });

  function PairingSession(user1, user2){
    this.drive = user1;
    this.navigate = user2;
    this.timeWorked = 0;
    this.timePaused = 0;
  }

  function TotalSessionInfo(activeTime, pauseTime){
    this.totalTime = pauseTime + activeTime;
    this.activeTime = activeTime;
    this.pauseTime = pauseTime;
  }

  function startTimerCount(){
    timer = setInterval(function(){
      totalTimeWorking ++;
      currentInterval --;
      updateTimerCountdown();
      checkDuration();
    }, 60000);
  }

  function calculatePauseTime(){
    pause_time = endTime - startTime - totalTimeWorking;
    currentSession.timePaused = pause_time;
  }

  function stopTimer(){
    window.clearInterval(timer);
  }

  function checkDuration(){
    if(currentInterval === 0){
      currentInterval = pairingDurationMs / 60000;
      currentSession.timeWorked = 20;
      alert('Switch it ^');
    }
  }

  function updateTimerCountdown(){
    $('.countdown').val(currentInterval);
  }
});

<commit_msg>Extrapolate Start Pairing Session Into Function<commit_after>$(document).ready(function(){
  var user1, user2, currentSession, startTime, endTime, currentInterval, totalTimeWorking, pairingDurationMs, timer;
  var sessions = [];

  $('button.duration-button').on('click', function(event){
    event.preventDefault();
    if($('input.duration').val() !== "" ){
      currentInterval = parseInt($('input.duration').val());
      pairingDurationMs = currentInterval * 60000;
    } else {
      alert("Please Enter A Real Number");
    }
  });

  $('button.start-pairing').on('click', function(){
    startPairing();
  });

  $('.pause_button').on('click', function(){
    if(timer){
      window.clearInterval(timer);
      timer = null;
    } else {
      startTimerCount();
    }
  });

  function PairingSession(user1, user2){
    this.drive = user1;
    this.navigate = user2;
    this.timeWorked = 0;
    this.timePaused = 0;
  }

  function TotalSessionInfo(activeTime, pauseTime){
    this.totalTime = pauseTime + activeTime;
    this.activeTime = activeTime;
    this.pauseTime = pauseTime;
  }

  function startPairing(){
    var date = new Date();
    startTime = date.getTime();
    currentSession = sessions.push(PairingSession(user1, user2));
    startTimerCount();
  }

  function startTimerCount(){
    timer = setInterval(function(){
      totalTimeWorking ++;
      currentInterval --;
      updateTimerCountdown();
      checkDuration();
    }, 60000);
  }

  function calculatePauseTime(){
    pause_time = endTime - startTime - totalTimeWorking;
    currentSession.timePaused = pause_time;
  }

  function stopTimer(){
    window.clearInterval(timer);
  }

  function checkDuration(){
    if(currentInterval === 0){
      currentInterval = pairingDurationMs / 60000;
      currentSession.timeWorked = 20;
      alert('Switch it ^');
    }
  }

  function updateTimerCountdown(){
    $('.countdown').val(currentInterval);
  }
});

<|endoftext|>