aboutsummaryrefslogtreecommitdiffstats
path: root/web/js/nms-map.js
blob: 224b3dba69381ebd034096a7eaa18ad4d9e739b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/***************************************************************************\
*                                                                           *
*  BitlBee - An IRC to IM gateway                                           *
*  Simple OAuth client (consumer) implementation.                           *
*                                                                           *
*  Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>                   *
*                                                                           *
*  This program is free software; you can redistribute it and/or modify     *
*  it under the terms of the GNU General Public License as published by     *
*  the Free Software Foundation; either version 2 of the License, or        *
*  (at your option) any later version.                                      *
*                                                                           *
*  This program is distributed in the hope that it will be useful,          *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
*  GNU General Public License for more details.                             *
*                                                                           *
*  You should have received a copy of the GNU General Public License along  *
*  with this program; if not, write to the Free Software Foundation, Inc.,  *
*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
*                                                                           *
\***************************************************************************/

/* http://oauth.net/core/1.0a/ */

struct oauth_info;

/* Callback function called twice during the access token request process.
   Return FALSE if something broke and the process must be aborted. */
typedef gboolean (*oauth_cb)( struct oauth_info * );

typedef enum
{
	OAUTH_INIT,
	OAUTH_REQUEST_TOKEN,
	OAUTH_ACCESS_TOKEN,
} oauth_stage_t;

struct oauth_info
{
	oauth_stage_t stage;
	const struct oauth_service *sp;
	
	oauth_cb func;
	void *data;
	
	struct http_request *http;
	
	char *auth_url;
	char *request_token;
	
	char *token;
	char *token_secret;
	GSList *params;
};

struct oauth_service
{
	char *url_request_token;
	char *url_access_token;
	char *url_authorize;
	
	char *consumer_key;
	char *consumer_secret;
};

/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1) 
   Request an initial anonymous token which can be used to construct an
   authorization URL for the user. This is passed to the callback function
   in a struct oauth_info. */
struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data );

/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3)
   The user gets a PIN or so which we now exchange for the final access
   token. This is passed to the callback function in the same
   struct oauth_info. */
gboolean oauth_access_token( const char *pin, struct oauth_info *st );

/* http://oauth.net/core/1.0a/#anchor12 (section 7)
   Generate an OAuth Authorization: HTTP header. access_token should be
   saved/fetched using the functions above. args can be a string with
   whatever's going to be in the POST body of the request. GET args will
   automatically be grabbed from url. */
char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args );

/* Shouldn't normally be required unless the process is aborted by the user. */
void oauth_info_free( struct oauth_info *info );

/* Convert to and back from strings, for easier saving. */
char *oauth_to_string( struct oauth_info *oi );
struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp );

