Beispiel #1
0
_NODES.createGenerator = function(tag, methods) {
	var ClassDefinition = Class(_NODES.NODE, function() {

		this._tag = tag

		each(methods, this, function(method, name) {
			this[name] = method
		})

	})
	return function() { return new ClassDefinition(slice(arguments, 0)) }
}
Beispiel #2
0
module.exports = Class(Server, function(supr) {

	this.run = function() {
		configureRequire(requireServer)
		requireServer.mount(this._httpServer, { port:this._port, host:this._host })
		Server.prototype.run.apply(this, arguments)
	}
	
	this._setupRoutes = function() {
		Server.prototype._setupRoutes.apply(this, arguments)
		this
			._setupRoute(/^\/stylus\/(\w+)\.styl$/, this._handleStylusRequest)
	}
	
	this._routeRequest = function(req, res) {
		if (requireServer.isRequireRequest(req)) { return }
		Server.prototype._routeRequest.apply(this, arguments)
	}
	
	this._handleStylusRequest = function(match, req, res) {
		var filename = __dirname + '/../client/' + this._clients[match[1]] + '.styl'
		fs.readFile(filename, 'utf8', function(err, stylusSource) {
			if (err) { return res.sendError(err, 404) }
			stylus.render(stylusSource, { filename:filename }, function(err, css) {
				if (err) { return res.sendError(err, 500) }
				res.send(css, 'text/css')
			})
		})
	}
	
	this._getStaticPath = function(version, path) {
		return this._staticDir+'/'+path
	}
	
})
Beispiel #3
0
module.exports = Class(Component, function() {

	this._class = 'Button'
	
	this.init = function(className, label, click) {
		Component.prototype.init.apply(this)
		if (className) { this._class += ' ' + className }
		this._label = label
		this._clickHandler = click
	}

	this.renderContent = function() {
		this
			.on('touchstart', bind(this, this._onTouchStart))
			.on('touchend', bind(this, this._onTouchEnd))
			.on('touchcancel', bind(this, this._onTouchCancel))
			.on('touchmove', bind(this, this._onTouchMove))
			
			// for browser development
			.on('click', this._clickHandler)
			.on('mousedown', bind(this, this.addClass, 'active'))
			.on('mouseup', bind(this, this.removeClass, 'active'))
			.on('mouseout', bind(this, this.removeClass, 'active'))
		
		this.append(SPAN('label', HTML(this._label)))
		
		if (this._disabled) { this.addClass('disabled') }

		// DIV({ style:{position:'absolute', top:0, left:'3.5%', width:'94%', 'borderRadius':8 } })
		// http://admindaily.com/glossy-buttons-without-images-using-only-css3.html
	}
	
	this.disable = function(flag) {
		this._disabled = (flag !== false)
		if (this._el) { this.toggleClass('disabled', this._disabled) }
		return this
	}
	
	this._onTouchStart = function(e) {
		// TODO Don't listen to move, end or cancel until this happend
		if (e.touches.length > 1) { return }
		e.cancel()
		if (this._disabled) { return }
		var offset = this.getOffset()
		this._touchRect = new Rect(offset.left, offset.top, offset.width, offset.height).pad(10)
		this.addClass('active')
	}
	
	this._onTouchMove = function(e) {
		if (!this._touchRect) { return }
		var touch = e.touches[0]
		if (this._touchRect.containsPoint({ x:touch.pageX, y:touch.pageY })) { this.addClass('active') }
		else { this.removeClass('active') }
	}
	
	this._onTouchEnd = function(e) { this._endTouch(e) }
	this._onTouchCancel = function(e) { this._endTouch(e) }
	this._endTouch = function(e) {
		if (!this._touchRect) { return }
		e.cancel()
		delete this._touchRect
		var shouldClick = this.hasClass('active')
		this.removeClass('active')
		if (shouldClick) { this._clickHandler() }
		// TODO deregister touch events
	}
})
module.exports = Class(UIComponent, function(supr) {
	
	this._class = 'MetricView'

	this._init = function(metric) {
		supr(this, '_init')
		this._slice = new DataSlice(metric)
		this._views = {}
	}

	this._createContent = function() {
		this._createTitle()
		this._createViewPicker()
	}

	this.getSlice = function() { return this._slice }
	this.getGraph = function() { return this._views['Chart'] }

	this._createTitle = function() {
		this.append(this.dom({ html:this._slice.getTitle(), 'class':'title' }))
	}

	this._createViewPicker = function() {
		var viewClasses = { 'Chart':graphs.Line } // TODO Implement table view and actually append these guys to the dom
		var buttons = new RadioButtons()
			.on('select', bind(this, '_selectView'))
			//.appendTo(this)
		each(viewClasses, function(viewClass, name) {
			buttons.add({ label:name, 'class':viewClass })
		})
		buttons.select(0)
	}

	this._selectView = function(view) {
		if (this._currentView) { this._currentView.hide() }
		var views = this._views,
			viewName = view.label
		if (!views[viewName]) {
			views[viewName] = new view['class'](this._slice)
		}
		this._currentView = views[viewName]
			.appendTo(this)
			.show()
	}
})
Beispiel #5
0
module.exports = Class(function() {
	
	this.init = function(opts) {
		this._port = opts.port
		this._host = opts.host
		this._staticDir = path.normalize(opts.staticDir)
		this._printErrors = opts.printErrors
		this._finEngine = require('fin/engines/' + opts.engine)
		this._httpServer = http.createServer(bind(this, this._routeRequest))
		this._favicon = fs.readFileSync(__dirname + '/img/favicon.ico')
	}
	
	this.run = function() {
		this._setupRoutes()
		fin.mount(this._httpServer, this._finEngine, { 'transports':['xhr-polling', 'jsonp-polling'], 'log level': 1 })
		this._httpServer.listen(this._port, this._host)
	}
	
	/* Request routing
	 *****************/
	this._setupRoutes = function() {
		this._routes = []
		this._handlers = []
		this
			._setupRoute(/^\/$/, this._handleRootRequest)
			._setupRoute(/^\/(\w+)\/$/, this._handleClientHTMLRequest)
			._setupRoute(/^\/favicon\.ico$/, this._serveFavicon)
			._setupRoute(/^\/static\/([\w\d-]+)\/([^\.]+)\.(\w+)/, this._handleStaticRequest)
			._setupRoute(/^\/apple-touch-icon.*\.png$/, this._notFound)
	}
	
	this._setupRoute = function(regex, handler) {
		this._routes.push(regex)
		this._handlers.push(handler)
		return this
	}

	this._routeRequest = function(req, res) {
		var response = new Response(res, this._printErrors)
		for (var i=0, regex; regex=this._routes[i]; i++) {
			var match = req.url.match(regex)
			if (!match) { continue }
			this._handlers[i].call(this, match, req, response)
			return
		}
		response.sendError('Unknown URL ' + req.url, 404)
	}
	
	/* Request handlers
	 ******************/
	this._serveFavicon = function(match, req, res) {
		res
			.cache(30 * time.days)
			.send(this._favicon, 'image/ico')
	}
	
	this._handleRootRequest = function(match, req, res) {
		var reqClient = client.parse(req.headers['user-agent'])
		var location = 
			  reqClient.isIPad ? 'iphone'
			: reqClient.isIPhone ? 'iphone'
			: reqClient.isIPod ? 'iphone'
			: 'iphone'
		
		res
			.redirect('/'+location+'/')
			.cache(3 * time.days)
			.send()
	}
	
	this._clients = { browser:'browser/browser', iphone:'ios/iphone' }
	this._currentVersionLink = 'current'
	this._handleClientHTMLRequest = function(match, req, res) {
		var clientPathBase = this._clients[match[1]]
		if (!clientPathBase) { return res.sendError('No such client "'+match[1]+'"', 404) }
		var reqClient = client.parse(req.headers['user-agent'])
		if (reqClient.isIOS && reqClient.osVersion < 5.0) {
			this._sendJSError(res, 'Aw shucks. You need iOS 5.0')
			return
		}
		this._sendStaticFile(this._currentVersionLink, 'client/' + clientPathBase, 'html', res)
	}
	
	this._handleStaticRequest = function(match, req, res) {
		var version = match[1],
			path = match[2],
			extension = match[3]
		this._sendStaticFile(version, path, extension, res)
	}
	
	this._notFound = function(match, req, res) {
		res
			.noContent()
			.cache(3 * time.days)
			.send()
	}
	
	/* Util
	 ******/
	this._contentTypes = { html:'text/html', js:'text/javascript', png:'image/png' }
	this._sendStaticFile = function(version, pathBase, extension, res) {
		// TODO read from memory cache
		fs.readFile(this._getStaticPath(version, pathBase+'.'+extension), 'utf8', bind(this, function(err, contents) {
			// TODO cache in memory
			if (err) { return res.sendError(err, 404) }
			res
				.cache(3 * time.hours)
				.send(contents, this._contentTypes[extension])
		}))
	}
	
	this._getStaticPath = function(version, path) {
		return this._staticDir+'/'+version+'/'+path
	}
	
	this._sendJSError = function(res, message) {
		res
			.cache(3 * time.hours)
			.send('<script>alert("'+message+'")</script>', 'text/html')
	}
})
Beispiel #6
0
_NODES.createGeneratorWithoutClass = function(tag) {
	var ClassDefinition = Class(_NODES.NODE, function() { this._tag = tag })
	return function() { return new ClassDefinition([null].concat(slice(arguments, 0))) }
}
Beispiel #7
0
_NODES.NODE = Class(Component, function() {

	this.init = function(args) {
		// No need to call Component.init - Nodes are not expected to publish
		this._args = args
		this._handlers = {}
	}

	this.on = function(eventName, handler) {
		if (this._el) {
			Component.prototype.on.call(this, eventName, this._handlers[eventName]=bind(this, handler))
		} else {
			var arg = {}
			arg[eventName] = handler
			this._args.push(arg)
		}
		return this
	}
	
	this.off = function(eventName) {
		Component.prototype.off.call(this, eventName, this._handlers[eventName])
		delete this._handlers[eventName]
		return this
	}

	this.attributeHandlers = {
		click: curry(this.on, 'click'),
		mousedown: curry(this.on, 'mousedown'),
		mouseup: curry(this.on, 'mouseup'),
		mouseover: curry(this.on, 'mouseover'),
		mouseout: curry(this.on, 'mouseout'),
		keypress: curry(this.on, 'keypress'),
		keydown: curry(this.on, 'keydown'),
		keyup: curry(this.on, 'keyup'),
		blur: curry(this.on, 'blur'),
		focus: curry(this.on, 'focus'),
		change: curry(this.on, 'change'),
		touchstart: curry(this.on, 'touchstart'),
		touchend: curry(this.on, 'touchend'),
		touchmove: curry(this.on, 'touchmove'),
		touchcancel: curry(this.on, 'touchcancel'),
		load: curry(this.on, 'load'),
		submit: curry(this.on, 'submit'),
		scroll: curry(this.on, 'scroll'),
		className: this.addClass,
		style: this.style
	}

	this._attributeAttributes = { 'for':1 }

	this.renderContent = function() {
		var args = this._args
		if (typeof args[0] == 'string') {
			this._el.className = args[0]
			this._processArgs(args, 1)
		} else {
			this._processArgs(args, 0)
		}
	}

	this._processArgs = function(args, index) {
		if (this._class) {
			this._el.className = this._class
		}
		while (index < args.length) {
			this._processArg(args[index++])
		}
	}

	this._processArg = function(arg) {
		if (arg == null) { return }
		var node = this._el,
			doc = this._doc
		if (typeof arg._render == 'function') {
			node.appendChild(arg._render(null, doc))
		} else if (typeof arg == 'string' || typeof arg == 'number') {
			node.appendChild(doc.createTextNode(arg))
		} else if (arg.nodeType && arg.nodeType == 1) { // http://stackoverflow.com/questions/120262/whats-the-best-way-to-detect-if-a-given-javascript-object-is-a-dom-element
			node.appendChild(arg)
		} else if (isArray(arg)) {
			this._processArgs(arg, 0)
		} else if (typeof arg == 'function') {
			arg.call(this)
		} else {
			each(arg, this, function(val, key) {
				if (this.attributeHandlers[key]) {
					this.attributeHandlers[key].call(this, val)
				} else {
					if (this._attributeAttributes[key]) { node.setAttribute(key, val) }
					else { node[key] = val }
				}
			})
		}
	}

	this.append = function() {
		if (this._el) { this._processArgs(arguments, 0) }
		else { this._args = this._args.concat(slice(arguments)) }
		return this
	}
	
	this.text = function(text) {
		if (this._el) { Component.prototype.text.apply(this, arguments) }
		else { this._args.push(text.toString()) }
		return this
	}
})
Beispiel #8
0
module.exports = Class(function() {

	this._isOfflineKey = '__fin_isOffline_sync__'
	
	this.init = function(fin) {
		this._isOffline = true
		fin
			.subscribe('Online', bind(this, this._onOnline))
			.subscribe('Offline', bind(this, this._onOffline))
	}
	
	this.get = function(key) {
		return store.get(key)
	}
	
	this.set = function(key, value) {
		console.log('set', key, value, this._isOffline)
		if (this._isOffline) {
			var syncKeys = store.get(this._isOfflineKey)
			if (!syncKeys) { syncKets = {} }
			syncKeys[key] = true
			store.set(this._isOfflineKey, syncKeys)
		}
		store.set(key, value)
	}
	
	this._onOnline = function() {
		var keysToSync = store.get(this._isOfflineKey)
		store.remove(this._isOfflineKey)
		console.log('Keys to sync', keysToSync)
		this._isOffline = false
	}
	
	this._onOffline = function() {
		this._isOffline = true
	}
})
Beispiel #9
0
module.exports = Class(UIComponent, function(supr) {
	
	var defaults = {
		defaultValue: null
	}

	this._elTag = 'input'
	this._elType = 'text'

	this.init = function(opts) {
		supr(this, 'init', arguments)
		opts = extend(opts, defaults)
		this._defaultValue = opts.defaultValue
	}

	this.value = function(value) {
		var el = this.getElement()
		if (typeof value == 'undefined') { return el.value == this._defaultValue ? '' : el.value } // getter
		el.value = value
	}

	this._createContent = function() {
		this._on('focus', bind(this, '_onFocus'))
		this._on('blur', bind(this, '_onBlur'))
		this._on('keypress', bind(this, '_onKeyPress'))
		this._onBlur()
	}

	this._onFocus = function() {
		if (this._el.value != this._defaultValue) { return }
		this._el.value = ''
		this.removeClass('defaultValue')
	}

	this._onBlur = function() {
		if (this._el.value) { return }
		this._el.value = this._defaultValue
		this.addClass('defaultValue')
	}

	this._onKeyPress = function() {
		setTimeout(bind(this, '_publish', 'KeyPress'), 0)
	}
})
Beispiel #10
0
module.exports = Class(function() {
	
	this.init = function(res, printErrors) {
		this._res = res
		this._printErrors = printErrors
		this._code = 200
		this._headers = {}
	}
	
	this.cache = function(milliseconds) {
		// TODO what about the 'Cache-Control':'no-cache' header? and Pragma?
		this._headers['Expires'] = new Date(new Date().getTime() + milliseconds).toUTCString()
		return this
	}
	
	this.redirect = function(location) {
		this._code = 302
		this._headers['Location'] = location
		return this
	}
	
	this.noContent = function() {
		this._code = 204
		return this
	}
	
	this.send = function(data, contentType) {
		// TODO No-Content if content length == 0
		if (data) { this._headers['Content-Length'] = data.length }
		if (contentType) { this._headers['Content-Type'] = contentType }
		this._res.writeHead(this._code, this._headers)
		this._res.end(data)
		return this
	}
	
	this.sendError = function(err, code) {
		this._res.writeHead(code || 500)
		var message = this._printErrors && (err.stack ? err.stack : err.message || err)
		this._res.end(message)
		process.stderr.write("RESPONSE ERROR code:" + code + " error:" + (err.stack ? err.stack : (err + ": " + new Error().stack)) + "\n")
		return this
	}
})