Node.js datasource plugin for freeboard.io with socket.io namespaces and rooms.

pull/2/head
Hugo Sequeira 2014-06-26 19:09:04 +02:00
parent c0328ccf50
commit c2fa5e91a5
51 changed files with 29841 additions and 3 deletions

18
.project 100755
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>NodeJSToFreeboardIO</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.eclipsesource.jshint.ui.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.nodeclipse.ui.NodeNature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,3 @@
eclipse.preferences.version=1
encoding//datasources/plugin_node.js=UTF-8
encoding/plugin_node_sample_server.js=UTF-8

View File

@ -1,4 +1,58 @@
plugins
=======
freeboard.io-node.js
=========
Plugins for Freeboard.io
This is a datasource plugin to connect freeboard.io dashboards to real-time node.js servers.
- Subscribe to real-time events using WebSockets (Sockets.io)
- Listen for events from different servers
- Restrict events from only certain [namespaces or rooms](http://socket.io/docs/rooms-and-namespaces/)
Installation
--------------
1. Place the file ```/datasources/plugin_node.js``` in ```/js/freeboard/plugins``` of your freeboard.io installation.
2. Edit your freeboard.io main HTML file and add the plugin to the header:
```js
<script type="text/javascript">
head.js(
...
"/js/freeboard/plugins/plugin_node.js",
```
Testing
--------------
You can test this plugin by running the node.js **sample server** in your terminal:
```js
node plugin_node_sample_server.js
```
Open the **sample freeboard.io client** with your browser:
```js
/plugin_node_sample_client.html
```
Requirements
--------------
To run the sample server make sure you have following dependencies installed:
1. [Node.js](http://nodejs.org)
2. [Socket.io](http://socket.io)
Screenshots
--------------
In this screenshot, you can see the sample server and dashboard running with multiple clients and receiving data in real-time.
![Image](plugin_node_sample_example.png)
License
----
MIT

File diff suppressed because one or more lines are too long

272
css/codemirror.css 100755
View File

@ -0,0 +1,272 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
}
.CodeMirror-scroll {
/* Set scrolling behaviour here */
overflow: auto;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* CURSOR */
.CodeMirror div.CodeMirror-cursor {
border-left: 1px solid black;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
}
/* Can style cursor different in overwrite (non-insert) mode */
div.CodeMirror-overwrite div.CodeMirror-cursor {}
.cm-tab { display: inline-block; }
.CodeMirror-ruler {
border-left: 1px solid #ccc;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
line-height: 1;
position: relative;
overflow: hidden;
background: white;
color: black;
}
.CodeMirror-scroll {
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
-moz-box-sizing: content-box;
box-sizing: content-box;
padding-bottom: 30px;
margin-bottom: -32px;
display: inline-block;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-lines {
cursor: text;
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-measure pre { position: static; }
.CodeMirror div.CodeMirror-cursor {
position: absolute;
border-right: none;
width: 0;
}
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 1;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}

2
css/jquery.gridster.min.css vendored 100755
View File

@ -0,0 +1,2 @@
/*! gridster.js - v0.1.0 - 2013-06-14 - * http://gridster.net/ - Copyright (c) 2013 ducksboard; Licensed MIT */
.gridster{position:relative}.gridster>*{margin:0 auto;-webkit-transition:height .4s;-moz-transition:height .4s;-o-transition:height .4s;-ms-transition:height .4s;transition:height .4s}.gridster .gs_w{z-index:2;position:absolute}.ready .gs_w:not(.preview-holder){-webkit-transition:opacity .3s,left .3s,top .3s;-moz-transition:opacity .3s,left .3s,top .3s;-o-transition:opacity .3s,left .3s,top .3s;transition:opacity .3s,left .3s,top .3s}.ready .gs_w:not(.preview-holder){-webkit-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-moz-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;-o-transition:opacity .3s,left .3s,top .3s,width .3s,height .3s;transition:opacity .3s,left .3s,top .3s,width .3s,height .3s}.gridster .preview-holder{z-index:1;position:absolute;background-color:#fff;border-color:#fff;opacity:.3}.gridster .player-revert{z-index:10!important;-webkit-transition:left .3s,top .3s!important;-moz-transition:left .3s,top .3s!important;-o-transition:left .3s,top .3s!important;transition:left .3s,top .3s!important}.gridster .dragging{z-index:10!important;-webkit-transition:all 0s!important;-moz-transition:all 0s!important;-o-transition:all 0s!important;transition:all 0s!important}

2666
css/styles.css 100755

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ freeboard.io-node.js │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright © 2014 Hugo Sequeira (https://github.com/hugocore) │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT license. │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Freeboard datasource plugin for node.js and socket.io. │ \\
// └────────────────────────────────────────────────────────────────────┘ \\
(function() {
var nodeJSDatasource = function(settings, updateCallback) {
var self = this;
var currentSettings = settings;
var url;
var socket;
var newMessageCallback;
function onNewMessageHandler(message) {
console.debug("Message received %s", message);
var objdata = JSON.parse(message);
if (typeof objdata == "object") {
updateCallback(objdata);
} else {
updateCallback(data);
}
}
function joinRoom(roomName, roomEvent) {
// Sends request to join the new room
// (handle event on server-side)
self.socket.emit(roomEvent, roomName);
console.info("Joining room '%s' with event '%s'", roomName, roomEvent);
}
function connectToServer(url, rooms) {
// Establish connection with server
self.url = url;
self.socket = io.connect(self.url);
// Join the rooms
self.socket.on('connect', function() {
console.info("Connecting to Node.js at: %s", self.url);
// Join the rooms
_.each(rooms, function(roomConfig) {
var roomName = roomConfig.roomName;
var roomEvent = roomConfig.roomEvent;
if (!_.isUndefined(roomName) && !_.isUndefined(roomEvent)) {
joinRoom(roomName, roomEvent);
}
});
});
self.socket.on('connect_error', function(object) {
console.error("It was not possible to connect to Node.js at: %s", self.url);
});
self.socket.on('reconnect_error', function(object) {
console.error("Still was not possible to re-connect to Node.js at: %s", self.url);
});
self.socket.on('reconnect_failed', function(object) {
console.error("Stopping re-connecting to Node.js at: %s", self.url);
});
}
function disconnecFromServer() {
// Disconnect any older socket
if (self.socket) {
self.socket.disconnect();
console.info("Disconnected from Node.js: %s", self.url);
}
}
function initializeDataSource() {
console.info("Subscribing to event: %s", currentSettings.eventName);
// Reset connection to server
disconnecFromServer();
connectToServer(currentSettings.url, currentSettings.rooms);
// Subscribe to the events
var newEventName = currentSettings.eventName;
self.newMessageCallback = onNewMessageHandler;
self.socket.on(newEventName, function(message) {
self.newMessageCallback(message);
});
}
initializeDataSource();
this.updateNow = function() {
// Just seat back, relax and wait for incoming events
return;
};
this.onDispose = function() {
// Stop responding to messages
self.newMessageCallback = function(message) {
return;
};
};
this.onSettingsChanged = function(newSettings) {
currentSettings = newSettings;
initializeDataSource();
};
};
freeboard
.loadDatasourcePlugin({
type_name : "node_js",
display_name : "Node.js (Socket.io)",
description : "A real-time stream datasource from node.js servers using socket.io.",
external_scripts : [ "https://cdn.socket.io/socket.io-1.0.6.js" ],
settings : [
{
name : "url",
display_name : "Server URL",
description : "(Optional) In case you are using custom namespaces, add the name of the namespace (e.g. chat) at the end of your URL.<br>For example: http://localhost/chat",
type : "text"
},
{
name : "eventName",
display_name : "Events",
description : "The name of the events you want this datasource to subscribe to.",
type : "text"
},
{
name : "rooms",
display_name : "(Optional) Rooms",
description : "In case you are using rooms, specify the name of the rooms you want to join. Otherwise, leave this empty.",
type : "array",
settings : [ {
name : "roomName",
display_name : "Room name",
type : "text"
}, {
name : "roomEvent",
display_name : "Name of the event to join the room",
type : "text"
} ]
} ],
newInstance : function(settings, newInstanceCallback,
updateCallback) {
newInstanceCallback(new nodeJSDatasource(settings,
updateCallback));
}
});
}());

View File

@ -0,0 +1,3 @@
#!/bin/bash
docco --css docco-fb.css ./../examples/plugin_example.js --output ./

630
docs/docco-fb.css 100755
View File

@ -0,0 +1,630 @@
/*--------------------- Typography ----------------------------*/
@import url(http://fonts.googleapis.com/css?family=Montserrat);
@font-face
{
font-family : 'aller-light';
src : url('public/fonts/aller-light.eot');
src : url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), url('public/fonts/aller-light.woff') format('woff'), url('public/fonts/aller-light.ttf') format('truetype');
font-weight : normal;
font-style : normal;
}
@font-face
{
font-family : 'aller-bold';
src : url('public/fonts/aller-bold.eot');
src : url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), url('public/fonts/aller-bold.woff') format('woff'), url('public/fonts/aller-bold.ttf') format('truetype');
font-weight : normal;
font-style : normal;
}
@font-face
{
font-family : 'novecento-bold';
src : url('public/fonts/novecento-bold.eot');
src : url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), url('public/fonts/novecento-bold.woff') format('woff'), url('public/fonts/novecento-bold.ttf') format('truetype');
font-weight : normal;
font-style : normal;
}
/*--------------------- Layout ----------------------------*/
html
{
height : 100%;
}
body
{
font-family : "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size : 14px;
line-height : 18px;
color : #30404f;
margin : 0;
padding : 0;
height : 100%;
}
#container
{
min-height : 100%;
}
a
{
color : #000;
}
b, strong
{
font-weight : 400;
color : #fff;
}
p, ul, ol
{
margin : 15px 0 0px;
}
h1, h2, h3, h4, h5, h6
{
font-family : montserrat, sans-serif;
color : #fff;
line-height : 1em;
font-weight : normal;
text-transform : uppercase;
margin : 30px 0 15px 0;
}
h1
{
margin-top : 40px;
}
hr
{
border : 0;
background : 1px solid #ddd;
height : 1px;
margin : 20px 0;
}
pre, tt, code
{
font-size : 12px;
line-height : 16px;
font-family : Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin : 0;
padding : 0;
}
.annotation
{
color: #A5A5A5;
font-weight : 200;
font-size: 14px;
}
.annotation pre
{
display : block;
margin : 0;
padding : 7px 10px;
background : #fcfcfc;
-moz-box-shadow : inset 0 0 10px rgba(0, 0, 0, 0.1);
-webkit-box-shadow : inset 0 0 10px rgba(0, 0, 0, 0.1);
box-shadow : inset 0 0 10px rgba(0, 0, 0, 0.1);
overflow-x : auto;
}
.annotation pre code
{
border : 0;
padding : 0;
background : transparent;
}
blockquote
{
border-left : 5px solid #ccc;
margin : 0;
padding : 1px 0 1px 1em;
}
.sections blockquote p
{
font-family : Menlo, Consolas, Monaco, monospace;
font-size : 12px;
line-height : 16px;
color : #999;
margin : 10px 0 0;
white-space : pre-wrap;
}
ul.sections
{
list-style : none;
padding : 0 0 5px 0;;
margin : 0;
}
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
ul.sections > li > div
{
-moz-box-sizing : border-box; /* firefox */
-ms-box-sizing : border-box; /* ie */
-webkit-box-sizing : border-box; /* webkit */
-khtml-box-sizing : border-box; /* konqueror */
box-sizing : border-box; /* css3 */
}
/*---------------------- Jump Page -----------------------------*/
#jump_to, #jump_page
{
margin : 0;
background : white;
-webkit-box-shadow : 0 0 25px #777;
-moz-box-shadow : 0 0 25px #777;
-webkit-border-bottom-left-radius : 5px;
-moz-border-radius-bottomleft : 5px;
font : 16px Arial;
cursor : pointer;
text-align : right;
list-style : none;
}
#jump_to a
{
text-decoration : none;
}
#jump_to a.large
{
display : none;
}
#jump_to a.small
{
font-size : 22px;
font-weight : bold;
color : #676767;
}
#jump_to, #jump_wrapper
{
position : fixed;
right : 0;
top : 0;
padding : 10px 15px;
margin : 0;
}
#jump_wrapper
{
display : none;
padding : 0;
}
#jump_to:hover #jump_wrapper
{
display : block;
}
#jump_page
{
padding : 5px 0 3px;
margin : 0 0 25px 25px;
}
#jump_page .source
{
display : block;
padding : 15px;
text-decoration : none;
border-top : 1px solid #eee;
}
#jump_page .source:hover
{
background : #f5f5ff;
}
#jump_page .source:first-child
{
}
/*---------------------- Low resolutions (> 320px) ---------------------*/
@media only screen and (min-width: 320px)
{
.pilwrap
{
display : none;
}
ul.sections > li > div
{
display : block;
padding : 5px 10px 0 10px;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol
{
padding-left : 30px;
}
ul.sections > li > div.content
{
background : #f5f5ff;
overflow-x : auto;
-webkit-box-shadow : inset 0 0 5px #e5e5ee;
box-shadow : inset 0 0 5px #e5e5ee;
border : 1px solid #dedede;
margin : 5px 10px 5px 10px;
padding-bottom : 5px;
}
ul.sections > li > div.annotation pre
{
margin : 7px 0 7px;
padding-left : 15px;
}
ul.sections > li > div.annotation p tt, .annotation code
{
background : #f8f8ff;
border : 1px solid #dedede;
font-size : 12px;
padding : 0 0.2em;
}
}
/*---------------------- (> 481px) ---------------------*/
@media only screen and (min-width: 481px)
{
#container
{
position : relative;
}
body
{
background-color : #f9f9f9;
font-size : 15px;
line-height : 21px;
}
pre, tt, code
{
line-height : 18px;
}
p, ul, ol
{
margin : 0 0 15px;
}
#jump_to
{
padding : 5px 10px;
}
#jump_wrapper
{
padding : 0;
}
#jump_to, #jump_page
{
font : 10px Arial;
text-transform : uppercase;
}
#jump_page .source
{
padding : 5px 10px;
}
#jump_to a.large
{
display : inline-block;
}
#jump_to a.small
{
display : none;
}
#background
{
position : absolute;
top : 0;
bottom : 0;
width : 350px;
background : #313131;
border-right : 1px solid #e5e5ee;
z-index : -1;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol
{
padding-left : 40px;
}
ul.sections > li
{
white-space : nowrap;
}
ul.sections > li > div
{
display : inline-block;
}
ul.sections > li > div.annotation
{
max-width : 350px;
min-width : 350px;
min-height : 5px;
padding : 13px;
overflow-x : hidden;
white-space : normal;
vertical-align : top;
text-align : left;
}
ul.sections > li > div.annotation pre
{
margin : 15px 0 15px;
padding-left : 15px;
}
ul.sections > li > div.content
{
padding : 13px;
vertical-align : top;
background : #f9f9f9;
border : none;
-webkit-box-shadow : none;
box-shadow : none;
}
.pilwrap
{
position : relative;
display : inline;
}
.pilcrow
{
font : 12px Arial;
text-decoration : none;
color : #454545;
position : absolute;
top : 3px;
left : -20px;
padding : 1px 2px;
opacity : 0;
-webkit-transition : opacity 0.2s linear;
}
.for-h1 .pilcrow
{
top : 47px;
}
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow
{
top : 35px;
}
ul.sections > li > div.annotation:hover .pilcrow
{
opacity : 1;
}
}
/*---------------------- (> 1025px) ---------------------*/
@media only screen and (min-width: 1025px)
{
body
{
font-size : 16px;
line-height : 24px;
}
#background
{
width : 525px;
}
ul.sections > li > div.annotation
{
max-width : 525px;
min-width : 525px;
padding : 10px 25px 1px 50px;
}
ul.sections > li > div.content
{
padding : 9px 15px 16px 25px;
}
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos
{
background-color : #f0f0f0;
padding-right : 10px;
}
span.lineno
{
background-color : #f0f0f0;
padding : 0 5px 0 5px;
}
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code
{
display : block;
padding : 0.5em;
color : #000;
background : #f8f8ff
}
pre .comment,
pre .template_comment,
pre .diff .header,
pre .javadoc
{
color : #408080;
font-style : italic
}
pre .keyword,
pre .assignment,
pre .literal,
pre .css .rule .keyword,
pre .winutils,
pre .javascript .title,
pre .lisp .title,
pre .subst
{
color : #954121;
/*font-weight: bold*/
}
pre .number,
pre .hexcolor
{
color : #40a070
}
pre .string,
pre .tag .value,
pre .phpdoc,
pre .tex .formula
{
color : #219161;
}
pre .title,
pre .id
{
color : #19469D;
}
pre .params
{
color : #00F;
}
pre .javascript .title,
pre .lisp .title,
pre .subst
{
font-weight : normal
}
pre .class .title,
pre .haskell .label,
pre .tex .command
{
color : #458;
font-weight : bold
}
pre .tag,
pre .tag .title,
pre .rules .property,
pre .django .tag .keyword
{
color : #000080;
font-weight : normal
}
pre .attribute,
pre .variable,
pre .instancevar,
pre .lisp .body
{
color : #008080
}
pre .regexp
{
color : #B68
}
pre .class
{
color : #458;
font-weight : bold
}
pre .symbol,
pre .ruby .symbol .string,
pre .ruby .symbol .keyword,
pre .ruby .symbol .keymethods,
pre .lisp .keyword,
pre .tex .special,
pre .input_number
{
color : #990073
}
pre .builtin,
pre .constructor,
pre .built_in,
pre .lisp .title
{
color : #0086b3
}
pre .preprocessor,
pre .pi,
pre .doctype,
pre .shebang,
pre .cdata
{
color : #999;
font-weight : bold
}
pre .deletion
{
background : #fdd
}
pre .addition
{
background : #dfd
}
pre .diff .change
{
background : #0086b3
}
pre .chunk
{
color : #aaa
}
pre .tex .formula
{
opacity : 0.5;
}

500
docs/docco.css 100755
View File

@ -0,0 +1,500 @@
/*--------------------- Typography ----------------------------*/
@font-face {
font-family: 'aller-light';
src: url('public/fonts/aller-light.eot');
src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-light.woff') format('woff'),
url('public/fonts/aller-light.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'aller-bold';
src: url('public/fonts/aller-bold.eot');
src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
url('public/fonts/aller-bold.woff') format('woff'),
url('public/fonts/aller-bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'novecento-bold';
src: url('public/fonts/novecento-bold.eot');
src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'),
url('public/fonts/novecento-bold.woff') format('woff'),
url('public/fonts/novecento-bold.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
/*--------------------- Layout ----------------------------*/
html { height: 100%; }
body {
font-family: "aller-light";
font-size: 14px;
line-height: 18px;
color: #30404f;
margin: 0; padding: 0;
height:100%;
}
#container { min-height: 100%; }
a {
color: #000;
}
b, strong {
font-weight: normal;
font-family: "aller-bold";
}
p, ul, ol {
margin: 15px 0 0px;
}
h1, h2, h3, h4, h5, h6 {
color: #112233;
line-height: 1em;
font-weight: normal;
font-family: "novecento-bold";
text-transform: uppercase;
margin: 30px 0 15px 0;
}
h1 {
margin-top: 40px;
}
hr {
border: 0;
background: 1px solid #ddd;
height: 1px;
margin: 20px 0;
}
pre, tt, code {
font-size: 12px; line-height: 16px;
font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
margin: 0; padding: 0;
}
.annotation pre {
display: block;
margin: 0;
padding: 7px 10px;
background: #fcfcfc;
-moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
-webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
overflow-x: auto;
}
.annotation pre code {
border: 0;
padding: 0;
background: transparent;
}
blockquote {
border-left: 5px solid #ccc;
margin: 0;
padding: 1px 0 1px 1em;
}
.sections blockquote p {
font-family: Menlo, Consolas, Monaco, monospace;
font-size: 12px; line-height: 16px;
color: #999;
margin: 10px 0 0;
white-space: pre-wrap;
}
ul.sections {
list-style: none;
padding:0 0 5px 0;;
margin:0;
}
/*
Force border-box so that % widths fit the parent
container without overlap because of margin/padding.
More Info : http://www.quirksmode.org/css/box.html
*/
ul.sections > li > div {
-moz-box-sizing: border-box; /* firefox */
-ms-box-sizing: border-box; /* ie */
-webkit-box-sizing: border-box; /* webkit */
-khtml-box-sizing: border-box; /* konqueror */
box-sizing: border-box; /* css3 */
}
/*---------------------- Jump Page -----------------------------*/
#jump_to, #jump_page {
margin: 0;
background: white;
-webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
-webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
font: 16px Arial;
cursor: pointer;
text-align: right;
list-style: none;
}
#jump_to a {
text-decoration: none;
}
#jump_to a.large {
display: none;
}
#jump_to a.small {
font-size: 22px;
font-weight: bold;
color: #676767;
}
#jump_to, #jump_wrapper {
position: fixed;
right: 0; top: 0;
padding: 10px 15px;
margin:0;
}
#jump_wrapper {
display: none;
padding:0;
}
#jump_to:hover #jump_wrapper {
display: block;
}
#jump_page {
padding: 5px 0 3px;
margin: 0 0 25px 25px;
}
#jump_page .source {
display: block;
padding: 15px;
text-decoration: none;
border-top: 1px solid #eee;
}
#jump_page .source:hover {
background: #f5f5ff;
}
#jump_page .source:first-child {
}
/*---------------------- Low resolutions (> 320px) ---------------------*/
@media only screen and (min-width: 320px) {
.pilwrap { display: none; }
ul.sections > li > div {
display: block;
padding:5px 10px 0 10px;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 30px;
}
ul.sections > li > div.content {
background: #f5f5ff;
overflow-x:auto;
-webkit-box-shadow: inset 0 0 5px #e5e5ee;
box-shadow: inset 0 0 5px #e5e5ee;
border: 1px solid #dedede;
margin:5px 10px 5px 10px;
padding-bottom: 5px;
}
ul.sections > li > div.annotation pre {
margin: 7px 0 7px;
padding-left: 15px;
}
ul.sections > li > div.annotation p tt, .annotation code {
background: #f8f8ff;
border: 1px solid #dedede;
font-size: 12px;
padding: 0 0.2em;
}
}
/*---------------------- (> 481px) ---------------------*/
@media only screen and (min-width: 481px) {
#container {
position: relative;
}
body {
background-color: #F5F5FF;
font-size: 15px;
line-height: 21px;
}
pre, tt, code {
line-height: 18px;
}
p, ul, ol {
margin: 0 0 15px;
}
#jump_to {
padding: 5px 10px;
}
#jump_wrapper {
padding: 0;
}
#jump_to, #jump_page {
font: 10px Arial;
text-transform: uppercase;
}
#jump_page .source {
padding: 5px 10px;
}
#jump_to a.large {
display: inline-block;
}
#jump_to a.small {
display: none;
}
#background {
position: absolute;
top: 0; bottom: 0;
width: 350px;
background: #fff;
border-right: 1px solid #e5e5ee;
z-index: -1;
}
ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
padding-left: 40px;
}
ul.sections > li {
white-space: nowrap;
}
ul.sections > li > div {
display: inline-block;
}
ul.sections > li > div.annotation {
max-width: 350px;
min-width: 350px;
min-height: 5px;
padding: 13px;
overflow-x: hidden;
white-space: normal;
vertical-align: top;
text-align: left;
}
ul.sections > li > div.annotation pre {
margin: 15px 0 15px;
padding-left: 15px;
}
ul.sections > li > div.content {
padding: 13px;
vertical-align: top;
background: #f5f5ff;
border: none;
-webkit-box-shadow: none;
box-shadow: none;
}
.pilwrap {
position: relative;
display: inline;
}
.pilcrow {
font: 12px Arial;
text-decoration: none;
color: #454545;
position: absolute;
top: 3px; left: -20px;
padding: 1px 2px;
opacity: 0;
-webkit-transition: opacity 0.2s linear;
}
.for-h1 .pilcrow {
top: 47px;
}
.for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
top: 35px;
}
ul.sections > li > div.annotation:hover .pilcrow {
opacity: 1;
}
}
/*---------------------- (> 1025px) ---------------------*/
@media only screen and (min-width: 1025px) {
body {
font-size: 16px;
line-height: 24px;
}
#background {
width: 525px;
}
ul.sections > li > div.annotation {
max-width: 525px;
min-width: 525px;
padding: 10px 25px 1px 50px;
}
ul.sections > li > div.content {
padding: 9px 15px 16px 25px;
}
}
/*---------------------- Syntax Highlighting -----------------------------*/
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code {
display: block; padding: 0.5em;
color: #000;
background: #f8f8ff
}
pre .comment,
pre .template_comment,
pre .diff .header,
pre .javadoc {
color: #408080;
font-style: italic
}
pre .keyword,
pre .assignment,
pre .literal,
pre .css .rule .keyword,
pre .winutils,
pre .javascript .title,
pre .lisp .title,
pre .subst {
color: #954121;
/*font-weight: bold*/
}
pre .number,
pre .hexcolor {
color: #40a070
}
pre .string,
pre .tag .value,
pre .phpdoc,
pre .tex .formula {
color: #219161;
}
pre .title,
pre .id {
color: #19469D;
}
pre .params {
color: #00F;
}
pre .javascript .title,
pre .lisp .title,
pre .subst {
font-weight: normal
}
pre .class .title,
pre .haskell .label,
pre .tex .command {
color: #458;
font-weight: bold
}
pre .tag,
pre .tag .title,
pre .rules .property,
pre .django .tag .keyword {
color: #000080;
font-weight: normal
}
pre .attribute,
pre .variable,
pre .instancevar,
pre .lisp .body {
color: #008080
}
pre .regexp {
color: #B68
}
pre .class {
color: #458;
font-weight: bold
}
pre .symbol,
pre .ruby .symbol .string,
pre .ruby .symbol .keyword,
pre .ruby .symbol .keymethods,
pre .lisp .keyword,
pre .tex .special,
pre .input_number {
color: #990073
}
pre .builtin,
pre .constructor,
pre .built_in,
pre .lisp .title {
color: #0086b3
}
pre .preprocessor,
pre .pi,
pre .doctype,
pre .shebang,
pre .cdata {
color: #999;
font-weight: bold
}
pre .deletion {
background: #fdd
}
pre .addition {
background: #dfd
}
pre .diff .change {
background: #0086b3
}
pre .chunk {
color: #aaa
}
pre .tex .formula {
opacity: 0.5;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,375 @@
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

BIN
img/dash-logo.png 100755

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
img/main-logo.png 100755

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

21
js/codemirror.js 100755

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,490 @@
// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ F R E E B O A R D │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright © 2013 Jim Heising (https://github.com/jheising) │ \\
// │ Copyright © 2013 Bug Labs, Inc. (http://buglabs.net) │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT license. │ \\
// └────────────────────────────────────────────────────────────────────┘ \\
(function()
{
var jsonDatasource = function(settings, updateCallback)
{
var self = this;
var updateTimer = null;
var currentSettings = settings;
function updateRefresh(refreshTime)
{
if(updateTimer)
{
clearInterval(updateTimer);
}
updateTimer = setInterval(function()
{
self.updateNow();
}, refreshTime);
}
updateRefresh(currentSettings.refresh * 1000);
this.updateNow = function()
{
$.ajax({
url : currentSettings.url,
dataType : (currentSettings.is_jsonp) ? "JSONP" : "JSON",
beforeSend: function(xhr)
{
try
{
_.each(currentSettings.headers, function(header)
{
var name = header.name;
var value = header.value;
if(!_.isUndefined(name) && !_.isUndefined(value))
{
xhr.setRequestHeader(name, value);
}
});
}
catch(e)
{
}
},
success : function(data)
{
updateCallback(data);
},
error : function(xhr, status, error)
{
}
});
}
this.onDispose = function()
{
clearInterval(updateTimer);
updateTimer = null;
}
this.onSettingsChanged = function(newSettings)
{
currentSettings = newSettings;
updateRefresh(currentSettings.refresh * 1000);
}
};
freeboard.loadDatasourcePlugin({
type_name : "JSON",
settings : [
{
name : "url",
display_name: "URL",
type : "text"
},
{
name : "refresh",
display_name : "Refresh Every",
type : "number",
suffix : "seconds",
default_value: 5
},
{
name : "is_jsonp",
display_name: "Is JSONP",
type : "boolean"
},
{
name : "headers",
display_name: "Headers",
type : "array",
settings : [
{
name : "name",
display_name: "Name",
type : "text"
},
{
name : "value",
display_name: "Value",
type : "text"
}
]
}
],
newInstance: function(settings, newInstanceCallback, updateCallback)
{
newInstanceCallback( new jsonDatasource(settings, updateCallback));
}
});
var openWeatherMapDatasource = function(settings, updateCallback)
{
var self = this;
var updateTimer = null;
var currentSettings = settings;
function updateRefresh(refreshTime)
{
if(updateTimer)
{
clearInterval(updateTimer);
}
updateTimer = setInterval(function()
{
self.updateNow();
}, refreshTime);
}
function toTitleCase(str)
{
return str.replace(/\w\S*/g, function(txt)
{
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
updateRefresh(currentSettings.refresh * 1000);
this.updateNow = function()
{
$.ajax({
url : "http://api.openweathermap.org/data/2.5/weather?q=" + encodeURIComponent(currentSettings.location) + "&units=" + currentSettings.units,
dataType : "JSONP",
success : function(data)
{
// Rejigger our data into something easier to understand
var newData = {
place_name : data.name,
sunrise : (new Date(data.sys.sunrise * 1000)).toLocaleTimeString(),
sunset : (new Date(data.sys.sunset * 1000)).toLocaleTimeString(),
conditions : toTitleCase(data.weather[0].description),
current_temp : data.main.temp,
high_temp : data.main.temp_max,
low_temp : data.main.temp_min,
pressure : data.main.pressure,
humidity : data.main.humidity,
wind_speed : data.wind.speed,
wind_direction : data.wind.deg
};
updateCallback(newData);
},
error : function(xhr, status, error)
{
}
});
}
this.onDispose = function()
{
clearInterval(updateTimer);
updateTimer = null;
}
this.onSettingsChanged = function(newSettings)
{
currentSettings = newSettings;
self.updateNow();
updateRefresh(currentSettings.refresh * 1000);
}
};
freeboard.loadDatasourcePlugin({
type_name : "openweathermap",
display_name: "Open Weather Map API",
settings : [
{
name : "location",
display_name: "Location",
type : "text",
description : "Example: London, UK"
},
{
name : "units",
display_name: "Units",
type : "option",
default : "imperial",
options: [
{
name : "Imperial",
value: "imperial"
},
{
name : "Metric",
value: "metric"
}
]
},
{
name : "refresh",
display_name : "Refresh Every",
type : "number",
suffix : "seconds",
default_value: 5
}
],
newInstance: function(settings, newInstanceCallback, updateCallback)
{
newInstanceCallback( new openWeatherMapDatasource(settings, updateCallback));
}
});
var dweetioDatasource = function(settings, updateCallback)
{
var self = this;
var currentSettings = settings;
function onNewDweet(dweet)
{
updateCallback(dweet);
}
this.updateNow = function()
{
dweetio.get_latest_dweet_for(currentSettings.thing_id, function(err, dweet){
if(err)
{
//onNewDweet({});
}
else
{
onNewDweet(dweet[0].content);
}
});
}
this.onDispose = function()
{
}
this.onSettingsChanged = function(newSettings)
{
dweetio.stop_listening();
currentSettings = newSettings;
dweetio.listen_for(currentSettings.thing_id, function(dweet)
{
onNewDweet(dweet.content);
});
}
self.onSettingsChanged(settings);
};
freeboard.loadDatasourcePlugin({
"type_name" : "dweet_io",
"display_name": "Dweet.io",
"external_scripts" : [
"http://dweet.io/client/dweet.io.min.js"
],
"settings" : [
{
name : "thing_id",
display_name: "Thing Name",
"description": "Example: salty-dog-1",
type : "text"
}
],
newInstance : function(settings, newInstanceCallback, updateCallback)
{
newInstanceCallback(new dweetioDatasource(settings, updateCallback));
}
});
var playbackDatasource = function(settings, updateCallback)
{
var self = this;
var currentSettings = settings;
var currentDataset = [];
var currentIndex = 0;
var currentTimeout;
function moveNext()
{
if(currentDataset.length > 0)
{
if(currentIndex < currentDataset.length)
{
updateCallback(currentDataset[currentIndex]);
currentIndex++;
}
if(currentIndex >= currentDataset.length && currentSettings.loop)
{
currentIndex = 0;
}
if(currentIndex < currentDataset.length)
{
currentTimeout = setTimeout(moveNext, currentSettings.refresh * 1000);
}
}
else
{
updateCallback({});
}
}
function stopTimeout()
{
currentDataset = [];
currentIndex = 0;
if(currentTimeout)
{
clearTimeout(currentTimeout);
currentTimeout = null;
}
}
this.updateNow = function()
{
stopTimeout();
$.ajax({
url : currentSettings.datafile,
dataType : (currentSettings.is_jsonp) ? "JSONP" : "JSON",
success : function(data)
{
if(_.isArray(data))
{
currentDataset = data;
}
else
{
currentDataset = [];
}
currentIndex = 0;
moveNext();
},
error : function(xhr, status, error)
{
}
});
}
this.onDispose = function()
{
stopTimeout();
}
this.onSettingsChanged = function(newSettings)
{
currentSettings = newSettings;
self.updateNow();
}
};
freeboard.loadDatasourcePlugin({
"type_name": "playback",
"display_name": "Playback",
"settings": [
{
"name": "datafile",
"display_name": "Data File URL",
"type": "text",
"description": "A link to a JSON array of data."
},
{
name : "is_jsonp",
display_name: "Is JSONP",
type : "boolean"
},
{
"name": "loop",
"display_name": "Loop",
"type": "boolean",
"description": "Rewind and loop when finished"
},
{
"name": "refresh",
"display_name": "Refresh Every",
"type": "number",
"suffix": "seconds",
"default_value": 5
}
],
newInstance: function(settings, newInstanceCallback, updateCallback)
{
newInstanceCallback( new playbackDatasource(settings, updateCallback));
}
});
var clockDatasource = function(settings, updateCallback)
{
var self = this;
var currentSettings = settings;
var timer;
function stopTimer()
{
if(timer)
{
clearTimeout(timer);
timer = null;
}
}
function updateTimer()
{
stopTimer();
timer = setInterval(self.updateNow, currentSettings.refresh * 1000);
}
this.updateNow = function()
{
var date = new Date();
var data = {
numeric_value : date.getTime(),
full_string_value : date.toLocaleString(),
date_string_value : date.toLocaleDateString(),
time_string_value : date.toLocaleTimeString(),
date_object : date
};
updateCallback(data);
}
this.onDispose = function()
{
stopTimer();
}
this.onSettingsChanged = function(newSettings)
{
currentSettings = newSettings;
updateTimer();
}
updateTimer();
};
freeboard.loadDatasourcePlugin({
"type_name": "clock",
"display_name": "Clock",
"settings": [
{
"name": "refresh",
"display_name": "Refresh Every",
"type": "number",
"suffix": "seconds",
"default_value": 1
}
],
newInstance: function(settings, newInstanceCallback, updateCallback)
{
newInstanceCallback( new clockDatasource(settings, updateCallback));
}
});
}());

View File

@ -0,0 +1,861 @@
// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ F R E E B O A R D │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright © 2013 Jim Heising (https://github.com/jheising) │ \\
// │ Copyright © 2013 Bug Labs, Inc. (http://buglabs.net) │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT license. │ \\
// └────────────────────────────────────────────────────────────────────┘ \\
(function () {
var SPARKLINE_HISTORY_LENGTH = 100;
var valueStyle = freeboard.getStyleString("values");
valueStyle +=
"overflow: hidden;" +
"text-overflow: ellipsis;" +
"display: inline;";
// Add some styles to our sheet
freeboard.addStyle('.text-widget-unit', 'padding-left: 5px;display:inline;');
freeboard.addStyle('.text-widget-regular-value', valueStyle + "font-size:30px;");
freeboard.addStyle('.text-widget-big-value', valueStyle + "font-size:75px;");
freeboard.addStyle('.gauge-widget-wrapper', "width: 100%;text-align: center;");
freeboard.addStyle('.gauge-widget', "width:200px;height:160px;display:inline-block;");
freeboard.addStyle('.sparkline', "width:100%;height: 75px;");
freeboard.addStyle('.sparkline-inline', "width:50%;float:right;height:30px;");
freeboard.addStyle('.indicator-light', "border-radius:50%;width:22px;height:22px;border:2px solid #3d3d3d;margin-top:5px;float:left;background-color:#222;margin-right:10px;");
freeboard.addStyle('.indicator-light.on', "background-color:#FFC773;box-shadow: 0px 0px 15px #FF9900;border-color:#FDF1DF;");
freeboard.addStyle('.indicator-text', "margin-top:10px;");
freeboard.addStyle('div.pointer-value', "position:absolute;height:95px;margin: auto;top: 0px;bottom: 0px;width: 100%;text-align:center;");
function easeTransitionText(currentValue, newValue, textElement, duration) {
if (currentValue == newValue)
return;
if ($.isNumeric(newValue) && $.isNumeric(currentValue)) {
var numParts = newValue.toString().split('.');
var endingPrecision = 0;
if (numParts.length > 1) {
endingPrecision = numParts[1].length;
}
numParts = currentValue.toString().split('.');
var startingPrecision = 0;
if (numParts.length > 1) {
startingPrecision = numParts[1].length;
}
jQuery({transitionValue: Number(currentValue), precisionValue: startingPrecision}).animate({transitionValue: Number(newValue), precisionValue: endingPrecision}, {
duration: duration,
step: function () {
$(textElement).text(this.transitionValue.toFixed(this.precisionValue));
},
done: function () {
$(textElement).text(newValue);
}
});
}
else {
$(textElement).text(newValue);
}
}
function addValueToSparkline(element, value) {
var values = $(element).data().values;
if (!values) {
values = [];
}
if (values.length >= SPARKLINE_HISTORY_LENGTH) {
values.shift();
}
//if(_.isNumber(value))
//{
values.push(Number(value));
$(element).data().values = values;
$(element).sparkline(values, {
type: "line",
height: "100%",
width: "100%",
fillColor: false,
lineColor: "#FF9900",
lineWidth: 2,
spotRadius: 3,
spotColor: false,
minSpotColor: "#78AB49",
maxSpotColor: "#78AB49",
highlightSpotColor: "#9D3926",
highlightLineColor: "#9D3926"
});
//}
}
var textWidget = function (settings) {
var self = this;
var currentSettings = settings;
var titleElement = $('<h2 class="section-title"></h2>');
var valueElement = $('<div></div>');
var unitsElement = $('<div class="text-widget-unit"></div>');
var sparklineElement = $('<span class="sparkline-inline"></span>');
var currentValue;
this.render = function (element) {
$(element).append(titleElement).append(valueElement).append(unitsElement).append(sparklineElement);
}
this.onSettingsChanged = function (newSettings) {
currentSettings = newSettings;
titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title));
valueElement
.toggleClass("text-widget-regular-value", (newSettings.size == "regular"))
.toggleClass("text-widget-big-value", (newSettings.size == "big"));
unitsElement.html((_.isUndefined(newSettings.units) ? "" : newSettings.units));
if (newSettings.sparkline) {
sparklineElement.show();
}
else {
delete sparklineElement.data().values;
sparklineElement.empty();
sparklineElement.hide();
}
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (settingName == "value") {
if (currentSettings.animate) {
easeTransitionText(currentValue, newValue, valueElement, 500);
}
else {
valueElement.text(newValue);
}
if (currentSettings.sparkline) {
addValueToSparkline(sparklineElement, newValue);
}
currentValue = newValue;
}
}
this.onDispose = function () {
}
this.getHeight = function () {
if (currentSettings.size == "big") {
return 2;
}
else {
return 1;
}
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "text_widget",
display_name: "Text",
settings: [
{
name: "title",
display_name: "Title",
type: "text"
},
{
name: "size",
display_name: "Size",
type: "option",
options: [
{
name: "Regular",
value: "regular"
},
{
name: "Big",
value: "big"
}
]
},
{
name: "value",
display_name: "Value",
type: "calculated"
},
{
name: "sparkline",
display_name: "Include Sparkline",
type: "boolean"
},
{
name: "animate",
display_name: "Animate Value Changes",
type: "boolean",
default_value: true
},
{
name: "units",
display_name: "Units",
type: "text"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new textWidget(settings));
}
});
var gaugeID = 0;
var gaugeWidget = function (settings) {
var self = this;
var thisGaugeID = "gauge-" + gaugeID++;
var titleElement = $('<h2 class="section-title"></h2>');
var gaugeElement = $('<div class="gauge-widget" id="' + thisGaugeID + '"></div>');
var gaugeObject;
var rendered = false;
var currentSettings = settings;
function createGauge() {
if (!rendered) {
return;
}
gaugeElement.empty();
gaugeObject = new JustGage({
id: thisGaugeID,
value: (_.isUndefined(currentSettings.min_value) ? 0 : currentSettings.min_value),
min: (_.isUndefined(currentSettings.min_value) ? 0 : currentSettings.min_value),
max: (_.isUndefined(currentSettings.max_value) ? 0 : currentSettings.max_value),
label: currentSettings.units,
showInnerShadow: false,
valueFontColor: "#d3d4d4"
});
}
this.render = function (element) {
rendered = true;
$(element).append(titleElement).append($('<div class="gauge-widget-wrapper"></div>').append(gaugeElement));
createGauge();
}
this.onSettingsChanged = function (newSettings) {
if (newSettings.min_value != currentSettings.min_value || newSettings.max_value != currentSettings.max_value || newSettings.units != currentSettings.units) {
currentSettings = newSettings;
createGauge();
}
else {
currentSettings = newSettings;
}
titleElement.html(newSettings.title);
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (!_.isUndefined(gaugeObject)) {
gaugeObject.refresh(Number(newValue));
}
}
this.onDispose = function () {
}
this.getHeight = function () {
return 3;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "gauge",
display_name: "Gauge",
settings: [
{
name: "title",
display_name: "Title",
type: "text"
},
{
name: "value",
display_name: "Value",
type: "calculated"
},
{
name: "units",
display_name: "Units",
type: "text"
},
{
name: "min_value",
display_name: "Minimum",
type: "text",
default_value: 0
},
{
name: "max_value",
display_name: "Maximum",
type: "text",
default_value: 100
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new gaugeWidget(settings));
}
});
var sparklineWidget = function (settings) {
var self = this;
var titleElement = $('<h2 class="section-title"></h2>');
var sparklineElement = $('<div class="sparkline"></div>');
this.render = function (element) {
$(element).append(titleElement).append(sparklineElement);
}
this.onSettingsChanged = function (newSettings) {
titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title));
}
this.onCalculatedValueChanged = function (settingName, newValue) {
addValueToSparkline(sparklineElement, newValue);
}
this.onDispose = function () {
}
this.getHeight = function () {
return 2;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "sparkline",
display_name: "Sparkline",
settings: [
{
name: "title",
display_name: "Title",
type: "text"
},
{
name: "value",
display_name: "Value",
type: "calculated"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new sparklineWidget(settings));
}
});
var pointerWidget = function (settings) {
var self = this;
var paper;
var strokeWidth = 3;
var triangle;
var width, height;
var currentValue = 0;
var valueDiv = $('<div class="text-widget-big-value"></div>');
var unitsDiv = $('<div></div>');
function polygonPath(points) {
if (!points || points.length < 2)
return [];
var path = []; //will use path object type
path.push(['m', points[0], points[1]]);
for (var i = 2; i < points.length; i += 2) {
path.push(['l', points[i], points[i + 1]]);
}
path.push(['z']);
return path;
}
this.render = function (element) {
width = $(element).width();
height = $(element).height();
var radius = Math.min(width, height) / 2 - strokeWidth * 2;
paper = Raphael($(element).get()[0], width, height);
var circle = paper.circle(width / 2, height / 2, radius);
circle.attr("stroke", "#FF9900");
circle.attr("stroke-width", strokeWidth);
triangle = paper.path(polygonPath([width / 2, (height / 2) - radius + strokeWidth, 15, 20, -30, 0]));
triangle.attr("stroke-width", 0);
triangle.attr("fill", "#fff");
$(element).append($('<div class="pointer-value"></div>').append(valueDiv).append(unitsDiv));
}
this.onSettingsChanged = function (newSettings) {
unitsDiv.html(newSettings.units);
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (settingName == "direction") {
if (!_.isUndefined(triangle)) {
var direction = "r";
var oppositeCurrent = currentValue + 180;
if (oppositeCurrent < newValue) {
//direction = "l";
}
triangle.animate({transform: "r" + newValue + "," + (width / 2) + "," + (height / 2)}, 250, "bounce");
}
currentValue = newValue;
}
else if (settingName == "value_text") {
valueDiv.html(newValue);
}
}
this.onDispose = function () {
}
this.getHeight = function () {
return 4;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "pointer",
display_name: "Pointer",
settings: [
{
name: "direction",
display_name: "Direction",
type: "calculated",
description: "In degrees"
},
{
name: "value_text",
display_name: "Value Text",
type: "calculated"
},
{
name: "units",
display_name: "Units",
type: "text"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new pointerWidget(settings));
}
});
var pictureWidget = function(settings)
{
var self = this;
var widgetElement;
var timer;
var imageURL;
function stopTimer()
{
if(timer)
{
clearInterval(timer);
timer = null;
}
}
function updateImage()
{
if(widgetElement && imageURL)
{
var cacheBreakerURL = imageURL + (imageURL.indexOf("?") == -1 ? "?" : "&") + Date.now();
$(widgetElement).css({
"background-image" : "url(" + cacheBreakerURL + ")"
});
}
}
this.render = function(element)
{
$(element).css({
width : "100%",
height: "100%",
"background-size" : "cover",
"background-position" : "center"
});
widgetElement = element;
}
this.onSettingsChanged = function(newSettings)
{
stopTimer();
if(newSettings.refresh && newSettings.refresh > 0)
{
timer = setInterval(updateImage, Number(newSettings.refresh) * 1000);
}
}
this.onCalculatedValueChanged = function(settingName, newValue)
{
if(settingName == "src")
{
imageURL = newValue;
}
updateImage();
}
this.onDispose = function()
{
stopTimer();
}
this.getHeight = function()
{
return 4;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "picture",
display_name: "Picture",
fill_size: true,
settings: [
{
name: "src",
display_name: "Image URL",
type: "calculated"
},
{
"type": "number",
"display_name": "Refresh every",
"name": "refresh",
"suffix": "seconds",
"description":"Leave blank if the image doesn't need to be refreshed"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new pictureWidget(settings));
}
});
var indicatorWidget = function (settings) {
var self = this;
var titleElement = $('<h2 class="section-title"></h2>');
var stateElement = $('<div class="indicator-text"></div>');
var indicatorElement = $('<div class="indicator-light"></div>');
var currentSettings = settings;
var isOn = false;
function updateState() {
indicatorElement.toggleClass("on", isOn);
if (isOn) {
stateElement.text((_.isUndefined(currentSettings.on_text) ? "" : currentSettings.on_text));
}
else {
stateElement.text((_.isUndefined(currentSettings.off_text) ? "" : currentSettings.off_text));
}
}
this.render = function (element) {
$(element).append(titleElement).append(indicatorElement).append(stateElement);
}
this.onSettingsChanged = function (newSettings) {
currentSettings = newSettings;
titleElement.html((_.isUndefined(newSettings.title) ? "" : newSettings.title));
updateState();
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (settingName == "value") {
isOn = Boolean(newValue);
}
updateState();
}
this.onDispose = function () {
}
this.getHeight = function () {
return 1;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "indicator",
display_name: "Indicator Light",
settings: [
{
name: "title",
display_name: "Title",
type: "text"
},
{
name: "value",
display_name: "Value",
type: "calculated"
},
{
name: "on_text",
display_name: "On Text",
type: "calculated"
},
{
name: "off_text",
display_name: "Off Text",
type: "calculated"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new indicatorWidget(settings));
}
});
freeboard.addStyle('.gm-style-cc a', "text-shadow:none;");
var googleMapWidget = function (settings) {
var self = this;
var currentSettings = settings;
var map;
var marker;
var currentPosition = {};
function updatePosition() {
if (map && marker && currentPosition.lat && currentPosition.lon) {
var newLatLon = new google.maps.LatLng(currentPosition.lat, currentPosition.lon);
marker.setPosition(newLatLon);
map.panTo(newLatLon);
}
}
this.render = function (element) {
function initializeMap() {
var mapOptions = {
zoom: 13,
center: new google.maps.LatLng(37.235, -115.811111),
disableDefaultUI: true,
draggable: false,
styles: [
{"featureType": "water", "elementType": "geometry", "stylers": [
{"color": "#2a2a2a"}
]},
{"featureType": "landscape", "elementType": "geometry", "stylers": [
{"color": "#000000"},
{"lightness": 20}
]},
{"featureType": "road.highway", "elementType": "geometry.fill", "stylers": [
{"color": "#000000"},
{"lightness": 17}
]},
{"featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [
{"color": "#000000"},
{"lightness": 29},
{"weight": 0.2}
]},
{"featureType": "road.arterial", "elementType": "geometry", "stylers": [
{"color": "#000000"},
{"lightness": 18}
]},
{"featureType": "road.local", "elementType": "geometry", "stylers": [
{"color": "#000000"},
{"lightness": 16}
]},
{"featureType": "poi", "elementType": "geometry", "stylers": [
{"color": "#000000"},
{"lightness": 21}
]},
{"elementType": "labels.text.stroke", "stylers": [
{"visibility": "on"},
{"color": "#000000"},
{"lightness": 16}
]},
{"elementType": "labels.text.fill", "stylers": [
{"saturation": 36},
{"color": "#000000"},
{"lightness": 40}
]},
{"elementType": "labels.icon", "stylers": [
{"visibility": "off"}
]},
{"featureType": "transit", "elementType": "geometry", "stylers": [
{"color": "#000000"},
{"lightness": 19}
]},
{"featureType": "administrative", "elementType": "geometry.fill", "stylers": [
{"color": "#000000"},
{"lightness": 20}
]},
{"featureType": "administrative", "elementType": "geometry.stroke", "stylers": [
{"color": "#000000"},
{"lightness": 17},
{"weight": 1.2}
]}
]
};
map = new google.maps.Map(element, mapOptions);
google.maps.event.addDomListener(element, 'mouseenter', function (e) {
e.cancelBubble = true;
if (!map.hover) {
map.hover = true;
map.setOptions({zoomControl: true});
}
});
google.maps.event.addDomListener(element, 'mouseleave', function (e) {
if (map.hover) {
map.setOptions({zoomControl: false});
map.hover = false;
}
});
marker = new google.maps.Marker({map: map});
updatePosition();
}
if (window.google && window.google.maps) {
initializeMap();
}
else {
window.gmap_initialize = initializeMap;
head.js("https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&callback=gmap_initialize");
}
}
this.onSettingsChanged = function (newSettings) {
currentSettings = newSettings;
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (settingName == "lat") {
currentPosition.lat = newValue;
}
else if (settingName == "lon") {
currentPosition.lon = newValue;
}
updatePosition();
}
this.onDispose = function () {
}
this.getHeight = function () {
return 4;
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
type_name: "google_map",
display_name: "Google Map",
fill_size: true,
settings: [
{
name: "lat",
display_name: "Latitude",
type: "calculated"
},
{
name: "lon",
display_name: "Longitude",
type: "calculated"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new googleMapWidget(settings));
}
});
freeboard.addStyle('.html-widget', "white-space:normal;width:100%;height:100%");
var htmlWidget = function (settings) {
var self = this;
var htmlElement = $('<div class="html-widget"></div>');
var currentSettings = settings;
this.render = function (element) {
$(element).append(htmlElement);
}
this.onSettingsChanged = function (newSettings) {
currentSettings = newSettings;
}
this.onCalculatedValueChanged = function (settingName, newValue) {
if (settingName == "html") {
htmlElement.html(newValue);
}
}
this.onDispose = function () {
}
this.getHeight = function () {
return Number(currentSettings.height);
}
this.onSettingsChanged(settings);
};
freeboard.loadWidgetPlugin({
"type_name": "html",
"display_name": "HTML",
"fill_size": true,
"settings": [
{
"name": "html",
"display_name": "HTML",
"type": "calculated",
"description": "Can be literal HTML, or javascript that outputs HTML."
},
{
"name": "height",
"display_name": "Height Blocks",
"type": "number",
"default_value": 4,
"description": "A height block is around 60 pixels"
}
],
newInstance: function (settings, newInstanceCallback) {
newInstanceCallback(new htmlWidget(settings));
}
});
}());