/* For reading misc. data. */
void oauth_params_add( GSList **params, const char *key, const char *value );
void oauth_params_parse(pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
"use strict";

/* WORK
 * IN
 * PROGRESS
 *
 * Interface:
 *
 * nmsMap.init() - start things up
 * nmsMap.setSwitchColor(switch,color)
 * nmsMap.setSwitchInfo(switch,info)
 * nmsMap.setSwitchHighlight(switch,true/false)
 * nmsMap.enableHighlights()
 * nmsMap.disableHighlights()
 */


var nmsMap = nmsMap || {
	_moveInProgress: false,
	stats: {
		colorChange:0,
		colorSame:0,
		earlyDrawAll:0,
		nowDups:0,
		nows:0,
		resizeEvents:0,
		switchInfoSame:0,
		switchInfoUpdate:0,
		highlightChange:0
	},
	contexts: ["bg","link","blur","switch","text","textInfo","top","input","hidden"],
	_info: {},
	_settings: {
		fontLineFactor: 2,
		textMargin: 3,
		xMargin: 10,
		yMargin: 20,
		fontSize: 15,
		fontFace: "sans-serif"
	},
	scale: 1,
	_init: true,
	_orig: { width:1920, height:1032 },
	_canvas: {
		get width() { return nmsMap.scale * nmsMap._orig.width; },
		get height() { return nmsMap.scale * nmsMap._orig.height; }
	},

	_color: { },
	_linknets: {} ,
	_highlight: { },
	_highlightActive: false,
	_c: {}
};

nmsMap._loadEvent = function(e) {
	nmsMap._init = false;
	nmsMap._drawAllSwitches();
};

nmsMap.init = function() {
	this._initContexts();
	this._init = true;
	nmsData.addHandler("switches","nmsMap",function(){nmsMap._resizeEvent();});
	window.addEventListener('resize',nmsMap._resizeEvent,true);
	window.addEventListener('load',nmsMap._loadEvent,true);
};

nmsMap.setSwitchColor = function(sw, color) {
	if (this._color[sw] != color) {
		this._color[sw] = color;
		this._drawSwitch(sw);
		this.stats.colorChange++;
	} else {
		this.stats.colorSame++;
	}
};


nmsMap.setSwitchHighlight = function(sw, highlight) {
    if( highlight )
        highlight = true;
    if ( this._highlight[sw] != highlight ) {
        this.stats.highlightChange++;
        this._highlight[sw] = highlight;
    }
    this._drawSwitch(sw);
};


nmsMap.enableHighlights = function() {
    this._highlightActive = true;
};


nmsMap.disableHighlights = function() {
    this._highlightActive = false;
    this._drawAllSwitches();
};


nmsMap.reset = function() {
	for (var sw in this._color) {
		nmsMap.setSwitchColor(sw, undefined);
	}
	for (var sw in this._info) {
		nmsMap.setSwitchInfo(sw, undefined);
	}
};

nmsMap.setSwitchInfo = function(sw,info) {
	if (this._info[sw] != info) {
		this._info[sw] = info;
		this._drawSwitchInfo(sw);
		this.stats.switchInfoUpdate++;
	} else {
		this.stats.switchInfoSame++;
	}
};

nmsMap._initContext = function(name) {
	this._c[name] = {};
	this._c[name].c = document.getElementById(name + "Canvas");
	this._c[name].ctx = this._c[name].c.getContext('2d');
};

nmsMap._initContexts = function() {
	for (var context in this.contexts) {
		this._initContext(this.contexts[context]);
	}
};

nmsMap._resizeEvent = function() {
	var width = window.innerWidth - nmsMap._c.bg.c.offsetLeft;
	var height = window.innerHeight - nmsMap._c.bg.c.offsetTop;

	var xScale = (width / (nmsMap._orig.width + nmsMap._settings.xMargin));
	var yScale = (height / (nmsMap._orig.height + nmsMap._settings.yMargin));
	
	if (xScale > yScale) {
		nmsMap.scale = yScale;	
	} else {
		nmsMap.scale = xScale;
	}
	for (var a in nmsMap._c) {
		/*
		 * Resizing this to a too small size breaks gradients on smaller screens.
		 */
		if (a == 'hidden' && a != 'blur')
			continue;
		nmsMap._c[a].c.height = nmsMap._canvas.height;
		nmsMap._c[a].c.width = nmsMap._canvas.width;
    if(a == 'bg') {
      nmsMap._drawBG();
    }
	}
	if (nmsMap._init != true) {
		nmsMap._blurDrawn = false;
		nmsMap._drawBG();
		nmsMap._drawAllSwitches();
		nmsMap._drawAllLinknets();
		nmsMap.drawNow();
		nmsMap.stats.resizeEvents++;
	}
};

/*
 * Draw current time-window
 *
 * FIXME: The math here is just wild approximation and guesswork because
 * I'm lazy.
 *
 * FIXME: 2: Should really just use _drawText() instead somehow. Font size
 * being an issue.
 *
 * FIXME 3: Currently assuming that time from api is UTC and converting to
 * local time zone with js. Should find a more robust solution.
 *
 */
nmsMap.drawNow = function ()
{
	var now = nmsData.now;
	if(String(now).indexOf('T') == -1) { //If now does not contain 'T' we assume its in epoch format
		now = new Date(nmsData.now * 1000);
	} else {
		now = new Date(nmsData.now); //Date assumes UTC
	}
	now = now.toString().split(' ').splice(1,4).join(' '); //Date returns local time
	if (nmsMap._lastNow == now) {
		nmsMap.stats.nowDups++;
		return;
	}
	nmsMap.stats.nows++;

	var ctx = nmsMap._c.top.ctx;
	ctx.save();
	ctx.scale(this.scale, this.scale);
	ctx.font = (2 * this._settings.fontSize) + "px " + this._settings.fontFace;
	ctx.clearRect(0,0,800,100);
	ctx.fillStyle = "white";
	ctx.strokeStyle = "black";
	ctx.lineWidth = nms.fontLineFactor;
	ctx.strokeText(now, this._settings.textMargin, 25);
	ctx.fillText(now, this._settings.textMargin, 25);
	ctx.restore();
};

nmsMap.setNightMode = function(toggle) {
	if (this._nightmode == toggle)
		return;
	this._nightmode = toggle;
	if (this._init == true) {
		return;
	}
	if (!toggle)
		this._c.blur.c.style.display = "none";
	else {
		this._drawAllBlur();
		this._c.blur.c.style.display = "";
	}
	nmsMap._drawBG();
};

nmsMap._drawBG = function() {
	var imageObj = document.getElementById('source');
	this._c.bg.ctx.drawImage(imageObj, 0, 0, nmsMap._canvas.width, nmsMap._canvas.height);
	if(this._nightmode)
		nmsMap._invertBG();
};

nmsMap._invertBG = function() {
	var imageData = this._c.bg.ctx.getImageData(0, 0, nmsMap._canvas.width, nmsMap._canvas.height);
	var data = imageData.data;

	for(var i = 0; i < data.length; i += 4) {
		data[i] = 255 - data[i];
		data[i + 1] = 255 - data[i + 1];
		data[i + 2] = 255 - data[i + 2];
	}
	this._c.bg.ctx.putImageData(imageData, 0, 0);
};

nmsMap._getBox = function(sw) {
	var box = nmsData.switches.switches[sw]['placement'];
	box.x = parseInt(box.x);
	box.y = parseInt(box.y);
	box.width = parseInt(box.width);
	box.height = parseInt(box.height);
	return box;
};

nmsMap._drawSwitchBlur = function(sw)
{
	if (nmsData.switches == undefined || nmsData.switches.switches == undefined)
		return;
	var box = this._getBox(sw);
	this._c.blur.ctx.save();
	this._c.blur.ctx.fillStyle = "red";
	this._c.blur.ctx.shadowBlur = 30;
	this._c.blur.ctx.shadowColor = "white";
	this._c.blur.ctx.scale(this.scale, this.scale); // FIXME <- fix what?!
	this._c.blur.ctx.fillRect(box['x'],box['y'],box['width'],box['height']);
	this._c.blur.ctx.restore();
};

nmsMap._drawSwitch = function(sw)
{
	// XXX: If a handler sets a color before switches are loaded... The
	// color will get set fine so this isn't a problem.
	if (nmsData.switches == undefined || nmsData.switches.switches == undefined)
		return;
	var box = this._getBox(sw);
	var color = nmsMap._color[sw];
	if(this._highlightActive) {
		if(nmsMap._highlight[sw]) {
			color = green;
		} else {
			color = white;
		}
	}
	if (color == undefined) {
		color = blue;
	}
	this._c.switch.ctx.fillStyle = color;
	this._drawBox(this._c.switch.ctx, box['x'],box['y'],box['width'],box['height']);
	this._c.switch.ctx.shadowBlur = 0;
	this._drawText(this._c.text.ctx, sw,box);

	if(this._info[sw])
		this._drawSwitchInfo(sw);
};

nmsMap._drawSwitchInfo = function(sw) {
	var box = this._getBox(sw);
	if (this._info[sw] == undefined) {
		this._clearBox(this._c.textInfo.ctx, box);
	} else {
		this._drawText(this._c.textInfo.ctx, this._info[sw], box, "right");
	}
};

nmsMap._clearBox = function(ctx,box) {
	ctx.save();
	ctx.scale(this.scale,this.scale);
	ctx.clearRect(box['x'], box['y'], box['width'], box['height']);
	ctx.restore();
};

nmsMap._drawText = function(ctx, text, box, align) {
	var rotate = false;

	if ((box['width'] + 10 )< box['height'])
		rotate = true;
	
	this._clearBox(ctx,box);
	ctx.save();
	ctx.scale(this.scale, this.scale);
	ctx.font = "bold " + this._settings.fontSize + "px " + this._settings.fontFace;
	ctx.lineWidth = nmsMap._settings.fontLineFactor;
	ctx.fillStyle = "white";
	ctx.strokeStyle = "black";
	ctx.translate(box.x + this._settings.textMargin, box.y + box.height - this._settings.textMargin);
	
	if (rotate) {
		ctx.translate(box.width - this._settings.textMargin * 2,0);
		ctx.rotate(Math.PI * 3/2);
	}

	if (align == "right") {
		ctx.textAlign = "right";
		/*
		 * Margin*2 is to compensate for the margin above.
		 */
		if (rotate)
			ctx.translate(box.height - this._settings.textMargin*2,0);
		else
			ctx.translate(box.width - this._settings.textMargin*2,0);
	}

	ctx.strokeText(text, 0, 0);
	ctx.fillText(text, 0, 0);
	ctx.restore();
};

nmsMap._setLinknetColor = function(l, color1, color2)
{
	var oldcolor1;
	var oldcolor2;
	try {
		oldcolor1 = nmsMap._linknets[l].sysname1;
		oldcolor2 = nmsMap._linknets[l].sysname2;
		if (oldcolor1 == color1 && oldcolor2 == color2) {
			return ;
		}
	} catch (e) {}
	nmsMap._linknets[l] = {};
	nmsMap._linknets[l].sysname1 = color1;
	nmsMap._linknets[l].sysname2 = color2;
	nmsMap._drawLinknet(l)
}

nmsMap._drawLinknet = function(l) {
	try {
		var color1 = blue;
		var color2 = blue;
		try {
			color1 = nmsMap._linknets[l].sysname1;
			color2 = nmsMap._linknets[l].sysname2;
		} catch(e) { }
		nmsMap._connectSwitches(nmsData.switches.linknets[l].sysname1, nmsData.switches.linknets[l].sysname2, color1, color2);
	} catch(e) { }
}

nmsMap._drawAllLinknets = function() {
	for (var l in nmsData.switches.linknets) {
		nmsMap._drawLinknet(l);
	}
}
nmsMap._drawAllSwitches = function() {
	if (nmsData.switches == undefined) {
		this.stats.earlyDrawAll++;
		return;
	}
	for (var sw in nmsData.switches.switches) {
		this._drawSwitch(sw);
	}
	if (this._nightmode)
		this._drawAllBlur();
};

nmsMap._drawAllBlur = function() {
	if (nmsMap._blurDrawn == true)
		return;
	nmsMap._blurDrawn = true;
	for (var sw in nmsData.switches.switches) {
		nmsMap._drawSwitchBlur(sw);
	}
};

nmsMap._drawBox = function(ctx, x, y, boxw, boxh) {
	ctx.save();
	ctx.scale(this.scale, this.scale); // FIXME <- what?!
	ctx.fillRect(x,y, boxw, boxh);
	ctx.lineWidth = 1;
	ctx.strokeStyle = "#000000";
	ctx.strokeRect(x,y, boxw, boxh);
	ctx.restore();
};

nmsMap._connectSwitches = function(sw1, sw2, color1, color2) {
	nmsMap._connectBoxes(this._getBox(sw1), this._getBox(sw2),
			     color1, color2);
};

/*
 * Draw a line between two boxes, with a gradient going from color1 to
 * color2.
 */
nmsMap._connectBoxes = function(box1, box2,color1, color2) {
	var ctx = nmsMap._c.link.ctx;
	if (color1 == undefined)
		color1 = blue;
	if (color2 == undefined)
		color2 = blue;
	var x0 = Math.floor(box1.x + box1.width/2);
	var y0 = Math.floor(box1.y + box1.height/2);
	var x1 = Math.floor(box2.x + box2.width/2);
	var y1 = Math.floor(box2.y + box2.height/2);
	ctx.save();
	ctx.scale(nmsMap.scale, nmsMap.scale);
	var gradient = ctx.createLinearGradient(x1,y1,x0,y0);
	gradient.addColorStop(0, color1);
	gradient.addColorStop(1, color2);
	ctx.strokeStyle = gradient;
	ctx.beginPath();
	ctx.moveTo(x0,y0);
	ctx.lineTo(x1,y1); 
	ctx.lineWidth = 5;
	ctx.stroke();
	ctx.closePath();
	ctx.restore();
};

nmsMap.moveSet = function(toggle) {
	nmsMap._moveInProgress = toggle;
	if (!toggle)
		nmsMap._moveStopListen();
};

/*
 * onclick handler for the canvas.
 *
 * Currently just shows info for a switch.
 */
nmsMap.canvasClick = function(e)
{
	var sw = findSwitch(e.pageX - e.target.offsetLeft, e.pageY - e.target.offsetTop);
	if (sw != undefined) {
		if (nmsMap._moveInProgress) {
			nmsMap._moveStart(sw, e);
		} else {
			nmsInfoBox.click(sw);
		}
	} else {
		nmsInfoBox.hide();
	}
};

nmsMap._clearOld = function(box) {
	if (box) {
		nmsMap._c.top.ctx.save();
		nmsMap._c.top.ctx.fillStyle = "#000000";
		nmsMap._c.top.ctx.scale(nmsMap.scale, nmsMap.scale); // FIXME
		nmsMap._c.top.ctx.clearRect(box['x'] - 5, box['y'] - 5, box['width'] + 10, box['height'] + 10);
		nmsMap._c.top.ctx.restore();
	}
};

nmsMap._moveMove = function(e) {
	nmsMap._moveX = (e.pageX - e.target.offsetLeft) / nmsMap.scale;
	nmsMap._moveY = (e.pageY - e.target.offsetTop) / nmsMap.scale;
	var diffx = nmsMap._moveX - nmsMap._moveXstart;
	var diffy = nmsMap._moveY - nmsMap._moveYstart;
	var box = {};
	nmsMap._clearOld(nmsMap._moveOldBox);
	box['x'] = nmsMap._moveBox['x'] + diffx;
	box['y'] = nmsMap._moveBox['y'] + diffy;
	box['height'] = nmsMap._moveBox['height'];
	box['width'] = nmsMap._moveBox['width'];
	nmsMap._moveOldBox = box;
	nmsMap._c.top.ctx.save();
	nmsMap._c.top.ctx.fillStyle = "red";
	nmsMap._drawBox(nmsMap._c.top.ctx, box['x'], box['y'], box['width'], box['height']);
	nmsMap._c.top.ctx.restore();
};

nmsMap._moveSubmit = function() {
	var data = {
		sysname: nmsMap._moving,
		placement: nmsMap._moveOldBox
	};
	var myData = JSON.stringify([data]);
	$.ajax({
		type: "POST",
		url: "/api/write/switch-update",
		dataType: "text",
		data:myData,
		success: function (data, textStatus, jqXHR) {
			nmsData.invalidate("switches");
		}
	});
};

nmsMap._moveStopListen = function() {
	nmsMap._c.input.c.removeEventListener('mousemove',nmsMap._moveMove, true);
	nmsMap._c.input.c.removeEventListener('mouseup',nmsMap._moveDone, true);
};

nmsMap._moveDone = function(e) {
	nmsMap._moveStopListen();
	if(nmsMap._moveOldBox == false) {
		return;
	}
	nmsMap._moveSubmit();
	nmsMap._clearOld(nmsMap._moveOldBox);
};

nmsMap._moveStart = function(sw, e)
{
	nmsMap._moving = sw;
	nmsMap._moveOldBox = false;
	nmsMap._moveXstart = (e.pageX - e.target.offsetLeft) / nmsMap.scale;
	nmsMap._moveYstart = (e.pageY - e.target.offsetTop) / nmsMap.scale;
	nmsMap._moveBox = nmsData.switches.switches[sw].placement;
	nmsMap._c.input.c.addEventListener('mousemove',nmsMap._moveMove,true);
	nmsMap._c.input.c.addEventListener('mouseup',nmsMap._moveDone,true);
};


/*
 * STUFF NOT YET INTEGRATED, BUT MOVED AWAY FROM nms.js TO TIDY.
 *
 * Consider this a TODO list.
 */

/*
 * Draw a linknet with index i.
 *
 * XXX: Might have to change the index here to match backend
 */
function drawLinknet(i)
{
	var c1 = nms.linknet_color[i] && nms.linknet_color[i].c1 ? nms.linknet_color[i].c1 : blue;
	var c2 = nms.linknet_color[i] && nms.linknet_color[i].c2 ? nms.linknet_color[i].c2 : blue;
	if (nmsData.switches.switches[nmsData.switches.linknets[i].sysname1] && nmsData.switches.switches[nmsData.switches.linknets[i].sysname2]) {
		connectSwitches(nmsData.switches.linknets[i].sysname1,nmsData.switches.linknets[i].sysname2, c1, c2);
	}
}

/*
 * Draw all linknets
 */
function drawLinknets()
{
	if (nmsData.switches && nmsData.switches.linknets) {
		for (var i in nmsData.switches.linknets) {
			drawLinknet(i);
		}
	}
}

/*
 * Change both colors of a linknet.
 *
 * XXX: Probably have to change this to better match the backend data
 */
function setLinknetColors(i,c1,c2)
{
	if (!nms.linknet_color[i] || 
 	     nms.linknet_color[i].c1 != c1 ||
	     nms.linknet_color[i].c2 != c2) {
		if (!nms.linknet_color[i])
			nms.linknet_color[i] = {};
		nms.linknet_color[i]['c1'] = c1;
		nms.linknet_color[i]['c2'] = c2;
		drawLinknet(i);
	}
}