chocolate
Version:
A full stack Node.js web framework built using Coffeescript
1,316 lines (1,040 loc) • 48.8 kB
text/coffeescript
# liteJq - lite jQuery
# adapted from
#https://github.com/soyjavi/QuoJS
#https://github.com/jquery/jquery
#https://github.com/dciccale/ki.js
#https://github.com/ded/domready
return unless window? and not (window.$? and window.$.fn? and window.$.fn.jquery?)
core_version = "0.1.1"
core_object = {}
core_array = []
# returns array of dom nodes
core_toArray = (a) ->
return a if $.type(a) is 'array'
i = -1 ; result = []
while item = a[++i] then result[i] = item
return result
core_hasOwn = core_object.hasOwnProperty
core_isArraylike = (obj) ->
length = obj.length ; type = $.type obj
return false if $.isWindow obj
return true if obj.nodeType is 1 and length
return type is "array" or type isnt "function" and (length is 0 or typeof length is "number" and length > 0 and (length - 1) of obj)
# $ definition
previous$ = window.$
$ = window.litejQ = (s) ->
return new $.prototype.init(s)
window.$ = $ unless previous$?
$.expando = "litejQ" + (core_version + Math.random()).replace /\D/g, ""
$.noConflict = ->
window.$ = previous$
return $
$.type = (obj) ->
return String(obj) unless obj?
class2type =
"[object Boolean]": "boolean"
"[object Number]": "number"
"[object String]": "string"
"[object Function]": "function"
"[object Array]": "array"
"[object Date]": "date"
"[object RegExp]": "regexp"
"[object Object]": "object"
"[object Error]": "error"
if typeof obj is "object" or typeof obj is "function" then class2type[Object::toString.call(obj)] or "object" else typeof obj
$.isArray = Array.isArray or (obj) -> $.type(obj) is "array"
$.isWindow = (obj) -> return obj? and obj is obj.window
$.isPlainObject = (obj) ->
# Must be an Object.
# Because of IE, we also have to check the presence of the constructor property.
# Make sure that DOM nodes and window objects don't pass through, as well
return no if not obj or $.type(obj) isnt "object" or obj.nodeType or $.isWindow(obj)
try # Not own constructor property must be Object
return no if obj.constructor and not core_hasOwn.call(obj, "constructor") and not core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf")
catch # IE8,9 Will throw exceptions on certain host objects #9897
return no
# Support: IE<9
# Handle iteration over inherited properties before own properties.
if $.support.ownLast
for key of obj then return core_hasOwn.call obj, key
# Own properties are enumerated firstly, so to speed up,
# if last one is own, then all properties are own.
for key of obj then {}
return key is undefined or core_hasOwn.call obj, key
# each method
# elements = the collection to iterate
# callback = the function to call each loop
# return this
$.each = (elements, callback) ->
if core_isArraylike elements
i = 0
while i < elements.length
return elements if callback.call(elements[i], i, elements[i]) is false
i++
else
for key of elements
return elements if callback.call(elements[key], key, elements[key]) is false
elements
$.map = (elems, callback, arg) ->
ret = [] ; i = 0 ; value = null
length = elems.length
_mapone = ->
value = callback elems[i], i, arg
ret[ret.length] = value if value?
# Go through the array, translating each of the items to their
if core_isArraylike(elems)
while i < length then _mapone() ; i++
# Go through every key on the object,
else
for i of elems then _mapone()
# Flatten any nested arrays
[].concat.apply [], ret
$.extend = ->
target = arguments[0] or {}
i = 1
length = arguments.length
deep = false
# Handle a deep copy situation
if typeof target is "boolean"
deep = target
target = arguments[1] or {}
# skip the boolean and the target
i = 2
# Handle case when target is a string or something (possible in deep copy)
target = {} if typeof target isnt "object" and not $.type(target) is "function"
# extend $ itself if only one argument is passed
if length is i then target = @ ; --i
while i < length
# Only deal with non-null/undefined values
if (options = arguments[i])?
# Extend the base object
for name of options
src = target[name]
copy = options[name]
# Prevent never-ending loop
if target is copy then continue
# Recurse if we're merging plain objects or arrays
if deep and copy and ($.isPlainObject(copy) or (copyIsArray = $.isArray(copy)))
if copyIsArray
copyIsArray = no
clone = if src and $.isArray(src) then src else []
else
clone = if src and $.isPlainObject(src) then src else {}
# Never move original objects, clone them
target[name] = $.extend deep, clone, copy
# Don't bring in undefined values
else if copy isnt undefined
target[name] = copy
i += 1
# Return the modified object
target
$.globalEval = (text) ->
return text unless text
if window.execScript then window.execScript text
else
script = document.createElement 'script'
script.setAttribute 'type', 'text/javascript'
script.text = text
document.head.appendChild script
document.head.removeChild script
return text
$.Environment = do ->
_current = null
Mobile_is_webkit = /WebKit\/([\d.]+)/
Mobile_supported_os =
Android: /(Android)\s+([\d.]+)/
ipad: /(iPad).*OS\s([\d_]+)/
iphone: /(iPhone\sOS)\s([\d_]+)/
Blackberry: /(BlackBerry|BB10|Playbook).*Version\/([\d.]+)/
FirefoxOS: /(Mozilla).*Mobile[^\/]*\/([\d\.]*)/
webOS: /(webOS|hpwOS)[\s\/]([\d.]+)/
_detectEnvironment = ->
user_agent = navigator.userAgent
environment = {}
environment.browser = _detectBrowser(user_agent)
environment.os = _detectOS(user_agent)
environment.isMobile = !!environment.os
environment.screen = _detectScreen()
environment
_detectBrowser = (user_agent) ->
is_webkit = user_agent.match(Mobile_is_webkit)
if is_webkit then is_webkit[0] else user_agent
_detectOS = (user_agent) ->
detected_os = null
for os of Mobile_supported_os
supported = user_agent.match(Mobile_supported_os[os])
if supported
detected_os =
name: (if (os is "iphone" or os is "ipad") then "ios" else os)
version: supported[2].replace("_", ".")
break
detected_os
_detectScreen = ->
width: window.innerWidth
height: window.innerHeight
isMobile: ->
_current = _current or _detectEnvironment()
_current.isMobile
environment: ->
_current = _current or _detectEnvironment()
_current
isOnline: ->
navigator.onLine
$.websocket = (url, options = {}) ->
# Creates a fallback object implementing the WebSocket interface
FallbackSocket = (url, options = {}) ->
# WebSocket interface constants
CONNECTING = 0
OPEN = 1
CLOSING = 2
CLOSED = 3
pollInterval = null
openTimout = null
options.fallbackPollInterval ?= 3000
options.fallbackPollParams ?= {}
options.fallbackOnly ?= false
furl = url.replace('ws:', 'http:').replace('wss:', 'https:')
# create WebSocket object
fws =
# ready state
readyState: CONNECTING
bufferedAmount: 0
send: (data) ->
return false unless fws.socket_id?
success = true
$.ajax
type: 'POST'
url: furl + '?' + $.param getFallbackParams()
data: data
dataType: 'text'
headers:
'x-litejq-websocket':'fallback'
'x-litejq-websocket-id': fws.socket_id
contentType : "application/x-www-form-urlencoded; charset=utf-8"
success: (data) -> fws.onmessage "data" : data
error: (xhr) -> success = false; fws.onerror(xhr)
return success
close: () ->
clearTimeout openTimout
clearInterval pollInterval
this.readyState = CLOSED
fws.onclose()
onopen: ->
onmessage: ->
onerror: ->
onclose: ->
previousRequest: null
currentRequest: null
socket_id: null
getFallbackParams = () ->
# update timestamp of previous and current poll request
fws.previousRequest = fws.currentRequest;
fws.currentRequest = new Date().getTime();
# extend default params with plugin options
return $.extend {"previousRequest": fws.previousRequest, "currentRequest": fws.currentRequest}, options.fallbackPollParams
poll = ->
return unless fws.socket_id?
$.ajax
type: 'GET'
url: furl + '?' + $.param getFallbackParams()
dataType: 'text'
headers:
'x-litejq-websocket':'fallback'
'x-litejq-websocket-id': fws.socket_id
success: (data) ->
fws.onmessage "data" : data
if data? and data isnt '' then setTimeout poll, 0
error: (xhr) -> fws.onerror(xhr)
$.ajax
type: 'OPTIONS'
url: furl
dataType: 'text'
headers: 'x-litejq-websocket':'fallback'
success: (data, xhr) ->
fws.socket_id = xhr.getResponseHeader('x-litejq-websocket-id')
fws.readyState = OPEN
fws.onopen()
poll()
pollInterval = setInterval poll, options.fallbackPollInterval
error: (xhr) -> fws.onerror(xhr)
return fws
if options.fallbackOnly or not window.WebSocket then new FallbackSocket(url, options)
else new WebSocket(url)
$.Ajax = do ->
Ajax_default =
type: "GET"
mime: "guess"
Ajax_mime_types =
script: "text/javascript, application/javascript"
json: "application/json"
'json-late': "application/json-late"
xml: "application/xml, text/xml"
html: "text/html"
text: "text/plain"
guess: "application/xml, text/xml, application/json, application/json-late, text/javascript, application/javascript, text/plain, text/html"
Ajax_jsonp_id = 0
Ajax_settings =
type: Ajax_default.type
async: true
success: ->
error: ->
context: null
dataType: Ajax_default.mime
headers: {'x-requested-with':'XMLHttpRequest'}
xhr: -> new window.XMLHttpRequest()
crossDomain: false
timeout: 0
cache: true
$.ajax = (url, options) ->
options = if typeof url is "object" then url else $.extend options, {url}
settings = $.extend {}, Ajax_settings, options
if settings.type is Ajax_default.type
if settings.data?
settings.url += "?" + $.param(settings.data)
delete settings.data
else
settings.data = $.param(settings.data)
dataType = settings.dataType.toLowerCase()
if settings.cache is off or options?.cache isnt on and dataType in ['jsonp', 'script']
settings.url += (if settings.url.indexOf('?') >= 0 then '&' else '?') + "_=" + (_xhrId++).toString(36)
return _jsonp(settings) if dataType is 'jsonp'
xhr = settings.xhr()
xhr.onreadystatechange = ->
if xhr.readyState is 4
clearTimeout abortTimeout
_xhrStatus xhr, settings
xhr.open settings.type, settings.url, settings.async
_xhrHeaders xhr, settings
if settings.timeout > 0
abortTimeout = setTimeout((-> _xhrTimeout xhr, settings ), settings.timeout)
try
xhr.send settings.data
catch error
xhr = error
_xhrError "Resource not found", xhr, settings
(if (settings.async) then xhr else _parseResponse(xhr, settings))
$.get = (url, data, success, dataType) ->
$.ajax
url: url
data: data
success: success
dataType: dataType
$.post = (url, data, success, dataType) -> _xhrForm("POST", url, data, success, dataType)
$.getJSON = $.getJson = (url, data, success) -> $.get url, data, success, 'json'
$.getScript = (url, success) -> $.get url, undefined, success, "script"
$.param = (parameters) ->
return encodeURIComponent parameters if $.type(parameters) is 'string'
serialize = []
for parameter of parameters
if parameters.hasOwnProperty(parameter)
serialize.push "#{encodeURIComponent parameter}=#{encodeURIComponent parameters[parameter]}"
serialize.join '&'
_xhrId = Date.now()
_xhrStatus = (xhr, settings) ->
if (xhr.status >= 200 and xhr.status < 300) or xhr.status is 0
if settings.async
_xhrSuccess _parseResponse(xhr, settings), xhr, settings
return
else
_xhrError "$.ajax: Unsuccesful request", xhr, settings
return
_xhrSuccess = (response, xhr, settings) ->
settings.success.call settings.context, response, xhr
return
_xhrError = (type, xhr, settings) ->
settings.error.call settings.context, type, xhr, settings
return
_xhrHeaders = (xhr, settings) ->
settings.headers["Content-Type"] = settings.contentType if settings.contentType
settings.headers["Accept"] = Ajax_mime_types[settings.dataType] if settings.dataType
for header of settings.headers
xhr.setRequestHeader header, settings.headers[header]
return
_xhrTimeout = (xhr, settings) ->
xhr.onreadystatechange = {}
xhr.abort()
_xhrError "$.ajax: Timeout exceeded", xhr, settings
return
_xhrForm = (method, url, data, success, dataType) ->
$.ajax
type: method
url: url
data: data
success: success
dataType: dataType
contentType: "application/x-www-form-urlencoded"
_parseResponse = (xhr, settings) ->
response = xhr.responseText
if response
switch settings.dataType
when 'json'
try
response = JSON.parse(response)
catch error
response = error
_xhrError "$.ajax: Parse Error", xhr, settings
when "xml"
response = xhr.responseXML
when "script"
$.globalEval response
response
_jsonp = (settings) ->
if settings.async
callbackName = "jsonp" + (++Ajax_jsonp_id)
script = document.createElement("script")
xhr = abort: ->
$(script).remove()
window[callbackName] = {} if callbackName of window
abortTimeout = undefined
window[callbackName] = (response) ->
clearTimeout abortTimeout
$(script).remove()
delete window[callbackName]
_xhrSuccess response, xhr, settings
script.src = settings.url + (if settings.url.indexOf('?') >= 0 then '&' else '?') + "callback=" + callbackName
$("head").append script
if settings.timeout > 0
abortTimeout = setTimeout((-> _xhrTimeout xhr, settings), settings.timeout)
xhr
else
console.error "$.ajax: Unable to make jsonp synchronous call."
# Query functions
Query = do ($) ->
_getSibling = (command) -> @map (index, element) ->
element = element[command]
element = element[command] while element and element.nodeType isnt 1
[element]
_query = (domain, selector) ->
selector = selector.trim()
if /^\.([\w-]+)$/.test selector
elements = domain.getElementsByClassName selector.replace ".", ""
else if /^[\w-]+$/.test selector
elements = domain.getElementsByTagName selector
else if /^#[\w\d-]+$/.test(selector) and domain is document
elements = domain.getElementById selector.replace "#", ""
unless elements then elements = []
else
elements = domain.querySelectorAll selector
if elements.nodeType then [elements] else [].slice.call core_toArray elements
_findAncestors = (nodes, depth) ->
ancestors = []
while nodes.length > 0 then nodes = $.map nodes, (node) ->
if depth isnt 0 and (node = node.parentNode) and node isnt document and ancestors.indexOf(node) < 0
ancestors.push node
depth += -1 if depth > 0
node
ancestors
_filtered = (nodes, selector) ->
if selector is undefined then $(nodes) else $(nodes).filter selector
_findParents = (selector, depth) ->
instance = (property) => @map -> @[property]
_filtered (if selector? then _findAncestors @, depth else instance "parentNode"), selector
filter: (selector) ->
$ [].filter.call(this, (element) ->
element.parentNode and _query(element.parentNode, selector).indexOf(element) >= 0)
find: (selector) ->
$(if @length is 1 then _query @[0], selector else @map -> _query @, selector)
parent: (selector) ->
_findParents.call @, selector, 1
parents: (selector) ->
_findParents.call @, selector
siblings: (selector) ->
siblings = @map (index, element) ->
[].slice.call(core_toArray element.parentNode.children).filter (child) ->
child isnt element
_filtered siblings, selector
children: (selector) ->
_filtered (@map -> [].slice.call core_toArray @children), selector
first: -> $ @[0]
last: -> $ @[@length - 1]
next: -> $ _getSibling.call @, "nextSibling"
prev: -> $ _getSibling.call @, "previousSibling"
closest: (selector, context) ->
node = @[0]
candidates = $(selector)
node = null unless candidates.length
while node and candidates.indexOf(node) < 0
node = node isnt context and node isnt document and node.parentNode
$(node)
# Events functions
Events = do ($) ->
$.Event = (src, props) ->
# Allow instantiation without the 'new' keyword
return new $.Event(src, props) unless this instanceof $.Event
# Event object
if src and src.type
@originalEvent = src
@type = src.type
# Events bubbling up the document may have been marked as prevented
# by a handler lower down the tree; reflect the correct value.
@isDefaultPrevented = (if (src.defaultPrevented or src.returnValue is false or src.getPreventDefault and src.getPreventDefault()) then _returnTrue else _returnFalse)
# Event type
else
@type = src
# Put explicitly provided properties onto the event object
$.extend this, props if props
# Create a timestamp if incoming event doesn't have one
@timeStamp = src and src.timeStamp or $.now()
# Mark it as fixed
this[$.expando] = on
$.now = -> (new Date()).getTime()
$.event =
fix: (event) ->
return event if event[$.expando]
# Create a writable copy of the event object and normalize some properties
type = event.type
originalEvent = event
fixHook = Events_fixHooks[type]
Events_fixHooks[type] = fixHook = (if /^(?:mouse|contextmenu)|click/.test(type) then Events_mouseHooks else (if /^key/.test(type) then Events_keyHooks else {})) unless fixHook
copy = if fixHook.props then Events_props.concat(fixHook.props) else Events_props
event = new $.Event originalEvent
i = copy.length ; while i-- then prop = copy[i] ; event[prop] = originalEvent[prop]
# Support: IE<9
# Fix target property
event.target = originalEvent.srcElement or document unless event.target
# Support: Chrome 23+, Safari?
# Target should not be a text node
event.target = event.target.parentNode if event.target.nodeType is 3
# Support: IE<9
# For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
event.metaKey = !!event.metaKey
if fixHook.filter then fixHook.filter(event, originalEvent) else event
add: (element, event_name, callback) ->
if element.addEventListener?
element.addEventListener event_name, callback, false
else if element.attachEvent?
element.attachEvent "on" + event_name, callback
else
element["on" + event_name] = callback
remove: (element, event_name, callback) ->
if element.removeEventListener
element.removeEventListener event_name, callback, false
else if element.detachEvent
element.detachEvent "on" + event_name, callback
else
element["on" + event_name] = null
Events_handlers = {}
Events_id = 1
Events_methods =
preventDefault: "isDefaultPrevented"
stopImmediatePropagation: "isImmediatePropagationStopped"
stopPropagation: "isPropagationStopped"
Events_desktop =
touchstart: "mousedown"
touchmove: "mousemove"
touchend: "mouseup"
touch: "click"
doubletap: "dblclick"
orientationchange: "resize"
# Includes some event props shared by KeyEvent and MouseEvent
Events_props = "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" ")
Events_fixHooks = {}
Events_keyHooks =
props: "char charCode key keyCode".split ' '
filter: (event, original) ->
# Add which for key events
event.which = (if original.charCode? then original.charCode else original.keyCode) unless event.which?
event
Events_mouseHooks =
props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" ")
filter: (event, original) ->
button = original.button
fromElement = original.fromElement
# Calculate pageX/Y if missing and clientX/Y available
if not event.pageX? and original.clientX?
eventDoc = event.target.ownerDocument or document
doc = eventDoc.documentElement
body = eventDoc.body
event.pageX = original.clientX + (doc and doc.scrollLeft or body and body.scrollLeft or 0) - (doc and doc.clientLeft or body and body.clientLeft or 0)
event.pageY = original.clientY + (doc and doc.scrollTop or body and body.scrollTop or 0) - (doc and doc.clientTop or body and body.clientTop or 0)
# Add relatedTarget, if necessary
event.relatedTarget = (if fromElement is event.target then original.toElement else fromElement) if not event.relatedTarget and fromElement
# Add which for click: 1 === left; 2 === middle; 3 === right
# Note: button is not normalized, so don't use it
event.which = ((if button & 1 then 1 else ((if button & 2 then 3 else ((if button & 4 then 2 else 0)))))) if not event.which and button isnt `undefined`
event
_subscribe = (element, event, callback, selector, delegate_callback) ->
event = _environmentEvent event
element_id = _getElementId element
element_handlers = Events_handlers[element_id] or (Events_handlers[element_id] = [])
delegate = delegate_callback and delegate_callback callback, event
handler =
event: event
callback: callback
selector: selector
proxy: _createProxyCallback delegate, callback, element
delegate: delegate
index: element_handlers.length
element_handlers.push handler
$.event.add element, handler.event, handler.proxy
_unsubscribe = (element, event, callback, selector) ->
event = _environmentEvent event
element_id = _getElementId element
for handler in _findHandlers(element_id, event, callback, selector)
delete Events_handlers[element_id][handler.index]
$.event.remove element, handler.event, handler.proxy
_getElementId = (element) -> element._id or (element._id = Events_id++)
_environmentEvent = (event) ->
environment_event = (if ($.Environment.isMobile()) then event else Events_desktop[event])
environment_event or event
_createProxyCallback = (delegate, callback, element) ->
callback = delegate or callback
proxy = (event) ->
event = $.event.fix event
result = callback.apply element, [event].concat event.data
event.preventDefault() if result is no
result
proxy
_findHandlers = (element_id, event, fn, selector) ->
(Events_handlers[element_id] or []).filter (handler) ->
handler and (not event or handler.event is event) and (not fn or handler.callback is fn) and (not selector or handler.selector is selector)
_createProxy = (event) ->
proxy = $.extend(originalEvent: event, event)
$.each Events_methods, (name, method) ->
proxy[name] = ->
@[method] = -> yes
event[name].apply event, arguments
proxy[method] = -> no
proxy
_returnTrue = -> yes
_returnFalse = -> no
# ready method
# a = function to call when dom is ready
# return this
ready: (callback) ->
fns = []
hack = document.documentElement.doScroll
loaded = (if hack then /^loaded|^c/ else /^loaded|c/).test document['readyState']
flush = (f) ->
loaded = 1 ; while f = fns.shift() then f()
document.addEventListener? 'DOMContentLoaded', fn = ->
document.removeEventListener 'DOMContentLoaded', fn, no
flush()
, no
if hack then document.attachEvent 'onreadystatechange', fn = ->
if /^c/.test document['readyState']
document.detachEvent 'onreadystatechange', fn
flush()
if hack
if window.top isnt window.self
if loaded then callback() else fns.push callback
else
do wait = ->
try document.documentElement.doScroll 'left'
catch then return setTimeout (-> wait() ), 50
callback()
else
if loaded then callback() else fns.push callback
return @
# on method
# event = string event type i.e 'click'
# selector = target an item for delegation
# callback = function
# return this
on: (event, selector, callback) ->
if not selector? or typeof selector is "function" then @bind(event, selector) else @delegate(selector, event, callback)
# off method
# event = string event type i.e 'click'
# selector = target an item for delegation
# callback = function
# return this
off: (event, selector, callback) ->
if not selector? or typeof selector is "function" then @unbind(event, selector) else @undelegate(selector, event, callback)
bind: (event, callback) ->
@each -> _subscribe @, event, callback ; return
unbind: (event, callback) ->
@each -> _unsubscribe @, event, callback ; return
delegate: (selector, event, callback) ->
@each (i, element) ->
_subscribe element, event, callback, selector, (fn) ->
(e) ->
if match = $(e.target or e.srcElement or document).closest(selector, element).get 0
evt = $.extend _createProxy(e),
currentTarget: match
liveFired: element
fn.apply match, [ evt ].concat([].slice.call(arguments, 1))
return
undelegate: (selector, event, callback) ->
@each ->
_unsubscribe @, event, callback, selector
return
triggerHandler: (event, args) ->
result = null
@each (i, element) ->
e = _createProxy(if $.type(event) is 'string' then $.Event(event) else event)
e._args = args
e.target = element
element_id = _getElementId element
$.each _findHandlers(element_id, event.type ? event), (i, handler) ->
result = handler.proxy e
return false if e.isImmediatePropagationStopped?()
result
# Style functions
Style = do ($) ->
_existsClass = (name, className) ->
classes = className.split /\s+/g
classes.indexOf(name) >= 0
_computedStyle = (element, property) ->
window.getComputedStyle(element, "").getPropertyValue property
addClass: (name) ->
@each ->
unless _existsClass(name, @className)
@className += " " + name
@className = @className.trim()
removeClass: (name) ->
@each ->
unless name
@className = ""
else
@className = @className.replace(name, " ").replace(/\s+/g, " ").trim() if _existsClass(name, @className)
toggleClass: (name) ->
@each ->
if _existsClass(name, @className)
@className = @className.replace(name, " ")
else
@className += " " + name
@className = @className.trim()
hasClass: (name) ->
this.length > 0 and _existsClass name, this[0].className
css: (property, value) ->
if typeof value != "undefined"
@each -> @style[property] = value
else if this.length > 0
@[0].style[property] or _computedStyle(@[0], property)
# Dom functions
Dom = do ($) ->
_stripScripts = (text, callback) ->
scripts = []
html = text.replace /<script([\s\S]*?)>([\s\S]*?)<\/script>/gi, (all, attr, code) -> scripts.push {attr, code}; return ''
callback.call @, html
if scripts.length > 0
for item in scripts
src = item.attr.match(/src.*?=.*?[\"'](.+?)[\"']/i)
src = src[1] if src?
code = item.code
if (src isnt null and src isnt '') or code isnt ''
script = document.createElement 'script'
script.setAttribute 'src', src if src isnt null
script.setAttribute 'type', 'text/javascript'
script.text = code if code isnt ""
@appendChild script
return
text: (value) ->
unless value?
ret = []
ret.push @[i][(if @[i].textContent? then 'textContent' else 'innerText')] for i in [0...@length]
ret.join ''
else
@.empty().append((@[0] and @[0].ownerDocument or document).createTextNode(value));
html: (value) ->
type = $.type(value)
if type is "undefined"
(if @length > 0 then @[0].innerHTML else)
else
@each ->
if type is "string"
_stripScripts.call @, value, (html) -> @innerHTML = html
else if type is "array"
$(@).html(v) for v in value
else
@innerHTML = @innerHTML + $(value).html()
append: (value) ->
type = $.type(value)
@each ->
if type is "string"
_stripScripts.call @, value, (html) -> @insertAdjacentHTML "beforeend", html
else if type is "array" or value?.length? and core_isArraylike value
$(@).append(v) for v in value
else
@appendChild value
prepend: (value) ->
type = $.type(value)
@each ->
if type is "string"
_stripScripts.call @, value, (html) -> @insertAdjacentHTML "afterbegin", html
else if type is "array"
$(@).prepend(v) for v in value
else
@insertBefore value, @firstChild
replaceWith: (value) ->
type = $.type(value)
@each ->
if @parentNode
if type is "string"
_stripScripts.call @, value, (html) -> @insertAdjacentHTML "beforeBegin", html
else if type is "array"
$(@).replaceWith(v) for v in value
else
@parentNode.insertBefore value, @
@remove()
after: (value) ->
type = $.type(value)
@each ->
if @parentNode
if type is "string"
_stripScripts.call @, value, (html) -> @insertAdjacentHTML "afterend", html
else if type is "array"
$(@).after(v) for v in value
else
@parentNode.insertBefore value, $(@).next()
empty: () ->
@each -> @innerHTML = ""
attr: (name, value) ->
if this.length is 0 then null
if $.type(name) is "string" and value is undefined
this[0].getAttribute name
else
@each -> @setAttribute name, value
removeAttr: (name) ->
@each -> @removeAttribute name
data: (name, value) -> @attr "data-" + name, value
removeData: (name) -> @removeAttr "data-" + name
val: (value) ->
if $.type(value) is "string" then @each -> @value = value
else (if @length > 0 then this[0].value else null)
show: -> @css "display", ""
hide: -> @css "display", "none"
height: ->
offset = @offset()
offset.height
width: ->
offset = @offset()
offset.width
offset: ->
return {} if this.length is 0
bounding = this[0].getBoundingClientRect()
left: bounding.left + (window.pageXOffset ? document.documentElement.scrollLeft)
top: bounding.top + (window.pageYOffset ? document.documentElement.scrollTop)
width: bounding.right - bounding.left
height: bounding.bottom - bounding.top
remove: ->
@each -> @parentNode.removeChild this if @parentNode?
# core prototype
core =
# default length
length: 0
# init method (internal use)
# a = selector, dom element, function, object or array
init: (a) ->
core_array.push.apply @, if a and a.nodeType then [a]
else (if "" + a is a
if a[0] is "<" and a[a.length-1] is ">" and a.length >= 3
len = a.length - 1 - if (a[a.length-2] is '/') then 1 else 0
[document.createElement(a.slice 1, len)]
else core_toArray(document.querySelectorAll(a))
else (if /^f/.test(typeof a) then [$(document).ready(a)]
else switch $.type(a)
when 'array' then a
when 'object' then [a]
else[]))
toArray: -> [].slice.call this
get: (num) ->
if num == null then @toArray()
else if num < 0 then @[@.length + num] else @[num]
each: (callback, args) ->
$.each @, callback, args
splice: core_array.splice
map: (fn) ->
$.map @, (el, i) -> fn.call el, i, el
'indexOf': core_array.indexOf
core[k] = v for k,v of o for o in [Query, Events, Style, Dom]
# set prototypes
$.prototype = $.fn = core.init.prototype = core
# -----------------------
# Support
$.support = {}
# Support: IE<8
# window.Element
document.querySelectorAll ?= do ->
style = document.createStyleSheet()
(selectors) ->
all = document.all
elements = []
selectors = selectors.replace(/\[for\b/gi, '[htmlFor').split ','
for i in [0...selectors.length] by 1
style.addRule selectors[i], 'k:v', 0
for j in [0...all.length] by 1 then all[j].currentStyle.k and elements.push all[j]
style.removeRule 0
elements
document.querySelector ?= (selectors) ->
elements = document.querySelectorAll selectors
return if elements.length then elements[0] else null
document.getElementsByClassName ?= (classNames) ->
classNames = String(classNames).replace /^|\s+/g, '.'
return document.querySelectorAll classNames
# Array filter
Array::filter ?= (fn) ->
throw new TypeError() unless this?
throw new TypeError() unless typeof fn is "function"
t = Object(this) ; len = t.length >>> 0
res = [] ; thisp = arguments[1]
i = 0
while i < len
if i of t
val = t[i]
res.push val if fn.call thisp, val, i, t
i++
res
# Trim
String::trim ?= -> return this.replace /^\s+|\s+$/g, ''
# JSON
window.JSON ?= do ->
cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
gap = undefined
indent = undefined
meta =
"\b": "\\b"
"\t": "\\t"
"\n": "\\n"
"\f": "\\f"
"\r": "\\r"
"\"": "\\\""
"\\": "\\\\"
rep = undefined
f = (n) -> (if n < 10 then "0" + n else n)
quote = (string) ->
escapable.lastIndex = 0
(if escapable.test(string) then "\"" + string.replace(escapable, (a) ->
c = meta[a]
(if typeof c is "string" then c else "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4))
) + "\"" else "\"" + string + "\"")
str = (key, holder) ->
mind = gap
value = holder[key]
value = value.toJSON(key) if value and typeof value is "object" and typeof value.toJSON is "function"
value = rep.call(holder, key, value) if typeof rep is "function"
switch typeof value
when "string" then quote value
when "number" then (if isFinite(value) then String(value) else "null")
when "boolean", "null"then String value
when "object"
return "null" unless value
gap += indent
partial = []
if Object::toString.apply(value) is "[object Array]"
length = value.length
i = 0
while i < length
partial[i] = str(i, value) or "null"
i += 1
v = (if partial.length is 0 then "[]" else (if gap then "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" else "[" + partial.join(",") + "]"))
gap = mind
return v
if rep and typeof rep is "object"
length = rep.length
i = 0
while i < length
if typeof rep[i] is "string"
k = rep[i]
v = str(k, value)
partial.push quote(k) + (if gap then ": " else ":") + v if v
i += 1
else
for k of value
if Object::hasOwnProperty.call(value, k)
v = str(k, value)
partial.push quote(k) + (if gap then ": " else ":") + v if v
v = (if partial.length is 0 then "{}" else (if gap then "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" else "{" + partial.join(",") + "}"))
gap = mind
v
if typeof Date::toJSON isnt "function"
Date::toJSON = -> (if isFinite(@valueOf()) then @getUTCFullYear() + "-" + f(@getUTCMonth() + 1) + "-" + f(@getUTCDate()) + "T" + f(@getUTCHours()) + ":" + f(@getUTCMinutes()) + ":" + f(@getUTCSeconds()) + "Z" else null)
String::toJSON = Number::toJSON = Boolean::toJSON = -> @valueOf()
stringify: (value, replacer, space) ->
gap = ""
indent = ""
if typeof space is "number" then i = 0 ; while i < space then indent += " " : i += 1
else indent = space if typeof space is "string"
rep = replacer
throw new Error("JSON.stringify") if replacer and typeof replacer isnt "function" and (typeof replacer isnt "object" or typeof replacer.length isnt "number")
str "", "": value
parse: (text, reviver) ->
walk = (holder, key) ->
value = holder[key]
if value and typeof value is "object"
for k of value
if Object::hasOwnProperty.call(value, k)
v = walk(value, k)
if v isnt `undefined` then value[k] = v
else delete value[k]
reviver.call holder, key, value
j = undefined
text = String(text)
cx.lastIndex = 0
if cx.test(text)
text = text.replace(cx, (a) ->
"\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4))
if /^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]").replace(/(?:^|:|,)(?:\s*\[)+/g, ""))
j = eval("(" + text + ")")
return (if typeof reviver is "function" then walk("": j, "") else j)
throw new SyntaxError("JSON.parse")
# window.getComputedStyle
window.getComputedStyle ?= do ->
getComputedStylePixel = (element, property, fontSize) ->
value = element.currentStyle[property].match(/([\d\.]+)(%|cm|em|in|mm|pc|pt|)/) or [0, 0, ""]
size = value[1]
suffix = value[2]
fontSize = (if not fontSize then fontSize else (if /%|em/.test(suffix) and element.parentElement then getComputedStylePixel(element.parentElement, "fontSize", null) else 16))
rootSize = (if property is "fontSize" then fontSize else (if /width/i.test(property) then element.clientWidth else element.clientHeight))
(if suffix is "%" then size / 100 * rootSize else (if suffix is "cm" then size * 0.3937 * 96 else (if suffix is "em" then size * fontSize else (if suffix is "in" then size * 96 else (if suffix is "mm" then size * 0.3937 * 96 / 10 else (if suffix is "pc" then size * 12 * 96 / 72 else (if suffix is "pt" then size * 96 / 72 else size)))))))
setShortStyleProperty = (style, property) ->
borderSuffix = (if property is "border" then "Width" else "")
t = property + "Top" + borderSuffix
r = property + "Right" + borderSuffix
b = property + "Bottom" + borderSuffix
l = property + "Left" + borderSuffix
style[property] = ((if style[t] is style[r] and style[t] is style[b] and style[t] is style[l] then [style[t]] else (if style[t] is style[b] and style[l] is style[r] then [style[t], style[r]] else (if style[l] is style[r] then [style[t], style[r], style[b]] else [style[t], style[r], style[b], style[l]])))).join(" ")
# <CSSStyleDeclaration>
CSSStyleDeclaration = (element) ->
style = this
currentStyle = element.currentStyle
fontSize = getComputedStylePixel(element, "fontSize")
unCamelCase = (match) ->
"-" + match.toLowerCase()
for property of currentStyle
Array::push.call style, (if property is "styleFloat" then "float" else property.replace(/[A-Z]/, unCamelCase))
if property is "width"
style[property] = element.offsetWidth + "px"
else if property is "height"
style[property] = element.offsetHeight + "px"
else if property is "styleFloat"
style.float = currentStyle[property]
else if /margin.|padding.|border.+W/.test(property) and style[property] isnt "auto"
style[property] = Math.round(getComputedStylePixel(element, property, fontSize)) + "px"
else
style[property] = currentStyle[property]
setShortStyleProperty style, "margin"
setShortStyleProperty style, "padding"
setShortStyleProperty style, "border"
style.fontSize = Math.round(fontSize) + "px"
CSSStyleDeclaration:: =
constructor: CSSStyleDeclaration
getPropertyPriority: ->
throw new Error "NotSupportedError: DOM Exception 9"
getPropertyValue: (property) ->
this[property.replace(/-\w/g, (match) ->
match[1].toUpperCase()
)]
item: (index) -> this[index]
removeProperty: -> throw new Error "NoModificationAllowedError: DOM Exception 7"
setProperty: -> throw new Error "NoModificationAllowedError: DOM Exception 7"
getPropertyCSSValue: -> throw new Error "NotSupportedError: DOM Exception 9"
# <window>.getComputedStyle
(element) -> new CSSStyleDeclaration element
# Support: IE<9
# Iteration over object's inherited properties before its own.
# (internal use)
# Selectors API Level 1 (http://www.w3.org/TR/selectors-api/)
# http://ajaxian.com/archives/creating-a-queryselector-for-ie-that-runs-at-native-speed
for i of $($.support) then break
$.support.ownLast = i isnt "0"
# indexOf
Array.prototype.indexOf ?= (elem) ->
if this.charAt?
for i in [0...this.length] by 1
if this.charAt(i) is elem then return i
else
for i in [0...this.length] by 1
if this[i] is elem then return i
return -1;