View File

@ -0,0 +1,108 @@
var pubnubDatasource = function (settings, updateCallback) {
var self = this;
var currentSettings = {};
var pubnub;
this.onPubNubHistory = function (envelope) {
var messages = envelope[0];
for (var idx in messages) {
self.onPubNubMessage(messages[idx]);
}
}
this.onPubNubMessage = function (message) {
if (_.isString(message)) {
// Parse as JSON if it's a string
try {
var messageObject = JSON.parse(message);
if (messageObject) {
updateCallback(messageObject);
}
}
catch (e) {
}
}
else
{
updateCallback(message);
}
}
this.changeChannel = function (newChannel) {
try {
pubnub.unsubscribe({
channel: currentSettings["channel"]
});
}
catch (e) {
}
if (newChannel) {
pubnub.subscribe({
channel: newChannel,
message: self.onPubNubMessage
})
}
}
this.closeConnection = function () {
self.changeChannel(null);
// TODO: Find out if there is a close function for pubnub. There isn't one documented.
pubnub = null;
}
this.updateNow = function () {
pubnub.history({
channel: currentSettings["channel"],
callback: self.onPubNubHistory
})
}
this.onDispose = function () {
self.closeConnection();
}
this.onSettingsChanged = function (newSettings) {
if (newSettings["subscribe_key"] !== currentSettings["subscribe_key"]) {
pubnub = PUBNUB.init({
subscribe_key: newSettings["subscribe_key"],
ssl: true
});
}
if (newSettings["channel"] !== currentSettings["channel"]) {
self.changeChannel(newSettings["channel"]);
}
currentSettings = newSettings;
}
self.onSettingsChanged(settings);
};
freeboard.loadDatasourcePlugin({
"type_name": "pubnub",
"display_name": "PubNub",
"external_scripts": [
"https://cdn.pubnub.com/pubnub.min.js"
],
"settings": [
{
"name": "subscribe_key",
"display_name": "Subscribe Key",
"type": "text"
},
{
"name": "channel",
"display_name": "Channel",
"type": "text"
},
],
newInstance: function (settings, newInstanceCallback, updateCallback) {
newInstanceCallback(new pubnubDatasource(settings, updateCallback));
}
});

9
js/head.js 100755
View File

@ -0,0 +1,9 @@
/*! head.core - v1.0.2 */
(function(n,t){"use strict";function r(n){a[a.length]=n}function k(n){var t=new RegExp(" ?\\b"+n+"\\b");c.className=c.className.replace(t,"")}function p(n,t){for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}function tt(){var t,e,f,o;c.className=c.className.replace(/ (w-|eq-|gt-|gte-|lt-|lte-|portrait|no-portrait|landscape|no-landscape)\d+/g,"");t=n.innerWidth||c.clientWidth;e=n.outerWidth||n.screen.width;u.screen.innerWidth=t;u.screen.outerWidth=e;r("w-"+t);p(i.screens,function(n){t>n?(i.screensCss.gt&&r("gt-"+n),i.screensCss.gte&&r("gte-"+n)):t<n?(i.screensCss.lt&&r("lt-"+n),i.screensCss.lte&&r("lte-"+n)):t===n&&(i.screensCss.lte&&r("lte-"+n),i.screensCss.eq&&r("e-q"+n),i.screensCss.gte&&r("gte-"+n))});f=n.innerHeight||c.clientHeight;o=n.outerHeight||n.screen.height;u.screen.innerHeight=f;u.screen.outerHeight=o;u.feature("portrait",f>t);u.feature("landscape",f<t)}function it(){n.clearTimeout(b);b=n.setTimeout(tt,50)}var y=n.document,rt=n.navigator,ut=n.location,c=y.documentElement,a=[],i={screens:[240,320,480,640,768,800,1024,1280,1440,1680,1920],screensCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!1},browsers:[{ie:{min:6,max:11}}],browserCss:{gt:!0,gte:!1,lt:!0,lte:!1,eq:!0},html5:!0,page:"-page",section:"-section",head:"head"},v,u,s,w,o,h,l,d,f,g,nt,e,b;if(n.head_conf)for(v in n.head_conf)n.head_conf[v]!==t&&(i[v]=n.head_conf[v]);u=n[i.head]=function(){u.ready.apply(null,arguments)};u.feature=function(n,t,i){return n?(Object.prototype.toString.call(t)==="[object Function]"&&(t=t.call()),r((t?"":"no-")+n),u[n]=!!t,i||(k("no-"+n),k(n),u.feature()),u):(c.className+=" "+a.join(" "),a=[],u)};u.feature("js",!0);s=rt.userAgent.toLowerCase();w=/mobile|android|kindle|silk|midp|phone|(windows .+arm|touch)/.test(s);u.feature("mobile",w,!0);u.feature("desktop",!w,!0);s=/(chrome|firefox)[ \/]([\w.]+)/.exec(s)||/(iphone|ipad|ipod)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(android)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(webkit|opera)(?:.*version)?[ \/]([\w.]+)/.exec(s)||/(msie) ([\w.]+)/.exec(s)||/(trident).+rv:(\w.)+/.exec(s)||[];o=s[1];h=parseFloat(s[2]);switch(o){case"msie":case"trident":o="ie";h=y.documentMode||h;break;case"firefox":o="ff";break;case"ipod":case"ipad":case"iphone":o="ios";break;case"webkit":o="safari"}for(u.browser={name:o,version:h},u.browser[o]=!0,l=0,d=i.browsers.length;l<d;l++)for(f in i.browsers[l])if(o===f)for(r(f),g=i.browsers[l][f].min,nt=i.browsers[l][f].max,e=g;e<=nt;e++)h>e?(i.browserCss.gt&&r("gt-"+f+e),i.browserCss.gte&&r("gte-"+f+e)):h<e?(i.browserCss.lt&&r("lt-"+f+e),i.browserCss.lte&&r("lte-"+f+e)):h===e&&(i.browserCss.lte&&r("lte-"+f+e),i.browserCss.eq&&r("eq-"+f+e),i.browserCss.gte&&r("gte-"+f+e));else r("no-"+f);r(o);r(o+parseInt(h,10));i.html5&&o==="ie"&&h<9&&p("abbr|article|aside|audio|canvas|details|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|progress|section|summary|time|video".split("|"),function(n){y.createElement(n)});p(ut.pathname.split("/"),function(n,u){if(this.length>2&&this[u+1]!==t)u&&r(this.slice(u,u+1).join("-").toLowerCase()+i.section);else{var f=n||"index",e=f.indexOf(".");e>0&&(f=f.substring(0,e));c.id=f.toLowerCase()+i.page;u||r("root"+i.section)}});u.screen={height:n.screen.height,width:n.screen.width};tt();b=0;n.addEventListener?n.addEventListener("resize",it,!1):n.attachEvent("onresize",it)})(window);
/*! head.css3 - v1.0.0 */
(function(n,t){"use strict";function a(n){for(var r in n)if(i[n[r]]!==t)return!0;return!1}function r(n){var t=n.charAt(0).toUpperCase()+n.substr(1),i=(n+" "+c.join(t+" ")+t).split(" ");return!!a(i)}var h=n.document,o=h.createElement("i"),i=o.style,s=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),c="Webkit Moz O ms Khtml".split(" "),l=n.head_conf&&n.head_conf.head||"head",u=n[l],f={gradient:function(){var n="background-image:";return i.cssText=(n+s.join("gradient(linear,left top,right bottom,from(#9f9),to(#fff));"+n)+s.join("linear-gradient(left top,#eee,#fff);"+n)).slice(0,-n.length),!!i.backgroundImage},rgba:function(){return i.cssText="background-color:rgba(0,0,0,0.5)",!!i.backgroundColor},opacity:function(){return o.style.opacity===""},textshadow:function(){return i.textShadow===""},multiplebgs:function(){i.cssText="background:url(https://),url(https://),red url(https://)";var n=(i.background||"").match(/url/g);return Object.prototype.toString.call(n)==="[object Array]"&&n.length===3},boxshadow:function(){return r("boxShadow")},borderimage:function(){return r("borderImage")},borderradius:function(){return r("borderRadius")},cssreflections:function(){return r("boxReflect")},csstransforms:function(){return r("transform")},csstransitions:function(){return r("transition")},touch:function(){return"ontouchstart"in n},retina:function(){return n.devicePixelRatio>1},fontface:function(){var t=u.browser.name,n=u.browser.version;switch(t){case"ie":return n>=9;case"chrome":return n>=13;case"ff":return n>=6;case"ios":return n>=5;case"android":return!1;case"webkit":return n>=5.1;case"opera":return n>=10;default:return!1}}};for(var e in f)f[e]&&u.feature(e,f[e].call(),!0);u.feature()})(window);
/*! head.load - v1.0.3 */
(function(n,t){"use strict";function w(){}function u(n,t){if(n){typeof n=="object"&&(n=[].slice.call(n));for(var i=0,r=n.length;i<r;i++)t.call(n,n[i],i)}}function it(n,i){var r=Object.prototype.toString.call(i).slice(8,-1);return i!==t&&i!==null&&r===n}function s(n){return it("Function",n)}function a(n){return it("Array",n)}function et(n){var i=n.split("/"),t=i[i.length-1],r=t.indexOf("?");return r!==-1?t.substring(0,r):t}function f(n){(n=n||w,n._done)||(n(),n._done=1)}function ot(n,t,r,u){var f=typeof n=="object"?n:{test:n,success:!t?!1:a(t)?t:[t],failure:!r?!1:a(r)?r:[r],callback:u||w},e=!!f.test;return e&&!!f.success?(f.success.push(f.callback),i.load.apply(null,f.success)):e||!f.failure?u():(f.failure.push(f.callback),i.load.apply(null,f.failure)),i}function v(n){var t={},i,r;if(typeof n=="object")for(i in n)!n[i]||(t={name:i,url:n[i]});else t={name:et(n),url:n};return(r=c[t.name],r&&r.url===t.url)?r:(c[t.name]=t,t)}function y(n){n=n||c;for(var t in n)if(n.hasOwnProperty(t)&&n[t].state!==l)return!1;return!0}function st(n){n.state=ft;u(n.onpreload,function(n){n.call()})}function ht(n){n.state===t&&(n.state=nt,n.onpreload=[],rt({url:n.url,type:"cache"},function(){st(n)}))}function ct(){var n=arguments,t=n[n.length-1],r=[].slice.call(n,1),f=r[0];return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(f?(u(r,function(n){s(n)||!n||ht(v(n))}),b(v(n[0]),s(f)?f:function(){i.load.apply(null,r)})):b(v(n[0])),i)}function lt(){var n=arguments,t=n[n.length-1],r={};return(s(t)||(t=null),a(n[0]))?(n[0].push(t),i.load.apply(null,n[0]),i):(u(n,function(n){n!==t&&(n=v(n),r[n.name]=n)}),u(n,function(n){n!==t&&(n=v(n),b(n,function(){y(r)&&f(t)}))}),i)}function b(n,t){if(t=t||w,n.state===l){t();return}if(n.state===tt){i.ready(n.name,t);return}if(n.state===nt){n.onpreload.push(function(){b(n,t)});return}n.state=tt;rt(n,function(){n.state=l;t();u(h[n.name],function(n){f(n)});o&&y()&&u(h.ALL,function(n){f(n)})})}function at(n){n=n||"";var t=n.split("?")[0].split(".");return t[t.length-1].toLowerCase()}function rt(t,i){function e(t){t=t||n.event;u.onload=u.onreadystatechange=u.onerror=null;i()}function o(f){f=f||n.event;(f.type==="load"||/loaded|complete/.test(u.readyState)&&(!r.documentMode||r.documentMode<9))&&(n.clearTimeout(t.errorTimeout),n.clearTimeout(t.cssTimeout),u.onload=u.onreadystatechange=u.onerror=null,i())}function s(){if(t.state!==l&&t.cssRetries<=20){for(var i=0,f=r.styleSheets.length;i<f;i++)if(r.styleSheets[i].href===u.href){o({type:"load"});return}t.cssRetries++;t.cssTimeout=n.setTimeout(s,250)}}var u,h,f;i=i||w;h=at(t.url);h==="css"?(u=r.createElement("link"),u.type="text/"+(t.type||"css"),u.rel="stylesheet",u.href=t.url,t.cssRetries=0,t.cssTimeout=n.setTimeout(s,500)):(u=r.createElement("script"),u.type="text/"+(t.type||"javascript"),u.src=t.url);u.onload=u.onreadystatechange=o;u.onerror=e;u.async=!1;u.defer=!1;t.errorTimeout=n.setTimeout(function(){e({type:"timeout"})},7e3);f=r.head||r.getElementsByTagName("head")[0];f.insertBefore(u,f.lastChild)}function vt(){for(var t,u=r.getElementsByTagName("script"),n=0,f=u.length;n<f;n++)if(t=u[n].getAttribute("data-headjs-load"),!!t){i.load(t);return}}function yt(n,t){var v,p,e;return n===r?(o?f(t):d.push(t),i):(s(n)&&(t=n,n="ALL"),a(n))?(v={},u(n,function(n){v[n]=c[n];i.ready(n,function(){y(v)&&f(t)})}),i):typeof n!="string"||!s(t)?i:(p=c[n],p&&p.state===l||n==="ALL"&&y()&&o)?(f(t),i):(e=h[n],e?e.push(t):e=h[n]=[t],i)}function e(){if(!r.body){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(e,50);return}o||(o=!0,vt(),u(d,function(n){f(n)}))}function k(){r.addEventListener?(r.removeEventListener("DOMContentLoaded",k,!1),e()):r.readyState==="complete"&&(r.detachEvent("onreadystatechange",k),e())}var r=n.document,d=[],h={},c={},ut="async"in r.createElement("script")||"MozAppearance"in r.documentElement.style||n.opera,o,g=n.head_conf&&n.head_conf.head||"head",i=n[g]=n[g]||function(){i.ready.apply(null,arguments)},nt=1,ft=2,tt=3,l=4,p;if(r.readyState==="complete")e();else if(r.addEventListener)r.addEventListener("DOMContentLoaded",k,!1),n.addEventListener("load",e,!1);else{r.attachEvent("onreadystatechange",k);n.attachEvent("onload",e);p=!1;try{p=!n.frameElement&&r.documentElement}catch(wt){}p&&p.doScroll&&function pt(){if(!o){try{p.doScroll("left")}catch(t){n.clearTimeout(i.readyTimeout);i.readyTimeout=n.setTimeout(pt,50);return}e()}}()}i.load=i.js=ut?lt:ct;i.test=ot;i.ready=yt;i.ready(r,function(){y()&&u(h.ALL,function(n){f(n)});i.feature&&i.feature("domloaded",!0)})})(window);
/*
//# sourceMappingURL=head.min.js.map
*/

15003
js/jquery-ui.js vendored 100755

File diff suppressed because it is too large Load Diff

139
js/jquery.caret.js 100755
View File

@ -0,0 +1,139 @@
(function($)
{
$.fn.insertAtCaret = function(text, opts)
{
var element = $(this).get(0);
if(document.selection)
{
element.focus();
var orig = element.value.replace(/\r\n/g, "\n");
var range = document.selection.createRange();
if(range.parentElement() != element)
{
return false;
}
range.text = text;
var actual = tmp = element.value.replace(/\r\n/g, "\n");
for(var diff = 0; diff < orig.length; diff++)
{
if(orig.charAt(diff) != actual.charAt(diff)) break;
}
for(var index = 0, start = 0; tmp.match(text) && (tmp = tmp.replace(text, "")) && index <= diff; index = start + text.length)
{
start = actual.indexOf(text, index);
}
}
else if(element.selectionStart)
{
var start = element.selectionStart;
var end = element.selectionEnd;
element.value = element.value.substr(0, start) + text + element.value.substr(end, element.value.length);
}
if(start)
{
setCaretTo(element, start + text.length);
}
else
{
element.value = text + element.value;
}
$(this).change();
return this;
}
$.fn.replaceTextAt = function(start, end, replacementText)
{
var element = $(this).get(0);
var wholeString = $(this).val();
var prefix = wholeString.substr(0, start);
var suffix = wholeString.substr(end);
$(this).val(prefix + replacementText + suffix);
var newCursorPosition = prefix.length + replacementText.length;
setCaretTo(element, newCursorPosition);
$(this).change();
return this;
}
$.fn.setCaretPosition = function(start, end)
{
var element = $(this).get(0);
element.focus();
setCaretTo(element, start, end);
return this;
}
$.fn.getCaretPosition = function()
{
var element = $(this).get(0);
$(element).focus();
return getCaretPosition(element);
}
$.fn.getSelectedText = function()
{
var element = $(this).get(0);
// workaround for firefox because window.getSelection does not work inside inputs
if(typeof element.selectionStart == 'number')
{
return $(element).val().substr(element.selectionStart, element.selectionEnd - element.selectionStart);
}
else if(document.getSelection)
{
return document.getSelection();
}
else if(window.getSelection)
{
return window.getSelection();
}
}
// privates
function setCaretTo(element, start, end)
{
if(element.createTextRange)
{
var range = element.createTextRange();
range.moveStart('character', start);
range.moveEnd('character', (end || start));
range.select();
}
else if(element.selectionStart)
{
element.focus();
element.setSelectionRange(start, (end || start));
}
}
function getCaretPosition(element)
{
if(typeof element.selectionStart == 'number')
{
return element.selectionStart;
}
else if(document.selection)
{
var range = document.selection.createRange();
var rangeLength = range.text.length;
range.moveStart('character', -element.value.length);
return range.text.length - rangeLength;
}
}
})(jQuery);

3868
js/jquery.gridster.js 100755

File diff suppressed because it is too large Load Diff

6
js/jquery.js vendored 100755

File diff suppressed because one or more lines are too long

5
js/jquery.sparkline.min.js vendored 100755

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,491 @@
/**
* JustGage - a handy JavaScript plugin for generating and animating nice & clean dashboard gauges.
* Copyright (c) 2012 Bojan Djuricic - pindjur(at)gmail(dot)com | http://www.madcog.com
* Licensed under MIT.
* Date: 31/07/2012
* @author Bojan Djuricic (@Toorshia)
* @version 1.0
*
* http://www.justgage.com
*/
JustGage = function(config)
{
if(!config.id)
{
alert("Missing id parameter for gauge!");
return false;
}
if(!document.getElementById(config.id))
{
alert("No element with id: \"" + config.id + "\" found!");
return false;
}
// configurable parameters
this.config = {
// id : string
// this is container element id
id : config.id,
// value : int
// value gauge is showing
value : (config.value) ? config.value : 0,
// valueFontColor : string
// color of label showing current value
valueFontColor : (config.valueFontColor) ? config.valueFontColor : "#010101",
// min : int
// min value
min : (config.min) ? config.min : 0,
// max : int
// max value
max : (config.max) ? config.max : 100,
// showMinMax : bool
// hide or display min and max values
showMinMax : (config.showMinMax != null) ? config.showMinMax : true,
// gaugeWidthScale : float
// width of the gauge element
gaugeWidthScale : (config.gaugeWidthScale) ? config.gaugeWidthScale : 1.0,
// gaugeColor : string
// background color of gauge element
gaugeColor : (config.gaugeColor) ? config.gaugeColor : "#edebeb",
// label : string
// text to show below value
label : (config.label) ? config.label : "",
// showInnerShadow : bool
// give gauge element small amount of inner shadow
showInnerShadow : (config.showInnerShadow != null) ? config.showInnerShadow : true,
// shadowOpacity : int
// 0 ~ 1
shadowOpacity : (config.shadowOpacity) ? config.shadowOpacity : 0.2,
// shadowSize: int
// inner shadow size
shadowSize : (config.shadowSize) ? config.shadowSize : 5,
// shadowVerticalOffset : int
// how much shadow is offset from top
shadowVerticalOffset: (config.shadowVerticalOffset) ? config.shadowVerticalOffset : 3,
// levelColors : string[]
// colors of indicator, from lower to upper, in RGB format
levelColors : (config.levelColors) ? config.levelColors : percentColors,
// levelColorsGradient : bool
// whether to use gradual color change for value, or sector-based
levelColorsGradient : (config.levelColorsGradient != null) ? config.levelColorsGradient : true,
// labelFontColor : string
// color of label showing label under value
labelFontColor : (config.labelFontColor) ? config.labelFontColor : "#b3b3b3",
// startAnimationTime : int
// length of initial animation
startAnimationTime : (config.startAnimationTime) ? config.startAnimationTime : 700,
// startAnimationType : string
// type of initial animation (linear, >, <, <>, bounce)
startAnimationType : (config.startAnimationType) ? config.startAnimationType : ">",
// refreshAnimationTime : int
// length of refresh animation
refreshAnimationTime: (config.refreshAnimationTime) ? config.refreshAnimationTime : 700,
// refreshAnimationType : string
// type of refresh animation (linear, >, <, <>, bounce)
refreshAnimationType: (config.refreshAnimationType) ? config.refreshAnimationType : ">"
};
// overflow values
if(config.value > this.config.max) this.config.value = this.config.max;
if(config.value < this.config.min) this.config.value = this.config.min;
this.originalValue = config.value;
// canvas
this.canvas = Raphael(this.config.id, "100%", "100%");
// canvas dimensions
//var canvasW = document.getElementById(this.config.id).clientWidth;
//var canvasH = document.getElementById(this.config.id).clientHeight;
var canvasW = getStyle(document.getElementById(this.config.id), "width").slice(0, -2) * 1;
var canvasH = getStyle(document.getElementById(this.config.id), "height").slice(0, -2) * 1;
// widget dimensions
var widgetW, widgetH;
if((canvasW / canvasH) > 1.25)
{
widgetW = 1.25 * canvasH;
widgetH = canvasH;
}
else
{
widgetW = canvasW;
widgetH = canvasW / 1.25;
}
// delta
var dx = (canvasW - widgetW) / 2;
var dy = (canvasH - widgetH) / 2;
// value
var valueFontSize = ((widgetH / 6.4) > 16) ? (widgetH / 6.4) : 16;
var valueX = dx + widgetW / 2;
var valueY = dy + widgetH / 1.4;
// label
var labelFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var labelX = dx + widgetW / 2;
//var labelY = dy + widgetH / 1.126760563380282;
var labelY = valueY + valueFontSize / 2 + 6;
// min
var minFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2;
var minY = dy + widgetH / 1.126760563380282;
// max
var maxFontSize = ((widgetH / 16) > 10) ? (widgetH / 16) : 10;
var maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * this.config.gaugeWidthScale) / 2;
var maxY = dy + widgetH / 1.126760563380282;
// parameters
this.params = {
canvasW : canvasW,
canvasH : canvasH,
widgetW : widgetW,
widgetH : widgetH,
dx : dx,
dy : dy,
valueFontSize: valueFontSize,
valueX : valueX,
valueY : valueY,
labelFontSize: labelFontSize,
labelX : labelX,
labelY : labelY,
minFontSize : minFontSize,
minX : minX,
minY : minY,
maxFontSize : maxFontSize,
maxX : maxX,
maxY : maxY
};
// pki - custom attribute for generating gauge paths
this.canvas.customAttributes.pki = function(value, min, max, w, h, dx, dy, gws)
{
var alpha = (1 - (value - min) / (max - min)) * Math.PI , Ro = w / 2 - w / 10, Ri = Ro - w / 6.666666666666667 * gws,
Cx = w / 2 + dx, Cy = h / 1.25 + dy,
Xo = w / 2 + dx + Ro * Math.cos(alpha), Yo = h - (h - Cy) + dy - Ro * Math.sin(alpha), Xi = w / 2 + dx + Ri * Math.cos(alpha), Yi = h - (h - Cy) + dy - Ri * Math.sin(alpha), path;
path += "M" + (Cx - Ri) + "," + Cy + " ";
path += "L" + (Cx - Ro) + "," + Cy + " ";
path += "A" + Ro + "," + Ro + " 0 0,1 " + Xo + "," + Yo + " ";
path += "L" + Xi + "," + Yi + " ";
path += "A" + Ri + "," + Ri + " 0 0,0 " + (Cx - Ri) + "," + Cy + " ";
path += "z ";
return { path: path };
}
// gauge
this.gauge = this.canvas.path().attr({
"stroke": "none",
"fill" : this.config.gaugeColor,
pki : [this.config.max, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH,
this.params.dx, this.params.dy, this.config.gaugeWidthScale]
});
this.gauge.id = this.config.id + "-gauge";
// level
this.level = this.canvas.path().attr({
"stroke": "none",
"fill" : getColorForPercentage((this.config.value - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient),
pki : [this.config.min, this.config.min, this.config.max, this.params.widgetW, this.params.widgetH,
this.params.dx, this.params.dy, this.config.gaugeWidthScale]
});
this.level.id = this.config.id + "-level";
// value
this.txtValue = this.canvas.text(this.params.valueX, this.params.valueY, this.originalValue);
this.txtValue.attr({
"font-size" : this.params.valueFontSize,
"font-weight" : "bold",
"font-family" : "Arial",
"fill" : this.config.valueFontColor,
"fill-opacity": "0"
});
this.txtValue.id = this.config.id + "-txtvalue";
// label
this.txtLabel = this.canvas.text(this.params.labelX, this.params.labelY, this.config.label);
this.txtLabel.attr({
"font-size" : this.params.labelFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": "0"
});
this.txtLabel.id = this.config.id + "-txtlabel";
// min
this.txtMin = this.canvas.text(this.params.minX, this.params.minY, this.config.min);
this.txtMin.attr({
"font-size" : this.params.minFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": (this.config.showMinMax == true) ? "1" : "0"
});
this.txtMin.id = this.config.id + "-txtmin";
// max
this.txtMax = this.canvas.text(this.params.maxX, this.params.maxY, this.config.max);
this.txtMax.attr({
"font-size" : this.params.maxFontSize,
"font-weight" : "normal",
"font-family" : "Arial",
"fill" : this.config.labelFontColor,
"fill-opacity": (this.config.showMinMax == true) ? "1" : "0"
});
this.txtMax.id = this.config.id + "-txtmax";
var defs = this.canvas.canvas.childNodes[1];
var svg = "http://www.w3.org/2000/svg";
if(ie < 9)
{
onCreateElementNsReady(function()
{
this.generateShadow();
});
}
else
{
this.generateShadow(svg, defs);
}
// animate
this.level.animate({pki: [this.config.value, this.config.min, this.config.max, this.params.widgetW,
this.params.widgetH, this.params.dx, this.params.dy, this.config.gaugeWidthScale
]}, this.config.startAnimationTime, this.config.startAnimationType);
this.txtValue.animate({"fill-opacity": "1"}, this.config.startAnimationTime, this.config.startAnimationType);
this.txtLabel.animate({"fill-opacity": "1"}, this.config.startAnimationTime, this.config.startAnimationType);
};
// refresh gauge level
JustGage.prototype.refresh = function(val)
{
// overflow values
originalVal = val;
if(val > this.config.max)
{
val = this.config.max;
}
if(val < this.config.min)
{
val = this.config.min;
}
var color = getColorForPercentage((val - this.config.min) / (this.config.max - this.config.min), this.config.levelColors, this.config.levelColorsGradient);
this.canvas.getById(this.config.id + "-txtvalue").attr({"text": originalVal});
this.canvas.getById(this.config.id + "-level").animate({pki : [val,
this.config.min,
this.config.max,
this.params.widgetW,
this.params.widgetH,
this.params.dx,
this.params.dy,
this.config.gaugeWidthScale
], "fill" : color}, this.config.refreshAnimationTime, this.config.refreshAnimationType);
};
var percentColors = [
"#a9d70b", "#f9c802", "#ff0000"
]
JustGage.prototype.generateShadow = function(svg, defs)
{
// FILTER
var gaussFilter = document.createElementNS(svg, "filter");
gaussFilter.setAttribute("id", this.config.id + "-inner-shadow");
defs.appendChild(gaussFilter);
// offset
var feOffset = document.createElementNS(svg, "feOffset");
feOffset.setAttribute("dx", 0);
feOffset.setAttribute("dy", this.config.shadowVerticalOffset);
gaussFilter.appendChild(feOffset);
// blur
var feGaussianBlur = document.createElementNS(svg, "feGaussianBlur");
feGaussianBlur.setAttribute("result", "offset-blur");
feGaussianBlur.setAttribute("stdDeviation", this.config.shadowSize);
gaussFilter.appendChild(feGaussianBlur);
// composite 1
var feComposite1 = document.createElementNS(svg, "feComposite");
feComposite1.setAttribute("operator", "out");
feComposite1.setAttribute("in", "SourceGraphic");
feComposite1.setAttribute("in2", "offset-blur");
feComposite1.setAttribute("result", "inverse");
gaussFilter.appendChild(feComposite1);
// flood
var feFlood = document.createElementNS(svg, "feFlood");
feFlood.setAttribute("flood-color", "black");
feFlood.setAttribute("flood-opacity", this.config.shadowOpacity);
feFlood.setAttribute("result", "color");
gaussFilter.appendChild(feFlood);
// composite 2
var feComposite2 = document.createElementNS(svg, "feComposite");
feComposite2.setAttribute("operator", "in");
feComposite2.setAttribute("in", "color");
feComposite2.setAttribute("in2", "inverse");
feComposite2.setAttribute("result", "shadow");
gaussFilter.appendChild(feComposite2);
// composite 3
var feComposite3 = document.createElementNS(svg, "feComposite");
feComposite3.setAttribute("operator", "over");
feComposite3.setAttribute("in", "shadow");
feComposite3.setAttribute("in2", "SourceGraphic");
gaussFilter.appendChild(feComposite3);
// set shadow
if(this.config.showInnerShadow == true)
{
this.canvas.canvas.childNodes[2].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
this.canvas.canvas.childNodes[3].setAttribute("filter", "url(#" + this.config.id + "-inner-shadow)");
}
}
var getColorForPercentage = function(pct, col, grad)
{
var no = col.length;
if(no === 1) return col[0];
var inc = (grad) ? (1 / (no - 1)) : (1 / no);
var colors = new Array();
for(var i = 0; i < col.length; i++)
{
var percentage = (grad) ? (inc * i) : (inc * (i + 1));
var rval = parseInt((cutHex(col[i])).substring(0, 2), 16);
var gval = parseInt((cutHex(col[i])).substring(2, 4), 16);
var bval = parseInt((cutHex(col[i])).substring(4, 6), 16);
colors[i] = { pct: percentage, color: { r: rval, g: gval, b: bval } };
}
if(pct == 0) return 'rgb(' + [colors[0].color.r, colors[0].color.g, colors[0].color.b].join(',') + ')';
for(var i = 0; i < colors.length; i++)
{
if(pct <= colors[i].pct)
{
if(grad == true)
{
var lower = colors[i - 1];
var upper = colors[i];
var range = upper.pct - lower.pct;
var rangePct = (pct - lower.pct) / range;
var pctLower = 1 - rangePct;
var pctUpper = rangePct;
var color = {
r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper),
g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper),
b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper)
};
return 'rgb(' + [color.r, color.g, color.b].join(',') + ')';
}
else
{
return 'rgb(' + [colors[i].color.r, colors[i].color.g, colors[i].color.b].join(',') + ')';
}
}
}
}
function getRandomInt(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function cutHex(str)
{
return (str.charAt(0) == "#") ? str.substring(1, 7) : str
}
function getStyle(oElm, strCssRule)
{
var strValue = "";
if(document.defaultView && document.defaultView.getComputedStyle)
{
strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
}
else if(oElm.currentStyle)
{
strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1)
{
return p1.toUpperCase();
});
strValue = oElm.currentStyle[strCssRule];
}
return strValue;
}
function onCreateElementNsReady(func)
{
if(document.createElementNS != undefined)
{
func();
}
else
{
setTimeout(function()
{
onCreateElementNsReady(func);
}, 100);
}
}
// ----------------------------------------------------------
// A short snippet for detecting versions of IE in JavaScript
// without resorting to user-agent sniffing
// ----------------------------------------------------------
// If you're not in IE (or IE version is less than 5) then:
// ie === undefined
// If you're in IE (>=5) then you can determine which version:
// ie === 7; // IE7
// Thus, to detect IE:
// if (ie) {}
// And to detect the version:
// ie === 6 // IE6
// ie > 7 // IE8, IE9 ...
// ie < 9 // Anything less than IE9
// ----------------------------------------------------------
// UPDATE: Now using Live NodeList idea from @jdalton
var ie = (function()
{
var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i');
while(div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', all[0]);
return v > 4 ? v : undef;
}());

94
js/knockout.js 100755
View File

@ -0,0 +1,94 @@
// Knockout JavaScript library v3.0.0
// (c) Steven Sanderson - http://knockoutjs.com/
// License: MIT (http://www.opensource.org/licenses/mit-license.php)
(function() {(function(q){var y=this||(0,eval)("this"),w=y.document,K=y.navigator,u=y.jQuery,B=y.JSON;(function(q){"function"===typeof require&&"object"===typeof exports&&"object"===typeof module?q(module.exports||exports):"function"===typeof define&&define.amd?define(["exports"],q):q(y.ko={})})(function(F){function G(a,c){return null===a||typeof a in N?a===c:!1}function H(b,c,d,e){a.d[b]={init:function(b){a.a.f.set(b,L,{});return{controlsDescendantBindings:!0}},update:function(b,h,k,m,f){k=a.a.f.get(b,L);h=a.a.c(h());
m=!d!==!h;var p=!k.ob;if(p||c||m!==k.Db)p&&(k.ob=a.a.Ya(a.e.childNodes(b),!0)),m?(p||a.e.S(b,a.a.Ya(k.ob)),a.Ta(e?e(f,h):f,b)):a.e.Z(b),k.Db=m}};a.g.Y[b]=!1;a.e.P[b]=!0}var a="undefined"!==typeof F?F:{};a.b=function(b,c){for(var d=b.split("."),e=a,g=0;g<d.length-1;g++)e=e[d[g]];e[d[d.length-1]]=c};a.s=function(a,c,d){a[c]=d};a.version="3.0.0";a.b("version",a.version);a.a=function(){function b(a,b){for(var f in a)a.hasOwnProperty(f)&&b(f,a[f])}function c(k,b){if("input"!==a.a.v(k)||!k.type||"click"!=
b.toLowerCase())return!1;var f=k.type;return"checkbox"==f||"radio"==f}var d={},e={};d[K&&/Firefox\/2/i.test(K.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];d.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(d,function(a,b){if(b.length)for(var f=0,c=b.length;f<c;f++)e[b[f]]=a});var g={propertychange:!0},h=w&&function(){for(var a=3,b=w.createElement("div"),f=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+
++a+"]><i></i><![endif]--\x3e",f[0];);return 4<a?a:q}();return{$a:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],n:function(a,b){for(var f=0,c=a.length;f<c;f++)b(a[f])},l:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var f=0,c=a.length;f<c;f++)if(a[f]===b)return f;return-1},Ua:function(a,b,f){for(var c=0,d=a.length;c<d;c++)if(b.call(f,a[c]))return a[c];return null},ia:function(b,c){var f=a.a.l(b,c);0<=f&&b.splice(f,1)},Va:function(b){b=
b||[];for(var c=[],f=0,d=b.length;f<d;f++)0>a.a.l(c,b[f])&&c.push(b[f]);return c},ha:function(a,b){a=a||[];for(var f=[],c=0,d=a.length;c<d;c++)f.push(b(a[c]));return f},ga:function(a,b){a=a||[];for(var f=[],c=0,d=a.length;c<d;c++)b(a[c])&&f.push(a[c]);return f},X:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var f=0,c=b.length;f<c;f++)a.push(b[f]);return a},V:function(b,c,f){var d=a.a.l(a.a.Ha(b),c);0>d?f&&b.push(c):f||b.splice(d,1)},extend:function(a,b){if(b)for(var f in b)b.hasOwnProperty(f)&&
(a[f]=b[f]);return a},K:b,Da:function(a,b){if(!a)return a;var f={},c;for(c in a)a.hasOwnProperty(c)&&(f[c]=b(a[c],c,a));return f},wa:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Vb:function(b){b=a.a.Q(b);for(var c=w.createElement("div"),f=0,d=b.length;f<d;f++)c.appendChild(a.L(b[f]));return c},Ya:function(b,c){for(var f=0,d=b.length,e=[];f<d;f++){var g=b[f].cloneNode(!0);e.push(c?a.L(g):g)}return e},S:function(b,c){a.a.wa(b);if(c)for(var f=0,d=c.length;f<d;f++)b.appendChild(c[f])},nb:function(b,
c){var f=b.nodeType?[b]:b;if(0<f.length){for(var d=f[0],e=d.parentNode,g=0,n=c.length;g<n;g++)e.insertBefore(c[g],d);g=0;for(n=f.length;g<n;g++)a.removeNode(f[g])}},$:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);if(1<a.length){var f=a[0],c=a[a.length-1];for(a.length=0;f!==c;)if(a.push(f),f=f.nextSibling,!f)return;a.push(c)}}return a},qb:function(a,b){7>h?a.setAttribute("selected",b):a.selected=b},la:function(a){return null===a||a===
q?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},ec:function(b,c){for(var f=[],d=(b||"").split(c),e=0,g=d.length;e<g;e++){var n=a.a.la(d[e]);""!==n&&f.push(n)}return f},ac:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Gb:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;
return!!a},va:function(b){return a.a.Gb(b,b.ownerDocument.documentElement)},Ra:function(b){return!!a.a.Ua(b,a.a.va)},v:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},r:function(b,d,f){var e=h&&g[d];if(e||"undefined"==typeof u)if(e||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var s=function(a){f.call(b,a)},l="on"+d;b.attachEvent(l,s);a.a.C.ea(b,function(){b.detachEvent(l,s)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(d,
f,!1);else{if(c(b,d)){var n=f;f=function(a,b){var f=this.checked;b&&(this.checked=!0!==b.Ab);n.call(this,a);this.checked=f}}u(b).bind(d,f)}},da:function(a,b){if(!a||!a.nodeType)throw Error("element must be a DOM node when calling triggerEvent");if("undefined"!=typeof u){var f=[];c(a,b)&&f.push({Ab:a.checked});u(a).trigger(b,f)}else if("function"==typeof w.createEvent)if("function"==typeof a.dispatchEvent)f=w.createEvent(e[b]||"HTMLEvents"),f.initEvent(b,!0,!0,y,0,0,0,0,0,!1,!1,!1,!1,0,a),a.dispatchEvent(f);
else throw Error("The supplied element doesn't support dispatchEvent");else if("undefined"!=typeof a.fireEvent)c(a,b)&&(a.checked=!0!==a.checked),a.fireEvent("on"+b);else throw Error("Browser doesn't support triggering events");},c:function(b){return a.M(b)?b():b},Ha:function(b){return a.M(b)?b.t():b},ma:function(b,c,f){if(c){var d=/\S+/g,e=b.className.match(d)||[];a.a.n(c.match(d),function(b){a.a.V(e,b,f)});b.className=e.join(" ")}},Ma:function(b,c){var f=a.a.c(c);if(null===f||f===q)f="";var d=a.e.firstChild(b);
!d||3!=d.nodeType||a.e.nextSibling(d)?a.e.S(b,[w.createTextNode(f)]):d.data=f;a.a.Jb(b)},pb:function(a,b){a.name=b;if(7>=h)try{a.mergeAttributes(w.createElement("<input name='"+a.name+"'/>"),!1)}catch(f){}},Jb:function(a){9<=h&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Hb:function(a){if(h){var b=a.style.width;a.style.width=0;a.style.width=b}},Zb:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var f=[],d=b;d<=c;d++)f.push(d);return f},Q:function(a){for(var b=[],c=0,d=a.length;c<
d;c++)b.push(a[c]);return b},cc:6===h,dc:7===h,ja:h,ab:function(b,c){for(var f=a.a.Q(b.getElementsByTagName("input")).concat(a.a.Q(b.getElementsByTagName("textarea"))),d="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},e=[],g=f.length-1;0<=g;g--)d(f[g])&&e.push(f[g]);return e},Wb:function(b){return"string"==typeof b&&(b=a.a.la(b))?B&&B.parse?B.parse(b):(new Function("return "+b))():null},Na:function(b,c,f){if(!B||!B.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return B.stringify(a.a.c(b),c,f)},Xb:function(c,d,f){f=f||{};var e=f.params||{},g=f.includeFields||this.$a,h=c;if("object"==typeof c&&"form"===a.a.v(c))for(var h=c.action,n=g.length-1;0<=n;n--)for(var r=a.a.ab(c,g[n]),v=r.length-1;0<=v;v--)e[r[v].name]=r[v].value;d=a.a.c(d);var t=w.createElement("form");t.style.display="none";t.action=h;t.method="post";for(var E in d)c=w.createElement("input"),c.name=E,c.value=a.a.Na(a.a.c(d[E])),t.appendChild(c);b(e,function(a,b){var c=w.createElement("input");c.name=
a;c.value=b;t.appendChild(c)});w.body.appendChild(t);f.submitter?f.submitter(t):t.submit();setTimeout(function(){t.parentNode.removeChild(t)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.n);a.b("utils.arrayFirst",a.a.Ua);a.b("utils.arrayFilter",a.a.ga);a.b("utils.arrayGetDistinctValues",a.a.Va);a.b("utils.arrayIndexOf",a.a.l);a.b("utils.arrayMap",a.a.ha);a.b("utils.arrayPushAll",a.a.X);a.b("utils.arrayRemoveItem",a.a.ia);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
a.a.$a);a.b("utils.getFormFields",a.a.ab);a.b("utils.peekObservable",a.a.Ha);a.b("utils.postJson",a.a.Xb);a.b("utils.parseJson",a.a.Wb);a.b("utils.registerEventHandler",a.a.r);a.b("utils.stringifyJson",a.a.Na);a.b("utils.range",a.a.Zb);a.b("utils.toggleDomNodeCssClass",a.a.ma);a.b("utils.triggerEvent",a.a.da);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.K);a.b("utils.addOrRemoveItem",a.a.V);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=function(a){var c=
this,d=Array.prototype.slice.call(arguments);a=d.shift();return function(){return c.apply(a,d.concat(Array.prototype.slice.call(arguments)))}});a.a.f=new function(){function a(b,h){var k=b[d];if(!k||"null"===k||!e[k]){if(!h)return q;k=b[d]="ko"+c++;e[k]={}}return e[k]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===q?q:e[d]},set:function(c,d,e){if(e!==q||a(c,!1)!==q)a(c,!0)[d]=e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},D:function(){return c++ +
d}}};a.b("utils.domData",a.a.f);a.b("utils.domData.clear",a.a.f.clear);a.a.C=new function(){function b(b,c){var e=a.a.f.get(b,d);e===q&&c&&(e=[],a.a.f.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),m=0;m<e.length;m++)e[m](d);a.a.f.clear(d);"function"==typeof u&&"function"==typeof u.cleanData&&u.cleanData([d]);if(g[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.f.D(),e={1:!0,8:!0,9:!0},g={1:!0,9:!0};return{ea:function(a,c){if("function"!=
typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},mb:function(c,e){var g=b(c,!1);g&&(a.a.ia(g,e),0==g.length&&a.a.f.set(c,d,q))},L:function(b){if(e[b.nodeType]&&(c(b),g[b.nodeType])){var d=[];a.a.X(d,b.getElementsByTagName("*"));for(var m=0,f=d.length;m<f;m++)c(d[m])}return b},removeNode:function(b){a.L(b);b.parentNode&&b.parentNode.removeChild(b)}}};a.L=a.a.C.L;a.removeNode=a.a.C.removeNode;a.b("cleanNode",a.L);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.C);
a.b("utils.domNodeDisposal.addDisposeCallback",a.a.C.ea);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.C.mb);(function(){a.a.Fa=function(b){var c;if("undefined"!=typeof u)if(u.parseHTML)c=u.parseHTML(b)||[];else{if((c=u.clean([b]))&&c[0]){for(b=c[0];b.parentNode&&11!==b.parentNode.nodeType;)b=b.parentNode;b.parentNode&&b.parentNode.removeChild(b)}}else{var d=a.a.la(b).toLowerCase();c=w.createElement("div");d=d.match(/^<(thead|tbody|tfoot)/)&&[1,"<table>","</table>"]||!d.indexOf("<tr")&&[2,
"<table><tbody>","</tbody></table>"]||(!d.indexOf("<td")||!d.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||[0,"",""];b="ignored<div>"+d[1]+b+d[2]+"</div>";for("function"==typeof y.innerShiv?c.appendChild(y.innerShiv(b)):c.innerHTML=b;d[0]--;)c=c.lastChild;c=a.a.Q(c.lastChild.childNodes)}return c};a.a.Ka=function(b,c){a.a.wa(b);c=a.a.c(c);if(null!==c&&c!==q)if("string"!=typeof c&&(c=c.toString()),"undefined"!=typeof u)u(b).html(c);else for(var d=a.a.Fa(c),e=0;e<d.length;e++)b.appendChild(d[e])}})();
a.b("utils.parseHtmlFragment",a.a.Fa);a.b("utils.setHtml",a.a.Ka);a.u=function(){function b(c,e){if(c)if(8==c.nodeType){var g=a.u.jb(c.nodeValue);null!=g&&e.push({Fb:c,Tb:g})}else if(1==c.nodeType)for(var g=0,h=c.childNodes,k=h.length;g<k;g++)b(h[g],e)}var c={};return{Ca:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);
c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},ub:function(a,b){var g=c[a];if(g===q)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return g.apply(null,b||[]),!0}finally{delete c[a]}},vb:function(c,e){var g=[];b(c,g);for(var h=0,k=g.length;h<k;h++){var m=g[h].Fb,f=[m];e&&a.a.X(f,e);a.u.ub(g[h].Tb,f);m.nodeValue="";m.parentNode&&m.parentNode.removeChild(m)}},jb:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.u);a.b("memoization.memoize",
a.u.Ca);a.b("memoization.unmemoize",a.u.ub);a.b("memoization.parseMemoText",a.u.jb);a.b("memoization.unmemoizeDomNodeAndDescendants",a.u.vb);a.xa={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.h({read:b,write:function(a){clearTimeout(d);d=setTimeout(function(){b(a)},c)}})},notify:function(a,c){a.equalityComparer="always"==c?null:G}};var N={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.xa);a.sb=function(b,c,d){this.target=b;this.qa=c;this.Eb=d;a.s(this,"dispose",
this.B)};a.sb.prototype.B=function(){this.Qb=!0;this.Eb()};a.ca=function(){this.F={};a.a.extend(this,a.ca.fn);a.s(this,"subscribe",this.T);a.s(this,"extend",this.extend);a.s(this,"getSubscriptionsCount",this.Lb)};var I="change";a.ca.fn={T:function(b,c,d){d=d||I;var e=new a.sb(this,c?b.bind(c):b,function(){a.a.ia(this.F[d],e)}.bind(this));this.F[d]||(this.F[d]=[]);this.F[d].push(e);return e},notifySubscribers:function(b,c){c=c||I;if(this.cb(c))try{a.i.Wa();for(var d=this.F[c].slice(0),e=0,g;g=d[e];++e)g&&
!0!==g.Qb&&g.qa(b)}finally{a.i.end()}},cb:function(a){return this.F[a]&&this.F[a].length},Lb:function(){var b=0;a.a.K(this.F,function(a,d){b+=d.length});return b},extend:function(b){var c=this;b&&a.a.K(b,function(b,e){var g=a.xa[b];"function"==typeof g&&(c=g(c,e)||c)});return c}};a.fb=function(a){return null!=a&&"function"==typeof a.T&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.ca);a.b("isSubscribable",a.fb);a.i=function(){var b=[];return{Wa:function(a){b.push(a&&{qa:a,Za:[]})},
end:function(){b.pop()},lb:function(c){if(!a.fb(c))throw Error("Only subscribable things can act as dependencies");if(0<b.length){var d=b[b.length-1];!d||0<=a.a.l(d.Za,c)||(d.Za.push(c),d.qa(c))}},p:function(a,d,e){try{return b.push(null),a.apply(d,e||[])}finally{b.pop()}}}}();a.q=function(b){function c(){if(0<arguments.length)return c.equalityComparer&&c.equalityComparer(d,arguments[0])||(c.O(),d=arguments[0],c.N()),this;a.i.lb(c);return d}var d=b;a.ca.call(c);c.t=function(){return d};c.N=function(){c.notifySubscribers(d)};
c.O=function(){c.notifySubscribers(d,"beforeChange")};a.a.extend(c,a.q.fn);a.s(c,"peek",c.t);a.s(c,"valueHasMutated",c.N);a.s(c,"valueWillMutate",c.O);return c};a.q.fn={equalityComparer:G};var C=a.q.Yb="__ko_proto__";a.q.fn[C]=a.q;a.ya=function(b,c){return null===b||b===q||b[C]===q?!1:b[C]===c?!0:a.ya(b[C],c)};a.M=function(b){return a.ya(b,a.q)};a.gb=function(b){return"function"==typeof b&&b[C]===a.q||"function"==typeof b&&b[C]===a.h&&b.Nb?!0:!1};a.b("observable",a.q);a.b("isObservable",a.M);a.b("isWriteableObservable",
a.gb);a.ba=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.q(b);a.a.extend(b,a.ba.fn);return b.extend({trackArrayChanges:!0})};a.ba.fn={remove:function(b){for(var c=this.t(),d=[],e="function"!=typeof b||a.M(b)?function(a){return a===b}:b,g=0;g<c.length;g++){var h=c[g];e(h)&&(0===d.length&&this.O(),d.push(h),c.splice(g,1),g--)}d.length&&this.N();return d},removeAll:function(b){if(b===
q){var c=this.t(),d=c.slice(0);this.O();c.splice(0,c.length);this.N();return d}return b?this.remove(function(c){return 0<=a.a.l(b,c)}):[]},destroy:function(b){var c=this.t(),d="function"!=typeof b||a.M(b)?function(a){return a===b}:b;this.O();for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.N()},destroyAll:function(b){return b===q?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.l(b,c)}):[]},indexOf:function(b){var c=this();return a.a.l(c,b)},replace:function(a,
c){var d=this.indexOf(a);0<=d&&(this.O(),this.t()[d]=c,this.N())}};a.a.n("pop push reverse shift sort splice unshift".split(" "),function(b){a.ba.fn[b]=function(){var a=this.t();this.O();this.Xa(a,b,arguments);a=a[b].apply(a,arguments);this.N();return a}});a.a.n(["slice"],function(b){a.ba.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.ba);var J="arrayChange";a.xa.trackArrayChanges=function(b){function c(){if(!d){d=!0;var c=b.notifySubscribers;b.notifySubscribers=
function(a,b){b&&b!==I||++g;return c.apply(this,arguments)};var m=[].concat(b.t()||[]);e=null;b.T(function(c){c=[].concat(c||[]);if(b.cb(J)){var d;if(!e||1<g)e=a.a.ra(m,c,{sparse:!0});d=e;d.length&&b.notifySubscribers(d,J)}m=c;e=null;g=0})}}if(!b.Xa){var d=!1,e=null,g=0,h=b.T;b.T=b.subscribe=function(a,b,f){f===J&&c();return h.apply(this,arguments)};b.Xa=function(a,b,c){function p(a,b,c){h.push({status:a,value:b,index:c})}if(d&&!g){var h=[],l=a.length,n=c.length,r=0;switch(b){case "push":r=l;case "unshift":for(b=
0;b<n;b++)p("added",c[b],r+b);break;case "pop":r=l-1;case "shift":l&&p("deleted",a[r],r);break;case "splice":b=Math.min(Math.max(0,0>c[0]?l+c[0]:c[0]),l);for(var l=1===n?l:Math.min(b+(c[1]||0),l),n=b+n-2,r=Math.max(l,n),v=2;b<r;++b,++v)b<l&&p("deleted",a[b],b),b<n&&p("added",c[v],b);break;default:return}e=h}}}};a.h=function(b,c,d){function e(){a.a.n(z,function(a){a.B()});z=[]}function g(){var a=k.throttleEvaluation;a&&0<=a?(clearTimeout(x),x=setTimeout(h,a)):h()}function h(){if(!s){if(E&&E()){if(!l){D();
p=!0;return}}else l=!1;s=!0;try{var b=a.a.ha(z,function(a){return a.target});a.i.Wa(function(c){var d;0<=(d=a.a.l(b,c))?b[d]=q:z.push(c.T(g))});for(var d=c?n.call(c):n(),e=b.length-1;0<=e;e--)b[e]&&z.splice(e,1)[0].B();p=!0;k.equalityComparer&&k.equalityComparer(f,d)||(k.notifySubscribers(f,"beforeChange"),f=d,k.notifySubscribers(f))}finally{a.i.end(),s=!1}z.length||D()}}function k(){if(0<arguments.length){if("function"===typeof r)r.apply(c,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
return this}p||h();a.i.lb(k);return f}function m(){return!p||0<z.length}var f,p=!1,s=!1,l=!1,n=b;n&&"object"==typeof n?(d=n,n=d.read):(d=d||{},n||(n=d.read));if("function"!=typeof n)throw Error("Pass a function that returns the value of the ko.computed");var r=d.write,v=d.disposeWhenNodeIsRemoved||d.I||null,t=d.disposeWhen||d.ua,E=t,D=e,z=[],x=null;c||(c=d.owner);k.t=function(){p||h();return f};k.Kb=function(){return z.length};k.Nb="function"===typeof d.write;k.B=function(){D()};k.aa=m;a.ca.call(k);
a.a.extend(k,a.h.fn);a.s(k,"peek",k.t);a.s(k,"dispose",k.B);a.s(k,"isActive",k.aa);a.s(k,"getDependenciesCount",k.Kb);v&&(l=!0,v.nodeType&&(E=function(){return!a.a.va(v)||t&&t()}));!0!==d.deferEvaluation&&h();v&&m()&&(D=function(){a.a.C.mb(v,D);e()},a.a.C.ea(v,D));return k};a.Pb=function(b){return a.ya(b,a.h)};F=a.q.Yb;a.h[F]=a.q;a.h.fn={equalityComparer:G};a.h.fn[F]=a.h;a.b("dependentObservable",a.h);a.b("computed",a.h);a.b("isComputed",a.Pb);(function(){function b(a,g,h){h=h||new d;a=g(a);if("object"!=
typeof a||null===a||a===q||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var k=a instanceof Array?[]:{};h.save(a,k);c(a,function(c){var d=g(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":k[c]=d;break;case "object":case "undefined":var p=h.get(d);k[c]=p!==q?p:b(d,g,h)}});return k}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=
[];this.Qa=[]}a.tb=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.M(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.tb(b);return a.a.Na(b,c,d)};d.prototype={save:function(b,c){var d=a.a.l(this.keys,b);0<=d?this.Qa[d]=c:(this.keys.push(b),this.Qa.push(c))},get:function(b){b=a.a.l(this.keys,b);return 0<=b?this.Qa[b]:q}}})();a.b("toJS",a.tb);a.b("toJSON",a.toJSON);(function(){a.k={o:function(b){switch(a.a.v(b)){case "option":return!0===
b.__ko__hasDomDataOptionValue__?a.a.f.get(b,a.d.options.Ea):7>=a.a.ja?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.k.o(b.options[b.selectedIndex]):q;default:return b.value}},na:function(b,c){switch(a.a.v(b)){case "option":switch(typeof c){case "string":a.a.f.set(b,a.d.options.Ea,q);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.f.set(b,a.d.options.Ea,c),b.__ko__hasDomDataOptionValue__=
!0,b.value="number"===typeof c?c:""}break;case "select":""===c&&(c=q);if(null===c||c===q)b.selectedIndex=-1;for(var d=b.options.length-1;0<=d;d--)if(a.k.o(b.options[d])==c){b.selectedIndex=d;break}1<b.size||-1!==b.selectedIndex||(b.selectedIndex=0);break;default:if(null===c||c===q)c="";b.value=c}}}})();a.b("selectExtensions",a.k);a.b("selectExtensions.readValue",a.k.o);a.b("selectExtensions.writeValue",a.k.na);a.g=function(){function b(b){b=a.a.la(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=
[],d=b.match(e),k,l,n=0;if(d){d.push(",");for(var r=0,v;v=d[r];++r){var t=v.charCodeAt(0);if(44===t){if(0>=n){k&&c.push(l?{key:k,value:l.join("")}:{unknown:k});k=l=n=0;continue}}else if(58===t){if(!l)continue}else if(47===t&&r&&1<v.length)(t=d[r-1].match(g))&&!h[t[0]]&&(b=b.substr(b.indexOf(v)+1),d=b.match(e),d.push(","),r=-1,v="/");else if(40===t||123===t||91===t)++n;else if(41===t||125===t||93===t)--n;else if(!k&&!l){k=34===t||39===t?v.slice(1,-1):v;continue}l?l.push(v):l=[v]}}return c}var c=["true",
"false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),g=/[\])"'A-Za-z0-9_$]+$/,h={"in":1,"return":1,"typeof":1},k={};return{Y:[],U:k,Ga:b,ka:function(e,f){function g(b,f){var e,r=a.getBindingHandler(b);if(r&&r.preprocess?f=r.preprocess(f,b,g):1){if(r=k[b])e=f,0<=a.a.l(c,e)?e=!1:(r=e.match(d),e=null===r?!1:r[1]?"Object("+r[1]+")"+
r[2]:e),r=e;r&&l.push("'"+b+"':function(_z){"+e+"=_z}");n&&(f="function(){return "+f+" }");h.push("'"+b+"':"+f)}}f=f||{};var h=[],l=[],n=f.valueAccessors,r="string"===typeof e?b(e):e;a.a.n(r,function(a){g(a.key||a.unknown,a.value)});l.length&&g("_ko_property_writers","{"+l.join(",")+"}");return h.join(",")},Sb:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},oa:function(b,c,d,e,k){if(b&&a.M(b))!a.gb(b)||k&&b.t()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();
a.b("expressionRewriting",a.g);a.b("expressionRewriting.bindingRewriteValidators",a.g.Y);a.b("expressionRewriting.parseObjectLiteral",a.g.Ga);a.b("expressionRewriting.preProcessBindings",a.g.ka);a.b("expressionRewriting._twoWayBindings",a.g.U);a.b("jsonExpressionRewriting",a.g);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.g.ka);(function(){function b(a){return 8==a.nodeType&&h.test(g?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&k.test(g?a.text:a.nodeValue)}function d(a,
d){for(var e=a,k=1,n=[];e=e.nextSibling;){if(c(e)&&(k--,0===k))return n;n.push(e);b(e)&&k++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-1].nextSibling:a.nextSibling:null}var g=w&&"\x3c!--test--\x3e"===w.createComment("test").text,h=g?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,k=g?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,m={ul:!0,ol:!0};a.e={P:{},childNodes:function(a){return b(a)?
d(a):a.childNodes},Z:function(c){if(b(c)){c=a.e.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.wa(c)},S:function(c,d){if(b(c)){a.e.Z(c);for(var e=c.nextSibling,k=0,n=d.length;k<n;k++)e.parentNode.insertBefore(d[k],e)}else a.a.S(c,d)},kb:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},eb:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):
c.appendChild(d):a.e.kb(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Mb:b,bc:function(a){return(a=(g?a.text:a.nodeValue).match(h))?a[1]:null},ib:function(d){if(m[a.a.v(d)]){var k=d.firstChild;if(k){do if(1===k.nodeType){var g;g=k.firstChild;var h=null;if(g){do if(h)h.push(g);else if(b(g)){var n=e(g,!0);n?g=n:h=[g]}else c(g)&&(h=[g]);while(g=
g.nextSibling)}if(g=h)for(h=k.nextSibling,n=0;n<g.length;n++)h?d.insertBefore(g[n],h):d.appendChild(g[n])}while(k=k.nextSibling)}}}}})();a.b("virtualElements",a.e);a.b("virtualElements.allowedBindings",a.e.P);a.b("virtualElements.emptyNode",a.e.Z);a.b("virtualElements.insertAfter",a.e.eb);a.b("virtualElements.prepend",a.e.kb);a.b("virtualElements.setDomNodeChildren",a.e.S);(function(){a.H=function(){this.zb={}};a.a.extend(a.H.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=
b.getAttribute("data-bind");case 8:return a.e.Mb(b);default:return!1}},getBindings:function(a,c){var d=this.getBindingsString(a,c);return d?this.parseBindingsString(d,c,a):null},getBindingAccessors:function(a,c){var d=this.getBindingsString(a,c);return d?this.parseBindingsString(d,c,a,{valueAccessors:!0}):null},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.e.bc(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var g=this.zb,
h=b+(e&&e.valueAccessors||""),k;if(!(k=g[h])){var m,f="with($context){with($data||{}){return{"+a.g.ka(b,e)+"}}}";m=new Function("$context","$element",f);k=g[h]=m}return k(c,d)}catch(p){throw p.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+p.message,p;}}});a.H.instance=new a.H})();a.b("bindingProvider",a.H);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Da(a.i.p(b),function(a,c){return function(){return b()[c]}})}function e(a,
b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var f,e=a.e.firstChild(c),k=a.H.instance,g=k.preprocessNode;if(g){for(;f=e;)e=a.e.nextSibling(f),g.call(k,f);e=a.e.firstChild(c)}for(;f=e;)e=a.e.nextSibling(f),h(b,f,d)}function h(b,c,d){var f=!0,e=1===c.nodeType;e&&a.e.ib(c);if(e&&d||a.H.instance.nodeHasBindings(c))f=m(c,null,b,d).shouldBindDescendants;f&&!p[a.a.v(c)]&&g(b,c,!e)}function k(b){var c=[],d={},f=[];a.a.K(b,function D(e){if(!d[e]){var k=a.getBindingHandler(e);k&&(k.after&&
(f.push(e),a.a.n(k.after,function(c){if(b[c]){if(-1!==a.a.l(f,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+f.join(", "));D(c)}}),f.pop()),c.push({key:e,bb:k}));d[e]=!0}});return c}function m(b,d,f,g){var h=a.a.f.get(b,s);if(!d){if(h)throw Error("You cannot apply bindings multiple times to the same element.");a.a.f.set(b,s,!0)}!h&&g&&a.rb(b,f);var m;if(d&&"function"!==typeof d)m=d;else{var p=a.H.instance,l=p.getBindingAccessors||e;if(d||f.A){var A=
a.h(function(){(m=d?d(f,b):l.call(p,b,f))&&f.A&&f.A();return m},null,{I:b});m&&A.aa()||(A=null)}else m=a.i.p(l,p,[b,f])}var u;if(m){var w=A?function(a){return function(){return c(A()[a])}}:function(a){return m[a]},y=function(){return a.a.Da(A?A():m,c)};y.get=function(a){return m[a]&&c(w(a))};y.has=function(a){return a in m};g=k(m);a.a.n(g,function(c){var d=c.bb.init,e=c.bb.update,k=c.key;if(8===b.nodeType&&!a.e.P[k])throw Error("The binding '"+k+"' cannot be used with virtual elements");try{"function"==
typeof d&&a.i.p(function(){var a=d(b,w(k),y,f.$data,f);if(a&&a.controlsDescendantBindings){if(u!==q)throw Error("Multiple bindings ("+u+" and "+k+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");u=k}}),"function"==typeof e&&a.h(function(){e(b,w(k),y,f.$data,f)},null,{I:b})}catch(g){throw g.message='Unable to process binding "'+k+": "+m[k]+'"\nMessage: '+g.message,g;}})}return{shouldBindDescendants:u===q}}function f(b){return b&&
b instanceof a.G?b:new a.G(b)}a.d={};var p={script:!0};a.getBindingHandler=function(b){return a.d[b]};a.G=function(b,c,d,f){var e=this,k="function"==typeof b,g,h=a.h(function(){var g=k?b():b;c?(c.A&&c.A(),a.a.extend(e,c),h&&(e.A=h)):(e.$parents=[],e.$root=g,e.ko=a);e.$rawData=b;e.$data=g;d&&(e[d]=g);f&&f(e,c,g);return e.$data},null,{ua:function(){return g&&!a.a.Ra(g)},I:!0});h.aa()&&(e.A=h,h.equalityComparer=null,g=[],h.wb=function(b){g.push(b);a.a.C.ea(b,function(b){a.a.ia(g,b);g.length||(h.B(),
e.A=h=q)})})};a.G.prototype.createChildContext=function(b,c,d){return new a.G(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);d&&d(a)})};a.G.prototype.extend=function(b){return new a.G(this.$rawData,this,null,function(c){a.a.extend(c,"function"==typeof b?b():b)})};var s=a.a.f.D(),l=a.a.f.D();a.rb=function(b,c){if(2==arguments.length)a.a.f.set(b,l,c),c.A&&c.A.wb(b);else return a.a.f.get(b,l)};a.pa=function(b,c,d){1===b.nodeType&&
a.e.ib(b);return m(b,c,f(d),!0)};a.xb=function(c,e,k){k=f(k);return a.pa(c,"function"===typeof e?d(e.bind(null,k,c)):a.a.Da(e,b),k)};a.Ta=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(f(a),b,!0)};a.Sa=function(a,b){if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||y.document.body;h(f(a),b,!0)};a.ta=function(b){switch(b.nodeType){case 1:case 8:var c=a.rb(b);if(c)return c;if(b.parentNode)return a.ta(b.parentNode)}return q};
a.Cb=function(b){return(b=a.ta(b))?b.$data:q};a.b("bindingHandlers",a.d);a.b("applyBindings",a.Sa);a.b("applyBindingsToDescendants",a.Ta);a.b("applyBindingAccessorsToNode",a.pa);a.b("applyBindingsToNode",a.xb);a.b("contextFor",a.ta);a.b("dataFor",a.Cb)})();var M={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.K(d,function(c,d){d=a.a.c(d);var h=!1===d||null===d||d===q;h&&b.removeAttribute(c);8>=a.a.ja&&c in M?(c=M[c],h?b.removeAttribute(c):b[c]=d):h||b.setAttribute(c,
d.toString());"name"===c&&a.a.pb(b,h?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,c,d){function e(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):b.value}function g(){var k=b.checked,g=s?e():k;if(l&&(!m||k)){var h=a.i.p(c);f?p!==g?(k&&(a.a.V(h,g,!0),a.a.V(h,p,!1)),p=g):a.a.V(h,g,k):a.g.oa(h,d,"checked",g,!0)}}function h(){var d=a.a.c(c());b.checked=f?0<=a.a.l(d,e()):k?d:e()===d}var k="checkbox"==b.type,m="radio"==b.type;if(k||m){var f=k&&a.a.c(c())instanceof
Array,p=f?e():q,s=m||f,l=!1;m&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.h(g,null,{I:b});a.a.r(b,"click",g);a.h(h,null,{I:b});l=!0}}};a.g.U.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());"object"==typeof d?a.a.K(d,function(c,d){d=a.a.c(d);a.a.ma(b,c,d)}):(d=String(d||""),a.a.ma(b,b.__ko__cssValue,!1),b.__ko__cssValue=d,a.a.ma(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());d&&b.disabled?b.removeAttribute("disabled"):
d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,g){var h=c()||{};a.a.K(h,function(k){"string"==typeof k&&a.a.r(b,k,function(b){var f,h=c()[k];if(h){try{var s=a.a.Q(arguments);e=g.$data;s.unshift(e);f=h.apply(e,s)}finally{!0!==f&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(k+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={hb:function(b){return function(){var c=
b(),d=a.a.Ha(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.J.Aa};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.J.Aa}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.hb(c))},update:function(b,c,d,e,g){return a.d.template.update(b,a.d.foreach.hb(c),d,e,g)}};a.g.Y.foreach=!1;a.e.P.foreach=!0;a.d.hasfocus=
{init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var g=b.ownerDocument;if("activeElement"in g){var f;try{f=g.activeElement}catch(h){f=g.body}e=f===b}g=c();a.g.oa(g,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var g=e.bind(null,!0),h=e.bind(null,!1);a.a.r(b,"focus",g);a.a.r(b,"focusin",g);a.a.r(b,"blur",h);a.a.r(b,"focusout",h)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===d||(d?b.focus():b.blur(),a.i.p(a.a.da,
null,[b,d?"focusin":"focusout"]))}};a.g.U.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.g.U.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Ka(b,c())}};var L=a.a.f.D();H("if");H("ifnot",!1,!0);H("with",!0,!1,function(a,c){return a.createChildContext(c)});a.d.options={init:function(b){if("select"!==a.a.v(b))throw Error("options binding applies only to SELECT elements");for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,
c,d){function e(){return a.a.ga(b.options,function(a){return a.selected})}function g(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function h(c,d){if(p.length){var f=0<=a.a.l(p,a.k.o(d[0]));a.a.qb(d[0],f);l&&!f&&a.i.p(a.a.da,null,[b,"change"])}}var k=0!=b.length&&b.multiple?b.scrollTop:null;c=a.a.c(c());var m=d.get("optionsIncludeDestroyed"),f={},p;p=b.multiple?a.a.ha(e(),a.k.o):0<=b.selectedIndex?[a.k.o(b.options[b.selectedIndex])]:[];if(c){"undefined"==typeof c.length&&(c=[c]);
var s=a.a.ga(c,function(b){return m||b===q||null===b||!a.a.c(b._destroy)});d.has("optionsCaption")&&(c=a.a.c(d.get("optionsCaption")),null!==c&&c!==q&&s.unshift(f))}else c=[];var l=!1;c=h;d.has("optionsAfterRender")&&(c=function(b,c){h(0,c);a.i.p(d.get("optionsAfterRender"),null,[c[0],b!==f?b:q])});a.a.Ja(b,s,function(b,c,e){e.length&&(p=e[0].selected?[a.k.o(e[0])]:[],l=!0);c=w.createElement("option");b===f?(a.a.Ma(c,d.get("optionsCaption")),a.k.na(c,q)):(e=g(b,d.get("optionsValue"),b),a.k.na(c,a.a.c(e)),
b=g(b,d.get("optionsText"),e),a.a.Ma(c,b));return[c]},null,c);(b.multiple?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.k.o(b.options[b.selectedIndex])!==p[0]:p.length||0<=b.selectedIndex)&&a.i.p(a.a.da,null,[b,"change"]);a.a.Hb(b);k&&20<Math.abs(k-b.scrollTop)&&(b.scrollTop=k)}};a.d.options.Ea=a.a.f.D();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.r(b,"change",function(){var e=c(),g=[];a.a.n(b.getElementsByTagName("option"),function(b){b.selected&&g.push(a.k.o(b))});
a.g.oa(e,d,"selectedOptions",g)})},update:function(b,c){if("select"!=a.a.v(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c());d&&"number"==typeof d.length&&a.a.n(b.getElementsByTagName("option"),function(b){var c=0<=a.a.l(d,a.k.o(b));a.a.qb(b,c)})}};a.g.U.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.K(d,function(c,d){d=a.a.c(d);b.style[c]=d||""})}};a.d.submit={init:function(b,c,d,e,g){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");
a.a.r(b,"submit",function(a){var d,e=c();try{d=e.call(g.$data,b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Ma(b,c())}};a.e.P.text=!0;a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.d.uniqueName.Bb;a.a.pb(b,d)}}};a.d.uniqueName.Bb=0;a.d.value={after:["options","foreach"],init:function(b,c,d){function e(){k=!1;var e=c(),f=a.k.o(b);a.g.oa(e,d,"value",f)}var g=
["change"],h=d.get("valueUpdate"),k=!1;h&&("string"==typeof h&&(h=[h]),a.a.X(g,h),g=a.a.Va(g));!a.a.ja||"input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.l(g,"propertychange")||(a.a.r(b,"propertychange",function(){k=!0}),a.a.r(b,"blur",function(){k&&e()}));a.a.n(g,function(c){var d=e;a.a.ac(c,"after")&&(d=function(){setTimeout(e,0)},c=c.substring(5));a.a.r(b,c,d)})},update:function(b,c){var d="select"===a.a.v(b),e=a.a.c(c()),g=a.k.o(b);
e!==g&&(g=function(){a.k.na(b,e)},g(),d&&(e!==a.k.o(b)?a.i.p(a.a.da,null,[b,"change"]):setTimeout(g,0)))}};a.g.U.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,g,h){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,g,h)}}})("click");a.w=function(){};a.w.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");
};a.w.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.w.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||w;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.m.j(d)}if(1==b.nodeType||8==b.nodeType)return new a.m.W(b);throw Error("Unknown template type: "+b);};a.w.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,
d)};a.w.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.w.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.w);a.Oa=function(){function b(b,c,d,k){b=a.g.Ga(b);for(var m=a.g.Y,f=0;f<b.length;f++){var p=b[f].key;if(m.hasOwnProperty(p)){var s=m[p];if("function"===typeof s){if(p=s(b[f].value))throw Error(p);}else if(!s)throw Error("This template engine does not support the '"+
p+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.g.ka(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return k.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Ib:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Oa.Ub(b,c)},
d)},Ub:function(a,g){return a.replace(c,function(a,c,d,f,e){return b(e,c,d,g)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",g)})},yb:function(b,c){return a.u.Ca(function(d,k){var m=d.nextSibling;m&&m.nodeName.toLowerCase()===c&&a.pa(m,b,k)})}}}();a.b("__tr_ambtns",a.Oa.yb);(function(){a.m={};a.m.j=function(a){this.j=a};a.m.j.prototype.text=function(){var b=a.a.v(this.j),b="script"===b?"text":"textarea"===b?"value":"innerHTML";if(0==arguments.length)return this.j[b];var c=arguments[0];
"innerHTML"===b?a.a.Ka(this.j,c):this.j[b]=c};var b=a.a.f.D()+"_";a.m.j.prototype.data=function(c){if(1===arguments.length)return a.a.f.get(this.j,b+c);a.a.f.set(this.j,b+c,arguments[1])};var c=a.a.f.D();a.m.W=function(a){this.j=a};a.m.W.prototype=new a.m.j;a.m.W.prototype.text=function(){if(0==arguments.length){var b=a.a.f.get(this.j,c)||{};b.Pa===q&&b.sa&&(b.Pa=b.sa.innerHTML);return b.Pa}a.a.f.set(this.j,c,{Pa:arguments[0]})};a.m.j.prototype.nodes=function(){if(0==arguments.length)return(a.a.f.get(this.j,
c)||{}).sa;a.a.f.set(this.j,c,{sa:arguments[0]})};a.b("templateSources",a.m);a.b("templateSources.domElement",a.m.j);a.b("templateSources.anonymousTemplate",a.m.W)})();(function(){function b(b,c,d){var e;for(c=a.e.nextSibling(c);b&&(e=b)!==c;)b=a.e.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var f=c[0],e=c[c.length-1],g=f.parentNode,h=a.H.instance,n=h.preprocessNode;if(n){b(f,e,function(a,b){var c=a.previousSibling,d=n.call(h,a);d&&(a===f&&(f=d[0]||b),a===e&&(e=d[d.length-1]||c))});c.length=
0;if(!f)return;f===e?c.push(f):(c.push(f,e),a.a.$(c,g))}b(f,e,function(b){1!==b.nodeType&&8!==b.nodeType||a.Sa(d,b)});b(f,e,function(b){1!==b.nodeType&&8!==b.nodeType||a.u.vb(b,[d])});a.a.$(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,s){s=s||{};var l=b&&d(b),l=l&&l.ownerDocument,n=s.templateEngine||g;a.Oa.Ib(f,n,l);f=n.renderTemplate(f,h,s,l);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");
l=!1;switch(e){case "replaceChildren":a.e.S(b,f);l=!0;break;case "replaceNode":a.a.nb(b,f);l=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}l&&(c(f,h),s.afterRender&&a.i.p(s.afterRender,null,[f,h.$data]));return f}var g;a.La=function(b){if(b!=q&&!(b instanceof a.w))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Ia=function(b,c,f,h,s){f=f||{};if((f.templateEngine||g)==q)throw Error("Set a template engine before calling renderTemplate");
s=s||"replaceChildren";if(h){var l=d(h);return a.h(function(){var g=c&&c instanceof a.G?c:new a.G(a.a.c(c)),r="function"==typeof b?b(g.$data,g):b,g=e(h,s,r,g,f);"replaceNode"==s&&(h=g,l=d(h))},null,{ua:function(){return!l||!a.a.va(l)},I:l&&"replaceNode"==s?l.parentNode:l})}return a.u.Ca(function(d){a.Ia(b,c,f,d,"replaceNode")})};a.$b=function(b,d,f,g,h){function l(a,b){c(b,r);f.afterRender&&f.afterRender(b,a)}function n(a,c){r=h.createChildContext(a,f.as,function(a){a.$index=c});var d="function"==
typeof b?b(a,r):b;return e(null,"ignoreTargetNode",d,r,f)}var r;return a.h(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.ga(b,function(b){return f.includeDestroyed||b===q||null===b||!a.a.c(b._destroy)});a.i.p(a.a.Ja,null,[g,b,n,f,l])},null,{I:g})};var h=a.a.f.D();a.d.template={init:function(b,c){var d=a.a.c(c());"string"==typeof d||d.name?a.e.Z(b):(d=a.e.childNodes(b),d=a.a.Vb(d),(new a.m.W(b)).nodes(d));return{controlsDescendantBindings:!0}},update:function(b,c,d,e,g){c=
a.a.c(c());d={};e=!0;var l,n=null;"string"!=typeof c&&(d=c,c=a.a.c(d.name),"if"in d&&(e=a.a.c(d["if"])),e&&"ifnot"in d&&(e=!a.a.c(d.ifnot)),l=a.a.c(d.data));"foreach"in d?n=a.$b(c||b,e&&d.foreach||[],d,b,g):e?(g="data"in d?g.createChildContext(l,d.as):g,n=a.Ia(c||b,g,d,b)):a.e.Z(b);g=n;(l=a.a.f.get(b,h))&&"function"==typeof l.B&&l.B();a.a.f.set(b,h,g&&g.aa()?g:q)}};a.g.Y.template=function(b){b=a.g.Ga(b);return 1==b.length&&b[0].unknown||a.g.Sb(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};
a.e.P.template=!0})();a.b("setTemplateEngine",a.La);a.b("renderTemplate",a.Ia);a.a.ra=function(){function a(b,d,e,g,h){var k=Math.min,m=Math.max,f=[],p,q=b.length,l,n=d.length,r=n-q||1,v=q+n+1,t,u,w;for(p=0;p<=q;p++)for(u=t,f.push(t=[]),w=k(n,p+r),l=m(0,p-1);l<=w;l++)t[l]=l?p?b[p-1]===d[l-1]?u[l-1]:k(u[l]||v,t[l-1]||v)+1:l+1:p+1;k=[];m=[];r=[];p=q;for(l=n;p||l;)n=f[p][l]-1,l&&n===f[p][l-1]?m.push(k[k.length]={status:e,value:d[--l],index:l}):p&&n===f[p-1][l]?r.push(k[k.length]={status:g,value:b[--p],
index:p}):(--l,--p,h.sparse||k.push({status:"retained",value:d[l]}));if(m.length&&r.length){b=10*q;var z;for(d=e=0;(h.dontLimitMoves||d<b)&&(z=m[e]);e++){for(g=0;f=r[g];g++)if(z.value===f.value){z.moved=f.index;f.moved=z.index;r.splice(g,1);d=g=0;break}d+=g}}return k.reverse()}return function(c,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};c=c||[];d=d||[];return c.length<=d.length?a(c,d,"added","deleted",e):a(d,c,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.ra);(function(){function b(b,
c,g,h,k){var m=[],f=a.h(function(){var f=c(g,k,a.a.$(m,b))||[];0<m.length&&(a.a.nb(m,f),h&&a.i.p(h,null,[g,f,k]));m.splice(0,m.length);a.a.X(m,f)},null,{I:b,ua:function(){return!a.a.Ra(m)}});return{R:m,h:f.aa()?f:q}}var c=a.a.f.D();a.a.Ja=function(d,e,g,h,k){function m(b,c){x=s[c];t!==c&&(z[b]=x);x.za(t++);a.a.$(x.R,d);r.push(x);w.push(x)}function f(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.n(c[d].R,function(a){b(a,d,c[d].fa)})}e=e||[];h=h||{};var p=a.a.f.get(d,c)===q,s=a.a.f.get(d,c)||[],
l=a.a.ha(s,function(a){return a.fa}),n=a.a.ra(l,e,h.dontLimitMoves),r=[],v=0,t=0,u=[],w=[];e=[];for(var z=[],l=[],x,A=0,y,B;y=n[A];A++)switch(B=y.moved,y.status){case "deleted":B===q&&(x=s[v],x.h&&x.h.B(),u.push.apply(u,a.a.$(x.R,d)),h.beforeRemove&&(e[A]=x,w.push(x)));v++;break;case "retained":m(A,v++);break;case "added":B!==q?m(A,B):(x={fa:y.value,za:a.q(t++)},r.push(x),w.push(x),p||(l[A]=x))}f(h.beforeMove,z);a.a.n(u,h.beforeRemove?a.L:a.removeNode);for(var A=0,p=a.e.firstChild(d),C;x=w[A];A++){x.R||
a.a.extend(x,b(d,g,x.fa,k,x.za));for(v=0;n=x.R[v];p=n.nextSibling,C=n,v++)n!==p&&a.e.eb(d,n,C);!x.Ob&&k&&(k(x.fa,x.R,x.za),x.Ob=!0)}f(h.beforeRemove,e);f(h.afterMove,z);f(h.afterAdd,l);a.a.f.set(d,c,r)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Ja);a.J=function(){this.allowTemplateRewriting=!1};a.J.prototype=new a.w;a.J.prototype.renderTemplateSource=function(b){var c=(9>a.a.ja?0:b.nodes)?b.nodes():null;if(c)return a.a.Q(c.cloneNode(!0).childNodes);b=b.text();return a.a.Fa(b)};a.J.Aa=
new a.J;a.La(a.J.Aa);a.b("nativeTemplateEngine",a.J);(function(){a.Ba=function(){var a=this.Rb=function(){if("undefined"==typeof u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,g){g=g||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+h+
"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},g.templateOptions);e=u.tmpl(h,b,e);e.appendTo(w.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){w.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(u.tmpl.tag.ko_code={open:"__.push($1 || '');"},u.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.Ba.prototype=
new a.w;var b=new a.Ba;0<b.Rb&&a.La(b);a.b("jqueryTmplTemplateEngine",a.Ba)})()})})();})();

10
js/raphael.2.1.0.min.js vendored 100755

File diff suppressed because one or more lines are too long

6
js/underscore.js 100755

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,250 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>freeboard</title>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="viewport"
content="width = device-width, initial-scale = 1, user-scalable = no" />
<link href="css/jquery.gridster.min.css" rel="stylesheet" />
<link href="css/styles.css" rel="stylesheet" />
<script src="js/head.js"></script>
<script type="text/javascript">
head
.js(
"js/knockout.js",
"js/jquery.js",
"js/jquery-ui.js",
"js/underscore.js",
"js/jquery.gridster.js",
"js/jquery.sparkline.min.js",
"js/jquery.caret.js",
"js/raphael.2.1.0.min.js",
"js/justgage.1.0.1.js",
"js/freeboard/freeboard.js",
"js/freeboard/plugins/freeboard.datasources.js",
"js/freeboard/plugins/freeboard.widgets.js",
"datasources/plugin_node.js",
// *** Load more plugins here ***
function() {
$(function() { //DOM Ready
freeboard.initialize(true);
freeboard
.loadDashboard({
"allow_edit": true,
"plugins": [],
"panes": [
{
"title": "Houses",
"width": 1,
"row": {
"2": 1,
"3": 1
},
"col": {
"2": 1,
"3": 1
},
"widgets": [
{
"type": "text_widget",
"settings": {
"title": "House of Stark - Jon",
"size": "regular",
"value": "datasources[\"Jon\"].value",
"sparkline": true,
"animate": true
}
},
{
"type": "text_widget",
"settings": {
"title": "House of Lannister - Tyrion",
"size": "regular",
"value": "datasources[\"Tyrion\"].value",
"sparkline": true,
"animate": true
}
}
]
},
{
"title": "Timezones",
"width": 1,
"row": {
"3": 1
},
"col": {
"3": 2
},
"widgets": [
{
"type": "text_widget",
"settings": {
"title": "House of Stark - Jon",
"size": "regular",
"value": "new Date(datasources[\"Jon\"].time_stamp).toLocaleTimeString();",
"animate": false
}
},
{
"type": "text_widget",
"settings": {
"title": "House of Lannister - Tyrion",
"size": "regular",
"value": "new Date(datasources[\"Tyrion\"].time_stamp).toLocaleTimeString();",
"animate": false
}
}
]
}
],
"datasources": [
{
"name": "Jon",
"type": "node_js",
"settings": {
"url": "http://localhost:8989/shows",
"eventName": "house.stark.jon",
"rooms": [
{
"roomName": "got",
"roomEvent": "subscribe"
}
]
}
},
{
"name": "Tyrion",
"type": "node_js",
"settings": {
"url": "http://localhost:8989/shows",
"eventName": "house.lannister.tyrion",
"rooms": [
{
"roomName": "got",
"roomEvent": "subscribe"
}
]
}
}
]
});
});
});
</script>
</head>
<body>
<div id="board-content">
<img id="dash-logo"
data-bind="attr:{src: header_image}, visible:header_image()">
<div class="gridster">
<ul data-bind="grid: true">
</ul>
</div>
</div>
<header id="main-header" data-bind="if:allow_edit">
<div id="admin-bar">
<div id="admin-menu">
<div id="board-tools">
<h1 id="board-logo" class="title bordered">freeboard</h1>
<div id="board-actions">
<ul class="board-toolbar vertical">
<li data-bind="click: loadDashboardFromLocalFile"><i
id="full-screen-icon" class="icon-folder-open icon-white"></i><label
id="full-screen">Load Freeboard</label></li>
<li data-bind="click: saveDashboard"><i
class="icon-download-alt icon-white"></i><label>Save
Freeboard</label></li>
<li id="add-pane" data-bind="click: createPane"><i
class="icon-plus icon-white"></i><label>Add Pane</label></li>
</ul>
</div>
</div>
<div id="datasources">
<h2 class="title">DATASOURCES</h2>
<div class="datasource-list-container">
<table class="table table-condensed sub-table"
id="datasources-list" data-bind="if: datasources().length">
<thead>
<tr>
<th>Name</th>
<th>Last Updated</th>
<th>&nbsp;</th>
</tr>
</thead>
<tbody data-bind="foreach: datasources">
<tr>
<td><span class="text-button datasource-name"
data-bind="text: name, pluginEditor: {operation: 'edit', type: 'datasource'}"></span>
</td>
<td data-bind="text: last_updated"></td>
<td>
<ul class="board-toolbar">
<li data-bind="click: updateNow"><i
class="icon-refresh icon-white"></i></li>
<li
data-bind="pluginEditor: {operation: 'delete', type: 'datasource'}">
<i class="icon-trash icon-white"></i>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<span class="text-button table-operation"
data-bind="pluginEditor: {operation: 'add', type: 'datasource'}">ADD</span>
</div>
</div>
</div>
<div id="toggle-header" data-bind="click: toggleEditing">
<i id="toggle-header-icon" class="icon-wrench icon-white"></i>
</div>
</header>
<div style="display: hidden">
<ul data-bind="template: { name: 'pane-template', foreach: panes}">
</ul>
</div>
<script type="text/html" id="pane-template">
<li data-bind="pane: true">
<header>
<h1 data-bind="text: title"></h1>
<ul class="board-toolbar pane-tools">
<li data-bind="pluginEditor: {operation: 'add', type: 'widget'}">
<i class="icon-plus icon-white"></i>
</li>
<li data-bind="pluginEditor: {operation: 'edit', type: 'pane'}">
<i class="icon-wrench icon-white"></i>
</li>
<li data-bind="pluginEditor: {operation: 'delete', type: 'pane'}">
<i class="icon-trash icon-white"></i>
</li>
</ul>
</header>
<section data-bind="foreach: widgets">
<div class="sub-section" data-bind="css: 'sub-section-height-' + height()">
<div class="widget" data-bind="widget: true, css:{fillsize:fillSize}"></div>
<div class="sub-section-tools">
<ul class="board-toolbar">
<!-- ko if:$parent.widgetCanMoveUp($data) -->
<li data-bind="click:$parent.moveWidgetUp"><i class="icon-chevron-up icon-white"></i></li>
<!-- /ko -->
<!-- ko if:$parent.widgetCanMoveDown($data) -->
<li data-bind="click:$parent.moveWidgetDown"><i class="icon-chevron-down icon-white"></i></li>
<!-- /ko -->
<li data-bind="pluginEditor: {operation: 'edit', type: 'widget'}"><i class="icon-wrench icon-white"></i></li>
<li data-bind="pluginEditor: {operation: 'delete', type: 'widget'}"><i class="icon-trash icon-white"></i></li>
</ul>
</div>
</div>
</section>
</li>
</script>
</body>
</html>

View File

@ -0,0 +1,100 @@
{
"allow_edit": true,
"plugins": [],
"panes": [
{
"title": "Houses",
"width": 1,
"row": {
"2": 1,
"3": 1
},
"col": {
"2": 1,
"3": 1
},
"widgets": [
{
"type": "text_widget",
"settings": {
"title": "House of Stark - Jon",
"size": "regular",
"value": "datasources[\"Jon\"].value",
"sparkline": true,
"animate": true
}
},
{
"type": "text_widget",
"settings": {
"title": "House of Lannister - Tyrion",
"size": "regular",
"value": "datasources[\"Tyrion\"].value",
"sparkline": true,
"animate": true
}
}
]
},
{
"title": "Timezones",
"width": 1,
"row": {
"3": 1
},
"col": {
"3": 2
},
"widgets": [
{
"type": "text_widget",
"settings": {
"title": "House of Stark - Jon",
"size": "regular",
"value": "new Date(datasources[\"Jon\"].time_stamp).toLocaleTimeString();",
"animate": false
}
},
{
"type": "text_widget",
"settings": {
"title": "House of Lannister - Tyrion",
"size": "regular",
"value": "new Date(datasources[\"Tyrion\"].time_stamp).toLocaleTimeString();",
"animate": false
}
}
]
}
],
"datasources": [
{
"name": "Jon",
"type": "node_js",
"settings": {
"url": "http://localhost:8989/shows",
"eventName": "house.stark.jon",
"rooms": [
{
"roomName": "got",
"roomEvent": "subscribe"
}
]
}
},
{
"name": "Tyrion",
"type": "node_js",
"settings": {
"url": "http://localhost:8989/shows",
"eventName": "house.lannister.tyrion",
"rooms": [
{
"roomName": "got",
"roomEvent": "subscribe"
}
]
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -0,0 +1,111 @@
// ┌────────────────────────────────────────────────────────────────────┐ \\
// │ freeboard.io-node.js │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright © 2014 Hugo Sequeira (https://github.com/hugocore) │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Licensed under the MIT license. │ \\
// ├────────────────────────────────────────────────────────────────────┤ \\
// │ Simple node.js and sockets.io server to test the node.js plugin. │ \\
// └────────────────────────────────────────────────────────────────────┘ \\
/*
* Configurations and helpers
*/
var namespace = '/shows';
var room = 'got';
var refreshTimer = 1000;
var connectionscounter = 0;
var eventNames = ['house.stark.jon', 'house.lannister.tyrion'];
var serverport = 8989;
/*
* Project dependencies
*/
var io = require('socket.io')(serverport);
/*
* Collects data
*/
// Implement the methods to handle the new events
function newEventCallback(eventName, message) {
// Construct the json object to be propagated
var json = {
value: message.value,
time_stamp: message.time_stamp,
};
// Invokes propagation
propagatesEvent(eventName, JSON.stringify(json));
}
// Connection to external data sources
function connectToExternalSources() {
// Simulate the connection and new messages with a timer function
setInterval(function() {
for (var i=0; i<eventNames.length; i++) {
// construct message
var oneHourInMilis = 3600000;
var message = {
value: Math.floor(Math.random()*101),
time_stamp: new Date().getTime()+(i*oneHourInMilis)
};
newEventCallback(eventNames[i], message);
}
}, refreshTimer);
}
/*
* Data propagation
*/
// Propagates event through all the connected clients
function propagatesEvent(eventName, event) {
if (connectionscounter>0) {
io.of(namespace).to(room).emit(eventName, event);
console.log("New event propagated in: Namespace='%s' Room='%s' EventName='%s' Event='%s'", namespace, room, eventName, event);
}
}
/*
* Handle Sockets.io connections
*/
// Event handlers
io.of(namespace).on('connection', function(socket) {
console.log("New client connected.");
// Do some logic for every new connection
connectionscounter++;
// On subscribe events join client to room
socket.on('subscribe', function(room) {
socket.join(room);
console.log("Client joined room: " + room);
});
// On disconnect events
socket.on('disconnect', function(socket) {
console.log("Client disconnect from rooms.");
connectionscounter--;
});
});
function puts(message) {
console.log(message);
}
/*
* Run
*/
console.log("Starting Node.js server with namespace='%s' and room='%s'", namespace, room);
connectToExternalSources();