Restructured of lib directory.

This commit is contained in:
Martin Edenhofer 2012-10-31 09:28:48 +01:00
parent c3d7391e02
commit e27664ec1c
34 changed files with 16331 additions and 24 deletions

View file

@ -1,40 +1,30 @@
#s#= require json2
#= require ./lib/jquery-1.8.1.min.js
#= require ./lib/ui/jquery-ui-1.8.23.custom.min.js
#= require ./lib/core/jquery-1.8.1.min.js
#= require ./lib/core/jquery-ui-1.8.23.custom.min.js
#= require ./lib/core/underscore-1.3.3.js
#not_used= require_tree ./lib/spine
#= require ./lib/spine/spine.js
#= require ./lib/spine/ajax.js
#= require ./lib/spine/route.js
#= require ./lib/bootstrap-dropdown.js
#= require ./lib/bootstrap-tooltip.js
#= require ./lib/bootstrap-popover.js
#= require ./lib/bootstrap-modal.js
#= require ./lib/bootstrap-tab.js
#= require ./lib/bootstrap-transition.js
#not_used= require_tree ./lib/bootstrap
#= require ./lib/bootstrap/bootstrap-dropdown.js
#= require ./lib/bootstrap/bootstrap-tooltip.js
#= require ./lib/bootstrap/bootstrap-popover.js
#= require ./lib/bootstrap/bootstrap-modal.js
#= require ./lib/bootstrap/bootstrap-tab.js
#= require ./lib/bootstrap/bootstrap-transition.js
#= require ./lib/underscore-1.3.3.js
#= require ./lib/ba-linkify.js
#= require ./lib/jquery.tagsinput.js
#= require ./lib/jquery.noty.js
#= require ./lib/waypoints.js
#= require ./lib/fileuploader.js
#= require ./lib/jquery.elastic.source.js
#= require_tree ./lib/base
#not_used= require_tree ./lib
#= require_self
#= require_tree ./models
#= require_tree ./controllers
#= require_tree ./views
#= require ./lib/ajax.js.coffee
#= require ./lib/clipboard.js.coffee
#= require ./lib/websocket.js.coffee
#= require ./lib/auth.js.coffee
#= require ./lib/i18n.js.coffee
#= require ./lib/store.js.coffee
#= require ./lib/collection.js.coffee
#= require ./lib/event.js.coffee
#= require ./lib/interface_handle.js.coffee
#= require_tree ./lib/app
class App extends Spine.Controller
@view: (name) ->

View file

@ -0,0 +1,66 @@
$ = jQuery.sub()
class App.Com
_instance = undefined # Must be declared here to force the closure on the class
@ajax: (args) -> # Must be a static method
if _instance == undefined
_instance ?= new _Singleton
_instance.ajax(args)
_instance
# The actual Singleton class
class _Singleton
defaults:
contentType: 'application/json'
dataType: 'json'
processData: false
headers: {'X-Requested-With': 'XMLHttpRequest'}
cache: false
async: true
queue_list: {}
count: 0
constructor: (@args) ->
# bindings
$('body').bind( 'ajaxSend', =>
@_show_spinner()
).bind( 'ajaxComplete', =>
@_hide_spinner()
)
# show error messages
$('body').bind( 'ajaxError', ( e, jqxhr, settings, exception ) ->
status = jqxhr.status
detail = jqxhr.responseText
if !status && !detail
detail = 'General communication error, maybe internet is not available!'
new App.ErrorModal(
message: 'StatusCode: ' + status
detail: detail
close: true
)
)
ajax: (params, defaults) ->
data = $.extend({}, @defaults, defaults, params)
if params['id']
if @queue_list[ params['id'] ]
@queue_list[ params['id'] ].abort()
@queue_list[ params['id'] ] = $.ajax( data )
else
$.ajax( data )
# console.log('AJAX', params['url'] )
_show_spinner: =>
@count++
$('.spinner').show()
_hide_spinner: =>
@count--
if @count == 0
$('.spinner').hide()

View file

@ -0,0 +1,108 @@
$ = jQuery.sub()
class App.Auth
@login: (params) ->
console.log 'login(...)', params
App.Com.ajax(
id: 'login',
type: 'POST',
url: '/signin',
data: JSON.stringify(params.data),
success: (data, status, xhr) =>
# clear store
App.Store.clear('all')
# execute callback
params.success(data, status, xhr)
error: (xhr, statusText, error) =>
params.error(xhr, statusText, error)
)
@loginCheck: ->
console.log 'loginCheck(...)'
App.Com.ajax(
id: 'login_check',
async: false,
type: 'GET',
url: '/signshow',
success: (data, status, xhr) =>
console.log 'logincheck:success', data
# if session is not valid
if data.error
# update config
for key, value of data.config
window.Config[key] = value
# empty session
window.Session = {}
# update websocked auth info
App.WebSocket.auth()
# rebuild navbar with new navbar items
App.Event.trigger 'navrebuild'
return false;
# set avatar
if !data.session.image
data.session.image = 'http://placehold.it/48x48'
# update config
for key, value of data.config
window.Config[key] = value
# store user data
for key, value of data.session
window.Session[key] = value
# update websocked auth info
App.WebSocket.auth()
# refresh/load default collections
for key, value of data.default_collections
App.Collection.reset( type: key, data: value )
# rebuild navbar with new navbar items
App.Event.trigger 'navrebuild', data.session
# rebuild navbar with updated ticket count of overviews
App.Event.trigger 'navupdate_remote'
error: (xhr, statusText, error) =>
console.log 'loginCheck:error'#, error, statusText, xhr.statusCode
# empty session
window.Session = {}
# clear store
App.Store.clear('all')
# update websocked auth info
App.WebSocket.auth()
)
@logout: ->
console.log 'logout(...)'
App.Com.ajax(
id: 'logout',
type: 'DELETE',
url: '/signout',
success: =>
# update websocked auth info
App.WebSocket.auth()
# clear store
App.Store.clear('all')
error: (xhr, statusText, error) =>
# update websocked auth info
App.WebSocket.auth()
)

View file

@ -0,0 +1,181 @@
class App.ClipBoard
_instance = undefined
@bind: (el) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.bind(el)
@getSelected: ->
if _instance == undefined
_instance ?= new _Singleton
_instance.getSelected()
@getSelectedLast: ->
if _instance == undefined
_instance ?= new _Singleton
_instance.getSelectedLast()
@keycode: (code) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.keycode(code)
class _Singleton
constructor: ->
@selection = ''
@selectionLast = ''
# bind to fill selected text into
bind: (el) ->
$(el).bind('mouseup', =>
# check selection on mouse up
@selection = @_getSelected()
if @selection
@selectionLast = @selection
)
$(el).bind('keyup', (e) =>
# check selection on sonder key
if e.keyCode == 91
@selection = @_getSelected()
if @selection
@selectionLast = @selection
# check selection of arrow keys
if e.keyCode == 37 || e.keyCode == 38 || e.keyCode == 39 || e.keyCode == 40
@selection = @_getSelected()
if @selection
@selectionLast = @selection
)
# get cross browser selected string
_getSelected: ->
text = '';
if window.getSelection
text = window.getSelection()
else if document.getSelection
text = document.getSelection()
else if document.selection
text = document.selection.createRange().text
if text
text = text.toString().trim()
text
# get current selection
getSelected: ->
@selection
# get latest selection
getSelectedLast: ->
@selectionLast
keycode: (code) ->
for key, value of @keycodesTable()
if value.toString() is code.toString()
return key
keycodesTable: ->
  map = {
  'backspace' : 8,
  'tab' : 9,
  'enter' : 13,
  'shift' : 16,
  'ctrl' : 17,
  'alt' : 18,
  'space' : 32,
  'pause_break' : '19',
  'caps_lock' : '20',
  'escape' : '27',
  'page_up' : '33',
  'page down' : '34',
  'end' : '35',
  'home' : '36',
  'left_arrow' : '37',
  'up_arrow' : '38',
  'right_arrow' : '39',
  'down_arrow' : '40',
  'insert' : '45',
  'delete' : '46',
  '0' : '48',
  '1' : '49',
  '2' : '50',
  '3' : '51',
  '4' : '52',
  '5' : '53',
  '6' : '54',
  '7' : '55',
  '8' : '56',
  '9' : '57',
  'a' : '65',
  'b' : '66',
  'c' : '67',
  'd' : '68',
  'e' : '69',
  'f' : '70',
  'g' : '71',
  'h' : '72',
  'i' : '73',
  'j' : '74',
  'k' : '75',
  'l' : '76',
  'm' : '77',
  'n' : '78',
  'o' : '79',
  'p' : '80',
  'q' : '81',
  'r' : '82',
  's' : '83',
  't' : '84',
  'u' : '85',
  'v' : '86',
  'w' : '87',
  'x' : '88',
  'y' : '89',
  'z' : '90',
  'left_window key' : '91',
  'right_window key' : '92',
  'select_key' : '93',
  'numpad 0' : '96',
  'numpad 1' : '97',
  'numpad 2' : '98',
  'numpad 3' : '99',
  'numpad 4' : '100',
  'numpad 5' : '101',
  'numpad 6' : '102',
  'numpad 7' : '103',
  'numpad 8' : '104',
  'numpad 9' : '105',
  'multiply' : '106',
  'add' : '107',
  'subtract' : '109',
  'decimal point' : '110',
  'divide' : '111',
  'f1' : '112',
  'f2' : '113',
  'f3' : '114',
  'f4' : '115',
  'f5' : '116',
  'f6' : '117',
  'f7' : '118',
  'f8' : '119',
  'f9' : '120',
  'f10' : '121',
  'f11' : '122',
  'f12' : '123',
  'num_lock' : '144',
  'scroll_lock' : '145',
  'semi_colon' : '186',
  'equal_sign' : '187',
  'comma' : '188',
  'dash' : '189',
  'period' : '190',
  'forward_slash' : '191',
  'grave_accent' : '192',
  'open_bracket' : '219',
  'backslash' : '220',
  'closebracket' : '221',
  'single_quote' : '222'
}
map

View file

@ -0,0 +1,363 @@
class App.Collection
_instance = undefined
@init: ->
_instance = new _Singleton
@load: ( args ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.load( args )
@reset: ( args ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.reset( args )
@find: ( type, id, callback, force ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.find( type, id, callback, force )
@get: ( args ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.get( args )
@all: ( type ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.all( type )
@deleteAll: ( type ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.deleteAll( type )
@findByAttribute: ( type, key, value ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.findByAttribute( type, key, value )
@count: ( type ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.count( type )
@fetch: ( type ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.fetch( type )
@observe: (args) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.observe(args)
@observeUnbindLevel: (level) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.observeUnbindLevel(level)
@_observeStats: ->
if _instance == undefined
_instance ?= new _Singleton
_instance._observeStats()
class _Singleton
constructor: (@args) ->
# add trigger - bind new events
App.Event.bind 'loadCollection', (data) =>
# load collections
if data.collections
for type of data.collections
console.log 'loadCollection:trigger', type, data.collections[type]
@load( localStorage: data.localStorage, type: type, data: data.collections[type] )
# add trigger - bind new events
App.Event.bind 'resetCollection', (data) =>
# load collections
if data.collections
for type of data.collections
console.log 'resetCollection:trigger', type, data.collections[type]
@reset( localStorage: data.localStorage, type: type, data: data.collections[type] )
# find collections to load
@_loadCollectionAll()
_loadCollectionAll: ->
list = App.Store.list()
for key in list
parts = key.split('::')
if parts[0] is 'collection'
data = App.Store.get( key )
if data && data.localStorage
console.log('load INIT', data)
@load( data )
reset: (params) ->
console.log( 'reset', params )
# empty in-memory
App[ params.type ].refresh( [], { clear: true } )
# remove permanent storage
list = App.Store.list()
for key in list
parts = key.split('::')
if parts[0] is 'collection' && parts[1] is params.type
App.Store.delete(key)
# load with new data
@load(params)
load: (params) ->
console.log( 'load', params )
return if _.isEmpty( params.data )
localStorage = params.localStorage
# load full array once
if _.isArray( params.data )
# console.log( 'load ARRAY', params.data)
App[ params.type ].refresh( params.data )
# remember in store if not already requested from local storage
if !localStorage
for object in params.data
App.Store.write( 'collection::' + params.type + '::' + object.id, { type: params.type, localStorage: true, data: [ object ] } )
return
# load data from object
# if _.isObject( params.data )
for key, object of params.data
# console.log( 'load OB', object)
App[ params.type ].refresh( object )
# remember in store if not already requested from local storage
if !localStorage
App.Store.write( 'collection::' + params.type + '::' + object.id, { type: params.type, localStorage: true, data: [ object ] } )
find: ( type, id, callback, force ) ->
# console.log( 'find', type, id, force )
# if App[type].exists( id ) && !callback
if !force && App[type].exists( id )
# console.log( 'find exists', type, id )
data = App[type].find( id )
if callback
callback( data )
else
if force
console.log( 'find forced to load!', type, id )
else
console.log( 'find not loaded!', type, id )
if callback
# execute callback if record got loaded
App[type].one 'refresh', ->
console.log 'loaded..' + type + '..', id
data = App.Collection.find( type, id )
callback( data )
# fetch object
console.log 'loading..' + type + '..', id
App[type].fetch( id: id )
return true
return false
# users
if type == 'User'
# set socal media links
if data['accounts']
for account of data['accounts']
if account == 'twitter'
data['accounts'][account]['link'] = 'http://twitter.com/' + data['accounts'][account]['username']
if account == 'facebook'
data['accounts'][account]['link'] = 'https://www.facebook.com/profile.php?id=' + data['accounts'][account]['uid']
# set image url
if data && !data['image']
data['image'] = 'http://placehold.it/48x48'
return data
# tickets
else if type == 'Ticket'
# priority
data.ticket_priority = @find( 'TicketPriority', data.ticket_priority_id )
# state
data.ticket_state = @find( 'TicketState', data.ticket_state_id )
# group
data.group = @find( 'Group', data.group_id )
# customer
if data.customer_id
data.customer = @find( 'User', data.customer_id )
# owner
if data.owner_id
data.owner = @find( 'User', data.owner_id )
# add created & updated
if data.created_by_id
data.created_by = @find( 'User', data.created_by_id )
if data.updated_by_id
data.updated_by = @find( 'User', data.updated_by_id )
return data
# articles
else if type == 'TicketArticle'
# add created & updated
data.created_by = @find( 'User', data.created_by_id )
# add possible actions
data.article_type = @find( 'TicketArticleType', data.ticket_article_type_id )
data.article_sender = @find( 'TicketArticleSender', data.ticket_article_sender_id )
return data
# history
else if type == 'History'
# add user
data.created_by = @find( 'User', data.created_by_id )
# add possible actions
if data.history_attribute_id
data.attribute = @find( 'HistoryAttribute', data.history_attribute_id )
if data.history_type_id
data.type = @find( 'HistoryType', data.history_type_id )
if data.history_object_id
data.object = @find( 'HistoryObject', data.history_object_id )
return data
else
return data
get: (params) ->
console.log('get')
App[ params.type ].refresh( object, options: { clear: true } )
all: (params) ->
all = App[ params.type ].all()
all_complied = []
for item in all
item_new = @find( params.type, item.id )
all_complied.push item_new
if params.filter
all_complied = @_filter( all_complied, params.filter )
if params.filterExtended
all_complied = @_filterExtended( all_complied, params.filterExtended )
if params.sortBy
all_complied = @_sortBy( all_complied, params.sortBy )
if params.order
all_complied = @_order( all_complied, params.order )
return all_complied
deleteAll: (type) ->
App[type].deleteAll()
findByAttribute: ( type, key, value ) ->
App[type].findByAttribute( key, value )
count: ( type ) ->
App[type].count()
fetch: ( type ) ->
App[type].fetch()
_sortBy: ( collection, attribute ) ->
_.sortBy( collection, (item) ->
return '' if item[ attribute ] is undefined || item[ attribute ] is null
return item[ attribute ].toLowerCase()
)
_order: ( collection, attribute ) ->
if attribute is 'DESC'
return collection.reverse()
return collection
_filter: ( collection, filter ) ->
for key, value of filter
collection = _.filter( collection, (item) ->
if item[ key ] is value
return item
)
return collection
_filterExtended: ( collection, filters ) ->
collection = _.filter( collection, (item) ->
# check all filters
for filter in filters
# all conditions need match
matchInner = undefined
for key, value of filter
if matchInner isnt false
reg = new RegExp( value, 'i' )
if item[ key ] isnt undefined && item[ key ] isnt null && item[ key ].match( reg )
matchInner = true
else
matchInner = false
# if all matched, add item to new collection
if matchInner is true
return item
return
)
return collection
observeUnbindLevel: (level) ->
return if !@observeCurrent
return if !@observeCurrent[level]
for observers in @observeCurrent[level]
@_observeUnbind( observers )
@observeCurrent[level] = []
observe: (data) ->
if !@observeCurrent
@observeCurrent = {}
if !@observeCurrent[ data.level ]
@observeCurrent[ data.level ] = []
@observeCurrent[ data.level ].push data.collections
for observe in data.collections
events = observe.event.split(' ')
for event in events
if App[ observe.collection ]
App[ observe.collection ].bind( event, observe.callback )
_observeUnbind: (observers) ->
for observe in observers
events = observe.event.split(' ')
for event in events
if App[ observe.collection ]
App[ observe.collection ].unbind( event, observe.callback )
_observeStats: ->
@observeCurrent

View file

@ -0,0 +1,90 @@
class App.Event
_instance = undefined
@init: ->
_instance = new _Singleton
@bind: ( events, callback, level ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.bind( events, callback, level )
@unbind: ( events, callback, level ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.unbind( events, callback, level )
@trigger: ( events, data ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.trigger( events, data )
@unbindLevel: (level) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.unbindLevel(level)
@_allBindings: ->
if _instance == undefined
_instance ?= new _Singleton
_instance._allBindings()
class _Singleton
constructor: ->
@eventCurrent = {}
unbindLevel: (level) ->
return if !@eventCurrent[level]
for item in @eventCurrent[level]
@unbind( item.event, item.callback, level )
@eventCurrent[level] = []
bind: ( events, callback, level ) ->
if !level
level = '_all'
if !@eventCurrent[level]
@eventCurrent[level] = []
# level boundary events
eventList = events.split(' ')
for event in eventList
# remember all events
@eventCurrent[ level ].push {
event: event,
callback: callback,
}
# bind
Spine.bind( event, callback )
unbind: ( events, callback, level ) ->
if !level
level = '_all'
if !@eventCurrent[level]
@eventCurrent[level] = []
eventList = events.split(' ')
for event in eventList
# remove from
@eventCurrent[level] = _.filter( @eventCurrent[level], (item) ->
if callback
return item if item.event isnt event && item.callback isnt callback
else
return item if item.event isnt event
)
Spine.unbind( event, callback )
trigger: ( events, data ) ->
eventList = events.split(' ')
for event in eventList
Spine.trigger event, data
_allBindings: ->
@eventCurrent

View file

@ -0,0 +1,169 @@
$ = jQuery.sub()
class App.i18n
_instance = undefined
@init: ->
_instance ?= new _Singleton
@translateContent: ( string, args... ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.translate_content( string, args )
@translateInline: ( string, args... ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.translate_inline( string, args )
@translateTimestamp: ( args ) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.timestamp( args )
class _Singleton
constructor: ->
@locale = 'de'
@timestampFormat = 'yyyy-mm-dd HH:MM'
@set( @locale )
# $('.translation [contenteditable]')
$('body')
.delegate '.translation', 'focus', (e) =>
$this = $(e.target)
$this.data 'before', $this.html()
# console.log('11111current', $this.html())
return $this
# .delegate '.translation', 'blur keyup paste', (e) =>
.delegate '.translation', 'blur', (e) =>
$this = $(e.target)
source = $this.attr('data-text')
# get new translation
translation_new = $this.html()
translation_new = ('' + translation_new)
.replace(/<.+?>/g, '')
# set new translation
$this.html(translation_new)
# update translation
return if $this.data('before') is translation_new
console.log 'Translation Update', translation_new, $this.data 'before'
$this.data 'before', translation_new
# update runtime translation map
@map[ source ] = translation_new
# replace rest in page
$(".translation[data-text='#{source}']").html( translation_new )
# update permanent translation map
translation = App.Collection.findByAttribute( 'Translation', 'source', source )
if translation
translation.updateAttribute( 'target', translation_new )
else
translation = new App.Translation
translation.load(
locale: @locale,
source: source,
target: translation_new,
)
translation.save()
return $this
set: ( locale ) ->
@map = {}
App.Com.ajax(
id: 'i18n-set-' + locale,
type: 'GET',
url: '/translations/lang/' + locale,
async: false,
success: (data, status, xhr) =>
# set timestamp format
if data.timestampFormat
@timestampFormat = data.timestampFormat
# load translation collection
for object in data.list
# set runtime lookup table
@map[ object[1] ] = object[2]
# load in collection if needed
App.Translation.refresh( { id: object[0], source: object[1], target: object[2], locale: @locale } )
error: (xhr, statusText, error) =>
console.log 'error', error, statusText, xhr.statusCode
)
translate_inline: ( string, args... ) =>
@translate( string, args... )
translate_content: ( string, args... ) =>
translated = @translate( string, args... )
# replace = '<span class="translation" contenteditable="true" data-text="' + @escape(string) + '">' + translated + '<span class="icon-edit"></span>'
if window.Config['Translation']
replace = '<span class="translation" contenteditable="true" data-text="' + @escape(string) + '">' + translated + ''
# if !@_translated
# replace += '<span class="missing">XX</span>'
replace += '</span>'
else
translated
translate: ( string, args... ) =>
# return '' on undefined
return '' if string is undefined
# return translation
if @map[string] isnt undefined
@_translated = true
translated = @map[string]
else
@_translated = false
translated = string
# search %s
for arg in args
translated = translated.replace(/%s/, arg)
# escape
translated = @escape(translated)
# return translated string
return translated
escape: ( string ) ->
string = ( '' + string )
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\x22/g, '&quot;')
timestamp: ( time ) =>
s = ( num, digits ) ->
while num.toString().length < digits
num = "0" + num
return num
timeObject = new Date(time)
d = timeObject.getDate()
m = timeObject.getMonth() + 1
y = timeObject.getFullYear()
S = timeObject.getSeconds()
M = timeObject.getMinutes()
H = timeObject.getHours()
format = @timestampFormat
format = format.replace /dd/, s( d, 2 )
format = format.replace /d/, d
format = format.replace /mm/, s( m, 2 )
format = format.replace /m/, m
format = format.replace /yyyy/, y
format = format.replace /SS/, s( S, 2 )
format = format.replace /MM/, s( M, 2 )
format = format.replace /HH/, s( H, 2 )
return format

View file

@ -0,0 +1,76 @@
class App.Run extends App.Controller
constructor: ->
super
@log 'RUN app'
@el = $('#app')
# init collections
App.Collection.init()
# create web socket connection
App.WebSocket.connect()
# init of i18n
App.i18n.init()
# start navigation controller
new App.Navigation( el: @el.find('#navigation') )
# check if session already exists/try to get session data from server
App.Auth.loginCheck()
# start notify controller
new App.Notify( el: @el.find('#notify') )
# start content
new App.Content( el: @el.find('#content') )
# bind to fill selected text into
App.ClipBoard.bind( @el )
class App.Content extends Spine.Controller
className: 'container'
constructor: ->
super
@log 'RUN content'
for route, callback of Config.Routes
do (route, callback) =>
@route(route, (params) ->
# remove observers for page
App.Collection.observeUnbindLevel('page')
# remove events for page
App.Event.unbindLevel('page')
# unbind in controller area
@el.unbind()
@el.undelegate()
# send current controller
params_only = {}
for i of params
if typeof params[i] isnt 'object'
params_only[i] = params[i]
# tell server what we are calling right now
App.WebSocket.send(
action: 'active_controller',
controller: route,
params: params_only,
)
# remove waypoints
$('footer').waypoint('remove')
params.el = @el
new callback( params )
# scroll to top
# window.scrollTo(0,0)
)
Spine.Route.setup()

View file

@ -0,0 +1,61 @@
class App.Store
_instance = undefined # Must be declared here to force the closure on the class
@renew: ->
_instance = new _Singleton
@write: (key, value) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.write(key, value)
@get: (args) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.get(args)
@delete: (args) ->
if _instance == undefined
_instance ?= new _Singleton
_instance.delete(args)
@clear: ->
if _instance == undefined
_instance ?= new _Singleton
_instance.clear()
@list: () ->
if _instance == undefined
_instance ?= new _Singleton
_instance.list()
# The actual Singleton class
class _Singleton
# write to local storage
write: (key, value) ->
localStorage.setItem( key, JSON.stringify( value ) )
# get item
get: (key) ->
value = localStorage.getItem( key )
return if !value
object = JSON.parse( value )
return object
# delete item
delete: (key) ->
localStorage.removeItem( key )
# clear local storage
clear: ->
localStorage.clear()
# return list of all keys
list: ->
list = []
logLength = localStorage.length-1;
for count in [0..logLength]
key = localStorage.key( count )
if key
list.push key
list

View file

@ -0,0 +1,163 @@
$ = jQuery.sub()
class App.WebSocket
_instance = undefined # Must be declared here to force the closure on the class
@connect: (args) -> # Must be a static method
if _instance == undefined
_instance ?= new _Singleton
_instance
@close: (args) -> # Must be a static method
if _instance isnt undefined
_instance.close()
@send: (args) -> # Must be a static method
@connect()
_instance.send(args)
@auth: (args) -> # Must be a static method
@connect()
_instance.auth(args)
# The actual Singleton class
class _Singleton extends App.Controller
queue: []
supported: true
constructor: (@args) ->
@connect()
send: (data) =>
return if !@supported
# console.log 'ws:send trying', data, @ws, @ws.readyState
# A value of 0 indicates that the connection has not yet been established.
# A value of 1 indicates that the connection is established and communication is possible.
# A value of 2 indicates that the connection is going through the closing handshake.
# A value of 3 indicates that the connection has been closed or could not be opened.
if @ws.readyState is 0
@queue.push data
else
# console.log( 'ws:send', data )
string = JSON.stringify( data )
@ws.send(string)
auth: (data) =>
return if !@supported
# logon websocket
data = {
action: 'login',
session: window.Session
}
@send(data)
close: =>
return if !@supported
@ws.close()
ping: =>
return if !@supported
# console.log 'send websockend ping'
@send( { action: 'ping' } )
# check if ping is back within 2 min
@clearDelay('websocket-ping-check')
check = =>
console.log 'no websockend ping response, reconnect...'
@close()
@delay check, 120000, 'websocket-ping-check'
pong: ->
return if !@supported
# console.log 'received websockend ping'
# test again after 1 min
@delay @ping, 60000
connect: =>
# console.log '------------ws connect....--------------'
if !window.WebSocket
@error = new App.ErrorModal(
message: 'Sorry, no websocket support!'
)
@supported = false
return
protocol = 'ws://'
if window.location.protocol is 'https:'
protocol = 'wss://'
@ws = new window.WebSocket( protocol + window.location.hostname + ":6042/" )
# Set event handlers.
@ws.onopen = =>
console.log( 'onopen' )
# close error message show up (because try so connect again) if exists
@clearDelay('websocket-no-connection-try-reconnect')
if @error
@error.modalHide()
@error = undefined
@auth()
# empty queue
for item in @queue
# console.log( 'ws:send queue', item )
@send(item)
@queue = []
# send ping to check connection
@delay @ping, 60000
@ws.onmessage = (e) =>
pipe = JSON.parse( e.data )
console.log( 'ws:onmessage', pipe )
# go through all blocks
for item in pipe
# reset reconnect loop
if item['action'] is 'pong'
@pong()
# fill collection
if item['collection']
console.log( "ws:onmessage collection:" + item['collection'] )
App.Store.write( item['collection'], item['data'] )
# fire event
if item['event']
if typeof item['event'] is 'object'
for event in item['event']
console.log( "ws:onmessage event:" + event )
App.Event.trigger( event, item['data'] )
else
console.log( "ws:onmessage event:" + item['event'] )
App.Event.trigger( item['event'], item['data'] )
# bind to send messages
App.Event.bind 'ws:send', (data) =>
@send(data)
@ws.onclose = (e) =>
console.log( 'onclose', e )
# show error message, first try to reconnect
if !@error
message = =>
@error = new App.ErrorModal(
message: 'No connection to websocket, trying to reconnect...'
)
@delay message, 7000, 'websocket-no-connection-try-reconnect'
# try reconnect after 4.5 sec.
@delay @connect, 4500
@ws.onerror = ->
console.log( 'onerror' )

View file

@ -0,0 +1,179 @@
/*!
* linkify - v0.3 - 6/27/2009
* http://benalman.com/code/test/js-linkify/
*
* Copyright (c) 2009 "Cowboy" Ben Alman
* Licensed under the MIT license
* http://benalman.com/about/license/
*
* Some regexps adapted from http://userscripts.org/scripts/review/7122
*/
// Turn text into linkified html.
//
// var html = linkify( text, options );
//
// options:
//
// callback (Function) - default: undefined - if defined, this will be called
// for each link- or non-link-chunk with two arguments, text and href. If the
// chunk is non-link, href will be omitted.
//
// punct_regexp (RegExp | Boolean) - a RegExp that can be used to trim trailing
// punctuation from links, instead of the default.
//
// This is a work in progress, please let me know if (and how) it fails!
window.linkify = (function(){
var
SCHEME = "[a-z\\d.-]+://",
IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
PATH = "(?:[;/][^#?<>\\s]*)?",
QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
URI1 = "\\b" + SCHEME + "[^<>\\s]+",
URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
MAILTO = "mailto:",
EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
quotes = {
"'": "`",
'>': '<',
')': '(',
']': '[',
'}': '{',
'»': '«',
'': ''
},
default_options = {
callback: function( text, href ) {
// return href ? '<a href="' + href + '" title="' + href + '">' + text + '<\/a>' : text;
return href ? '<a href="' + href + '" title="' + href + '" target="_blank">' + text + '<\/a>' : text;
},
punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
};
return function( txt, options ) {
options = options || {};
// me
txt = txt
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
// me
// Temp variables.
var arr,
i,
link,
href,
// Output HTML.
html = '',
// Store text / link parts, in order, for re-combination.
parts = [],
// Used for keeping track of indices in the text.
idx_prev,
idx_last,
idx,
link_last,
// Used for trimming trailing punctuation and quotes from links.
matches_begin,
matches_end,
quote_begin,
quote_end;
// Initialize options.
for ( i in default_options ) {
if ( options[ i ] === undefined ) {
options[ i ] = default_options[ i ];
}
}
// Find links.
while ( arr = URI_RE.exec( txt ) ) {
link = arr[0];
idx_last = URI_RE.lastIndex;
idx = idx_last - link.length;
// Not a link if preceded by certain characters.
if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
continue;
}
// Trim trailing punctuation.
do {
// If no changes are made, we don't want to loop forever!
link_last = link;
quote_end = link.substr( -1 )
quote_begin = quotes[ quote_end ];
// Ending quote character?
if ( quote_begin ) {
matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
// If quotes are unbalanced, remove trailing quote character.
if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
link = link.substr( 0, link.length - 1 );
idx_last--;
}
}
// Ending non-quote punctuation character?
if ( options.punct_regexp ) {
link = link.replace( options.punct_regexp, function(a){
idx_last -= a.length;
return '';
});
}
} while ( link.length && link !== link_last );
href = link;
// Add appropriate protocol to naked links.
if ( !SCHEME_RE.test( href ) ) {
href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
: !href.indexOf( 'irc.' ) ? 'irc://'
: !href.indexOf( 'ftp.' ) ? 'ftp://'
: 'http://' )
+ href;
}
// Push preceding non-link text onto the array.
if ( idx_prev != idx ) {
parts.push([ txt.slice( idx_prev, idx ) ]);
idx_prev = idx_last;
}
// Push massaged link onto the array
parts.push([ link, href ]);
};
// Push remaining non-link text onto the array.
parts.push([ txt.substr( idx_prev ) ]);
// Process the array items.
for ( i = 0; i < parts.length; i++ ) {
html += options.callback.apply( window, parts[i] );
}
// In case of catastrophic failure, return the original text;
return html || txt;
};
})();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,162 @@
/**
* @name Elastic
* @descripton Elastic is jQuery plugin that grow and shrink your textareas automatically
* @version 1.6.11
* @requires jQuery 1.2.6+
*
* @author Jan Jarfalk
* @author-email jan.jarfalk@unwrongest.com
* @author-website http://www.unwrongest.com
*
* @licence MIT License - http://www.opensource.org/licenses/mit-license.php
*/
(function($){
jQuery.fn.extend({
elastic: function() {
// We will create a div clone of the textarea
// by copying these attributes from the textarea to the div.
var mimics = [
'paddingTop',
'paddingRight',
'paddingBottom',
'paddingLeft',
'fontSize',
'lineHeight',
'fontFamily',
'width',
'fontWeight',
'border-top-width',
'border-right-width',
'border-bottom-width',
'border-left-width',
'borderTopStyle',
'borderTopColor',
'borderRightStyle',
'borderRightColor',
'borderBottomStyle',
'borderBottomColor',
'borderLeftStyle',
'borderLeftColor'
];
return this.each( function() {
// Elastic only works on textareas
if ( this.type !== 'textarea' ) {
return false;
}
var $textarea = jQuery(this),
$twin = jQuery('<div />').css({
'position' : 'absolute',
'display' : 'none',
'word-wrap' : 'break-word',
'white-space' :'pre-wrap'
}),
lineHeight = parseInt($textarea.css('line-height'),10) || parseInt($textarea.css('font-size'),'10'),
minheight = parseInt($textarea.css('height'),10) || lineHeight*3,
maxheight = parseInt($textarea.css('max-height'),10) || Number.MAX_VALUE,
goalheight = 0;
// Opera returns max-height of -1 if not set
if (maxheight < 0) { maxheight = Number.MAX_VALUE; }
// Append the twin to the DOM
// We are going to meassure the height of this, not the textarea.
$twin.appendTo($textarea.parent());
// Copy the essential styles (mimics) from the textarea to the twin
var i = mimics.length;
while(i--){
$twin.css(mimics[i].toString(),$textarea.css(mimics[i].toString()));
}
// Updates the width of the twin. (solution for textareas with widths in percent)
function setTwinWidth(){
var curatedWidth = Math.floor(parseInt($textarea.width(),10));
if($twin.width() !== curatedWidth){
$twin.css({'width': curatedWidth + 'px'});
// Update height of textarea
update(true);
}
}
// Sets a given height and overflow state on the textarea
function setHeightAndOverflow(height, overflow){
var curratedHeight = Math.floor(parseInt(height,10));
if($textarea.height() !== curratedHeight){
$textarea.css({'height': curratedHeight + 'px','overflow':overflow});
}
}
// This function will update the height of the textarea if necessary
function update(forced) {
// Get curated content from the textarea.
var textareaContent = $textarea.val().replace(/&/g,'&amp;').replace(/ {2}/g, '&nbsp;').replace(/<|>/g, '&gt;').replace(/\n/g, '<br />');
// Compare curated content with curated twin.
var twinContent = $twin.html().replace(/<br>/ig,'<br />');
if(forced || textareaContent+'&nbsp;' !== twinContent){
// Add an extra white space so new rows are added when you are at the end of a row.
$twin.html(textareaContent+'&nbsp;');
// Change textarea height if twin plus the height of one line differs more than 3 pixel from textarea height
if(Math.abs($twin.height() + lineHeight - $textarea.height()) > 3){
var goalheight = $twin.height()+lineHeight;
if(goalheight >= maxheight) {
setHeightAndOverflow(maxheight,'auto');
} else if(goalheight <= minheight) {
setHeightAndOverflow(minheight,'hidden');
} else {
setHeightAndOverflow(goalheight,'hidden');
}
}
}
}
// Hide scrollbars
$textarea.css({'overflow':'hidden'});
// Update textarea size on keyup, change, cut and paste
$textarea.bind('keyup change cut paste', function(){
update();
});
// Update width of twin if browser or textarea is resized (solution for textareas with widths in percent)
$(window).bind('resize', setTwinWidth);
$textarea.bind('resize', setTwinWidth);
$textarea.bind('update', update);
// Compact textarea on blur
$textarea.bind('blur',function(){
if($twin.height() < maxheight){
if($twin.height() > minheight) {
$textarea.height($twin.height());
} else {
$textarea.height(minheight);
}
}
});
// And this line is to catch the browser paste event
$textarea.bind('input paste',function(e){ setTimeout( update, 250); });
// Run update once when elastic is initialized
update();
});
}
});
})(jQuery);

View file

@ -0,0 +1,220 @@
/**
* jQuery Noty Plugin v1.1.1
* Authors: Nedim Arabacı (http://ned.im), Muhittin Özer (http://muhittinozer.com)
*
* Examples and Documentation - http://needim.github.com/noty/
*
* Licensed under the MIT licenses:
* http://www.opensource.org/licenses/mit-license.php
*
**/
(function($) {
$.noty = function(options, customContainer) {
var base = this;
var $noty = null;
var isCustom = false;
base.init = function(options) {
base.options = $.extend({}, $.noty.defaultOptions, options);
base.options.type = base.options.cssPrefix+base.options.type;
base.options.id = base.options.type+'_'+new Date().getTime();
base.options.layout = base.options.cssPrefix+'layout_'+base.options.layout;
if (base.options.custom.container) customContainer = base.options.custom.container;
isCustom = ($.type(customContainer) === 'object') ? true : false;
return base.addQueue();
};
// Push notification to queue
base.addQueue = function() {
var isGrowl = ($.inArray(base.options.layout, $.noty.growls) == -1) ? false : true;
if (!isGrowl) (base.options.force) ? $.noty.queue.unshift({options: base.options}) : $.noty.queue.push({options: base.options});
return base.render(isGrowl);
};
// Render the noty
base.render = function(isGrowl) {
// Layout spesific container settings
var container = (isCustom) ? customContainer.addClass(base.options.theme+' '+base.options.layout+' noty_custom_container') : $('body');
if (isGrowl) {
if ($('ul.noty_cont.' + base.options.layout).length == 0)
container.prepend($('<ul/>').addClass('noty_cont ' + base.options.layout));
container = $('ul.noty_cont.' + base.options.layout);
} else {
if ($.noty.available) {
var fromQueue = $.noty.queue.shift(); // Get noty from queue
if ($.type(fromQueue) === 'object') {
$.noty.available = false;
base.options = fromQueue.options;
} else {
$.noty.available = true; // Queue is over
return base.options.id;
}
} else {
return base.options.id;
}
}
base.container = container;
// Generating noty bar
base.bar = $('<div class="noty_bar"/>').attr('id', base.options.id).addClass(base.options.theme+' '+base.options.layout+' '+base.options.type);
$noty = base.bar;
$noty.append(base.options.template).find('.noty_text').html(base.options.text);
$noty.data('noty_options', base.options);
// Close button display
(base.options.closeButton) ? $noty.addClass('noty_closable').find('.noty_close').show() : $noty.find('.noty_close').remove();
// Bind close event to button
$noty.find('.noty_close').bind('click', function() { $noty.trigger('noty.close'); });
// If we have a button we must disable closeOnSelfClick and closeOnSelfOver option
if (base.options.buttons) base.options.closeOnSelfClick = base.options.closeOnSelfOver = false;
// Close on self click
if (base.options.closeOnSelfClick) $noty.bind('click', function() { $noty.trigger('noty.close'); }).css('cursor', 'pointer');
// Close on self mouseover
if (base.options.closeOnSelfOver) $noty.bind('mouseover', function() { $noty.trigger('noty.close'); }).css('cursor', 'pointer');
// Set buttons if available
if (base.options.buttons) {
$buttons = $('<div/>').addClass('noty_buttons');
$noty.find('.noty_message').append($buttons);
$.each(base.options.buttons, function(i, button) {
bclass = (button.type) ? button.type : 'gray';
$button = $('<button/>').addClass(bclass).html(button.text).appendTo($noty.find('.noty_buttons'))
.bind('click', function() {
if ($.isFunction(button.click)) {
button.click.call($button, $noty);
}
});
});
}
return base.show(isGrowl);
};
base.show = function(isGrowl) {
// is Modal?
if (base.options.modal) $('<div/>').addClass('noty_modal').addClass(base.options.theme).prependTo($('body')).fadeIn('fast');
$noty.close = function() { return this.trigger('noty.close'); };
// Prepend noty to container
(isGrowl) ? base.container.prepend($('<li/>').append($noty)) : base.container.prepend($noty);
// topCenter and center specific options
if (base.options.layout == 'noty_layout_topCenter' || base.options.layout == 'noty_layout_center') {
$.noty.reCenter($noty);
}
$noty.bind('noty.setText', function(event, text) {
$noty.find('.noty_text').html(text); $.noty.reCenter($noty);
});
$noty.bind('noty.getId', function(event) {
return $noty.data('noty_options').id;
});
// Bind close event
$noty.one('noty.close', function(event) {
var options = $noty.data('noty_options');
// Modal Cleaning
if (options.modal) $('.noty_modal').fadeOut('fast', function() { $(this).remove(); });
$noty.clearQueue().stop().animate(
$noty.data('noty_options').animateClose,
$noty.data('noty_options').speed,
$noty.data('noty_options').easing,
$noty.data('noty_options').onClose)
.promise().done(function() {
// Layout spesific cleaning
if ($.inArray($noty.data('noty_options').layout, $.noty.growls) > -1) {
$noty.parent().remove();
} else {
$noty.remove();
// queue render
$.noty.available = true;
base.render(false);
}
});
});
// Start the show
$noty.animate(base.options.animateOpen, base.options.speed, base.options.easing, base.options.onShow);
// If noty is have a timeout option
if (base.options.timeout) $noty.delay(base.options.timeout).promise().done(function() { $noty.trigger('noty.close'); });
return base.options.id;
};
// Run initializer
return base.init(options);
};
// API
$.noty.get = function(id) { return $('#'+id); };
$.noty.close = function(id) {
$.noty.get(id).trigger('noty.close');
};
$.noty.setText = function(id, text) {
$.noty.get(id).trigger('noty.setText', text);
};
$.noty.closeAll = function() {
$.noty.clearQueue();
$('.noty_bar').trigger('noty.close');
};
$.noty.reCenter = function(noty) {
noty.css({'left': ($(window).width() - noty.outerWidth()) / 2 + 'px'});
};
$.noty.clearQueue = function() {
$.noty.queue = [];
};
$.noty.queue = [];
$.noty.growls = ['noty_layout_topLeft', 'noty_layout_topRight', 'noty_layout_bottomLeft', 'noty_layout_bottomRight'];
$.noty.available = true;
$.noty.defaultOptions = {
layout: 'top',
theme: 'noty_theme_default',
animateOpen: {height: 'toggle'},
animateClose: {height: 'toggle'},
easing: 'swing',
text: '',
type: 'alert',
speed: 500,
timeout: 5000,
closeButton: false,
closeOnSelfClick: true,
closeOnSelfOver: false,
force: false,
onShow: false,
onClose: false,
buttons: false,
modal: false,
template: '<div class="noty_message"><span class="noty_text"></span><div class="noty_close"></div></div>',
cssPrefix: 'noty_',
custom: {
container: null
}
};
$.fn.noty = function(options) {
return this.each(function() {
(new $.noty(options, $(this)));
});
};
})(jQuery);
//Helper
function noty(options) {
return jQuery.noty(options); // returns an id
}

View file

@ -0,0 +1,353 @@
/*
jQuery Tags Input Plugin 1.3.3
Copyright (c) 2011 XOXCO, Inc
Documentation for this plugin lives here:
http://xoxco.com/clickable/jquery-tags-input
Licensed under the MIT license:
http://www.opensource.org/licenses/mit-license.php
ben@xoxco.com
*/
(function($) {
var delimiter = new Array();
var tags_callbacks = new Array();
$.fn.doAutosize = function(o){
var minWidth = $(this).data('minwidth'),
maxWidth = $(this).data('maxwidth'),
val = '',
input = $(this),
testSubject = $('#'+$(this).data('tester_id'));
if (val === (val = input.val())) {return;}
// Enter new content into testSubject
var escaped = val.replace(/&/g, '&amp;').replace(/\s/g,' ').replace(/</g, '&lt;').replace(/>/g, '&gt;');
testSubject.html(escaped);
// Calculate new width + whether to change
var testerWidth = testSubject.width(),
newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth,
currentWidth = input.width(),
isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth)
|| (newWidth > minWidth && newWidth < maxWidth);
// Animate width
if (isValidWidthChange) {
input.width(newWidth);
}
};
$.fn.resetAutosize = function(options){
// alert(JSON.stringify(options));
var minWidth = $(this).data('minwidth') || options.minInputWidth || $(this).width(),
maxWidth = $(this).data('maxwidth') || options.maxInputWidth || ($(this).closest('.tagsinput').width() - options.inputPadding),
val = '',
input = $(this),
testSubject = $('<tester/>').css({
position: 'absolute',
top: -9999,
left: -9999,
width: 'auto',
fontSize: input.css('fontSize'),
fontFamily: input.css('fontFamily'),
fontWeight: input.css('fontWeight'),
letterSpacing: input.css('letterSpacing'),
whiteSpace: 'nowrap'
}),
testerId = $(this).attr('id')+'_autosize_tester';
if(! $('#'+testerId).length > 0){
testSubject.attr('id', testerId);
testSubject.appendTo('body');
}
input.data('minwidth', minWidth);
input.data('maxwidth', maxWidth);
input.data('tester_id', testerId);
input.css('width', minWidth);
};
$.fn.addTag = function(value,options) {
options = jQuery.extend({focus:false,callback:true},options);
this.each(function() {
var id = $(this).attr('id');
var tagslist = $(this).val().split(delimiter[id]);
if (tagslist[0] == '') {
tagslist = new Array();
}
value = jQuery.trim(value);
if (options.unique) {
var skipTag = $(tagslist).tagExist(value);
if(skipTag == true) {
//Marks fake input as not_valid to let styling it
$('#'+id+'_tag').addClass('not_valid');
}
} else {
var skipTag = false;
}
if (value !='' && skipTag != true) {
$('<span>').addClass('tag').append(
$('<span>').text(value).append('&nbsp;&nbsp;'),
$('<a>', {
href : '#',
title : 'Removing tag',
text : 'x'
}).click(function () {
return $('#' + id).removeTag(escape(value));
})
).insertBefore('#' + id + '_addTag');
tagslist.push(value);
$('#'+id+'_tag').val('');
if (options.focus) {
$('#'+id+'_tag').focus();
} else {
$('#'+id+'_tag').blur();
}
$.fn.tagsInput.updateTagsField(this,tagslist);
if (options.callback && tags_callbacks[id] && tags_callbacks[id]['onAddTag']) {
var f = tags_callbacks[id]['onAddTag'];
f.call(this, value);
}
if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
{
var i = tagslist.length;
var f = tags_callbacks[id]['onChange'];
f.call(this, $(this), tagslist[i-1]);
}
}
});
return false;
};
$.fn.removeTag = function(value) {
value = unescape(value);
this.each(function() {
var id = $(this).attr('id');
var old = $(this).val().split(delimiter[id]);
$('#'+id+'_tagsinput .tag').remove();
str = '';
for (i=0; i< old.length; i++) {
if (old[i]!=value) {
str = str + delimiter[id] +old[i];
}
}
$.fn.tagsInput.importTags(this,str);
if (tags_callbacks[id] && tags_callbacks[id]['onRemoveTag']) {
var f = tags_callbacks[id]['onRemoveTag'];
f.call(this, value);
}
});
return false;
};
$.fn.tagExist = function(val) {
return (jQuery.inArray(val, $(this)) >= 0); //true when tag exists, false when not
};
// clear all existing tags and import new ones from a string
$.fn.importTags = function(str) {
id = $(this).attr('id');
$('#'+id+'_tagsinput .tag').remove();
$.fn.tagsInput.importTags(this,str);
}
$.fn.tagsInput = function(options) {
var settings = jQuery.extend({
interactive:true,
defaultText:'add a tag',
minChars:0,
width:'300px',
height:'100px',
autocomplete: {selectFirst: false },
'hide':true,
'delimiter':',',
'unique':true,
removeWithBackspace:true,
placeholderColor:'#666666',
autosize: true,
comfortZone: 20,
inputPadding: 6*2
},options);
this.each(function() {
if (settings.hide) {
$(this).hide();
}
var id = $(this).attr('id')
var data = jQuery.extend({
pid:id,
real_input: '#'+id,
holder: '#'+id+'_tagsinput',
input_wrapper: '#'+id+'_addTag',
fake_input: '#'+id+'_tag'
},settings);
delimiter[id] = data.delimiter;
if (settings.onAddTag || settings.onRemoveTag || settings.onChange) {
tags_callbacks[id] = new Array();
tags_callbacks[id]['onAddTag'] = settings.onAddTag;
tags_callbacks[id]['onRemoveTag'] = settings.onRemoveTag;
tags_callbacks[id]['onChange'] = settings.onChange;
}
var markup = '<div id="'+id+'_tagsinput" class="tagsinput"><div id="'+id+'_addTag">';
if (settings.interactive) {
markup = markup + '<input id="'+id+'_tag" value="" data-default="'+settings.defaultText+'" />';
}
markup = markup + '</div><div class="tags_clear"></div></div>';
$(markup).insertAfter(this);
$(data.holder).css('width',settings.width);
$(data.holder).css('height',settings.height);
if ($(data.real_input).val()!='') {
$.fn.tagsInput.importTags($(data.real_input),$(data.real_input).val());
}
if (settings.interactive) {
$(data.fake_input).val($(data.fake_input).attr('data-default'));
$(data.fake_input).css('color',settings.placeholderColor);
$(data.fake_input).resetAutosize(settings);
$(data.holder).bind('click',data,function(event) {
$(event.data.fake_input).focus();
});
$(data.fake_input).bind('focus',data,function(event) {
if ($(event.data.fake_input).val()==$(event.data.fake_input).attr('data-default')) {
$(event.data.fake_input).val('');
}
$(event.data.fake_input).css('color','#000000');
});
if (settings.autocomplete_url != undefined) {
// 2012-02-23 me
// autocomplete_options = {source: settings.autocomplete_url};
autocomplete_options = settings.auto;
// 2012-02-23 me
for (attrname in settings.autocomplete) {
autocomplete_options[attrname] = settings.autocomplete[attrname];
}
if (jQuery.Autocompleter !== undefined) {
$(data.fake_input).autocomplete(settings.autocomplete_url, settings.autocomplete);
$(data.fake_input).bind('result',data,function(event,data,formatted) {
if (data) {
$('#'+id).addTag(data[0] + "",{focus:true,unique:(settings.unique)});
}
});
} else if (jQuery.ui.autocomplete !== undefined) {
$(data.fake_input).autocomplete(autocomplete_options);
$(data.fake_input).bind('autocompleteselect',data,function(event,ui) {
$(event.data.real_input).addTag(ui.item.value,{focus:true,unique:(settings.unique)});
return false;
});
}
} else {
// if a user tabs out of the field, create a new tag
// this is only available if autocomplete is not used.
$(data.fake_input).bind('blur',data,function(event) {
var d = $(this).attr('data-default');
if ($(event.data.fake_input).val()!='' && $(event.data.fake_input).val()!=d) {
if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
} else {
$(event.data.fake_input).val($(event.data.fake_input).attr('data-default'));
$(event.data.fake_input).css('color',settings.placeholderColor);
}
return false;
});
}
// if user types a comma, create a new tag
$(data.fake_input).bind('keypress',data,function(event) {
if (event.which==event.data.delimiter.charCodeAt(0) || event.which==13 ) {
event.preventDefault();
if( (event.data.minChars <= $(event.data.fake_input).val().length) && (!event.data.maxChars || (event.data.maxChars >= $(event.data.fake_input).val().length)) )
$(event.data.real_input).addTag($(event.data.fake_input).val(),{focus:true,unique:(settings.unique)});
$(event.data.fake_input).resetAutosize(settings);
return false;
} else if (event.data.autosize) {
$(event.data.fake_input).doAutosize(settings);
}
});
//Delete last tag on backspace
data.removeWithBackspace && $(data.fake_input).bind('keydown', function(event)
{
if(event.keyCode == 8 && $(this).val() == '')
{
event.preventDefault();
var last_tag = $(this).closest('.tagsinput').find('.tag:last').text();
var id = $(this).attr('id').replace(/_tag$/, '');
last_tag = last_tag.replace(/[\s]+x$/, '');
$('#' + id).removeTag(escape(last_tag));
$(this).trigger('focus');
}
});
$(data.fake_input).blur();
//Removes the not_valid class when user changes the value of the fake input
if(data.unique) {
$(data.fake_input).keydown(function(event){
if(event.keyCode == 8 || String.fromCharCode(event.which).match(/\w+|[áéíóúÁÉÍÓÚñÑ,/]+/)) {
$(this).removeClass('not_valid');
}
});
}
} // if settings.interactive
return false;
});
return this;
};
$.fn.tagsInput.updateTagsField = function(obj,tagslist) {
var id = $(obj).attr('id');
$(obj).val(tagslist.join(delimiter[id]));
};
$.fn.tagsInput.importTags = function(obj,val) {
$(obj).val('');
var id = $(obj).attr('id');
var tags = val.split(delimiter[id]);
for (i=0; i<tags.length; i++) {
$(obj).addTag(tags[i],{focus:false,callback:false});
}
if(tags_callbacks[id] && tags_callbacks[id]['onChange'])
{
var f = tags_callbacks[id]['onChange'];
f.call(obj, obj, tags[i]);
}
};
})(jQuery);

View file

@ -0,0 +1,674 @@
/*!
jQuery Waypoints - v1.1.6
Copyright (c) 2011-2012 Caleb Troughton
Dual licensed under the MIT license and GPL license.
https://github.com/imakewebthings/jquery-waypoints/blob/master/MIT-license.txt
https://github.com/imakewebthings/jquery-waypoints/blob/master/GPL-license.txt
*/
/*
Waypoints is a small jQuery plugin that makes it easy to execute a function
whenever you scroll to an element.
GitHub Repository: https://github.com/imakewebthings/jquery-waypoints
Documentation and Examples: http://imakewebthings.github.com/jquery-waypoints
Changelog:
v1.1.6
- Fix potential memory leak by unbinding events on empty context elements.
v1.1.5
- Make plugin compatible with Browserify/RequireJS. (Thanks @cjroebuck)
v1.1.4
- Add handler option to give alternate binding method. (Issue #34)
v1.1.3
- Fix cases where waypoints are added post-load and should be triggered
immediately. (Issue #28)
v1.1.2
- Fixed error thrown by waypoints with triggerOnce option that were
triggered via resize refresh.
v1.1.1
- Fixed bug in initialization where all offsets were being calculated
as if set to 0 initially, causing unwarranted triggers during the
subsequent refresh.
- Added onlyOnScroll, an option for individual waypoints that disables
triggers due to an offset refresh that crosses the current scroll
point. (All credit to @knuton on this one.)
v1.1
- Moved the continuous option out of global settings and into the options
object for individual waypoints.
- Added the context option, which allows for using waypoints within any
scrollable element, not just the window.
v1.0.2
- Moved scroll and resize handler bindings out of load. Should play nicer
with async loaders like Head JS and LABjs.
- Fixed a 1px off error when using certain % offsets.
- Added unit tests.
v1.0.1
- Added $.waypoints('viewportHeight').
- Fixed iOS bug (using the new viewportHeight method).
- Added offset function alias: 'bottom-in-view'.
v1.0
- Initial release.
Support:
- jQuery versions 1.4.3+
- IE6+, FF3+, Chrome 6+, Safari 4+, Opera 11
- Other versions and browsers may work, these are just the ones I've looked at.
*/
(function($, wp, wps, window, undefined){
'$:nomunge';
var $w = $(window),
// Keeping common strings as variables = better minification
eventName = 'waypoint.reached',
/*
For the waypoint and direction passed in, trigger the waypoint.reached
event and deal with the triggerOnce option.
*/
triggerWaypoint = function(way, dir) {
way.element.trigger(eventName, dir);
if (way.options.triggerOnce) {
way.element[wp]('destroy');
}
},
/*
Given a jQuery element and Context, returns the index of that element in the waypoints
array. Returns the index, or -1 if the element is not a waypoint.
*/
waypointIndex = function(el, context) {
if (!context) return -1;
var i = context.waypoints.length - 1;
while (i >= 0 && context.waypoints[i].element[0] !== el[0]) {
i -= 1;
}
return i;
},
// Private list of all elements used as scrolling contexts for waypoints.
contexts = [],
/*
Context Class - represents a scrolling context. Properties include:
element: jQuery object containing a single HTML element.
waypoints: Array of waypoints operating under this scroll context.
oldScroll: Keeps the previous scroll position to determine scroll direction.
didScroll: Flag used in scrolling the context's scroll event.
didResize: Flag used in scrolling the context's resize event.
doScroll: Function that checks for crossed waypoints. Called from throttler.
*/
Context = function(context) {
$.extend(this, {
element: $(context),
oldScroll: 0,
/*
List of all elements that have been registered as waypoints.
Each object in the array contains:
element: jQuery object containing a single HTML element.
offset: The window scroll offset, in px, that triggers the waypoint event.
options: Options object that was passed to the waypoint fn function.
*/
'waypoints': [],
didScroll: false,
didResize: false,
doScroll: $.proxy(function() {
var newScroll = this.element.scrollTop(),
// Are we scrolling up or down? Used for direction argument in callback.
isDown = newScroll > this.oldScroll,
that = this,
// Get a list of all waypoints that were crossed since last scroll move.
pointsHit = $.grep(this.waypoints, function(el, i) {
return isDown ?
(el.offset > that.oldScroll && el.offset <= newScroll) :
(el.offset <= that.oldScroll && el.offset > newScroll);
}),
len = pointsHit.length;
// iOS adjustment
if (!this.oldScroll || !newScroll) {
$[wps]('refresh');
}
// Done with scroll comparisons, store new scroll before ejection
this.oldScroll = newScroll;
// No waypoints crossed? Eject.
if (!len) return;
// If several waypoints triggered, need to do so in reverse order going up
if (!isDown) pointsHit.reverse();
/*
One scroll move may cross several waypoints. If the waypoint's continuous
option is true it should fire even if it isn't the last waypoint. If false,
it will only fire if it's the last one.
*/
$.each(pointsHit, function(i, point) {
if (point.options.continuous || i === len - 1) {
triggerWaypoint(point, [isDown ? 'down' : 'up']);
}
});
}, this)
});
// Setup scroll and resize handlers. Throttled at the settings-defined rate limits.
$(context).bind('scroll.waypoints', $.proxy(function() {
if (!this.didScroll) {
this.didScroll = true;
window.setTimeout($.proxy(function() {
this.doScroll();
this.didScroll = false;
}, this), $[wps].settings.scrollThrottle);
}
}, this)).bind('resize.waypoints', $.proxy(function() {
if (!this.didResize) {
this.didResize = true;
window.setTimeout($.proxy(function() {
$[wps]('refresh');
this.didResize = false;
}, this), $[wps].settings.resizeThrottle);
}
}, this));
$w.load($.proxy(function() {
/*
Fire a scroll check, should the page be loaded at a non-zero scroll value,
as with a fragment id link or a page refresh.
*/
this.doScroll();
}, this));
},
/* Returns a Context object from the contexts array, given the raw HTML element
for that context. */
getContextByElement = function(element) {
var found = null;
$.each(contexts, function(i, c) {
if (c.element[0] === element) {
found = c;
return false;
}
});
return found;
},
// Methods exposed to the effin' object
methods = {
/*
jQuery.fn.waypoint([handler], [options])
handler
function, optional
A callback function called when the user scrolls past the element.
The function signature is function(event, direction) where event is
a standard jQuery Event Object and direction is a string, either 'down'
or 'up' indicating which direction the user is scrolling.
options
object, optional
A map of options to apply to this set of waypoints, including where on
the browser window the waypoint is triggered. For a full list of
options and their defaults, see $.fn.waypoint.defaults.
This is how you register an element as a waypoint. When the user scrolls past
that element it triggers waypoint.reached, a custom event. Since the
parameters for creating a waypoint are optional, we have a few different
possible signatures. Lets look at each of them.
someElements.waypoint();
Calling .waypoint with no parameters will register the elements as waypoints
using the default options. The elements will fire the waypoint.reached event,
but calling it in this way does not bind any handler to the event. You can
bind to the event yourself, as with any other event, like so:
someElements.bind('waypoint.reached', function(event, direction) {
// make it rain
});
You will usually want to create a waypoint and immediately bind a function to
waypoint.reached, and can do so by passing a handler as the first argument to
.waypoint:
someElements.waypoint(function(event, direction) {
if (direction === 'down') {
// do this on the way down
}
else {
// do this on the way back up through the waypoint
}
});
This will still use the default options, which will trigger the waypoint when
the top of the element hits the top of the window. We can pass .waypoint an
options object to customize things:
someElements.waypoint(function(event, direction) {
// do something amazing
}, {
offset: '50%' // middle of the page
});
You can also pass just an options object.
someElements.waypoint({
offset: 100 // 100px from the top
});
This behaves like .waypoint(), in that it registers the elements as waypoints
but binds no event handlers.
Calling .waypoint on an existing waypoint will extend the previous options.
If the call includes a handler, it will be bound to waypoint.reached without
unbinding any other handlers.
*/
init: function(f, options) {
// Register each element as a waypoint, add to array.
this.each(function() {
var cElement = $.fn[wp].defaults.context,
context,
$this = $(this);
// Default window context or a specific element?
if (options && options.context) {
cElement = options.context;
}
// Find the closest element that matches the context
if (!$.isWindow(cElement)) {
cElement = $this.closest(cElement)[0];
}
context = getContextByElement(cElement);
// Not a context yet? Create and push.
if (!context) {
context = new Context(cElement);
contexts.push(context);
}
// Extend default and preexisting options
var ndx = waypointIndex($this, context),
base = ndx < 0 ? $.fn[wp].defaults : context.waypoints[ndx].options,
opts = $.extend({}, base, options);
// Offset aliases
opts.offset = opts.offset === "bottom-in-view" ?
function() {
var cHeight = $.isWindow(cElement) ? $[wps]('viewportHeight')
: $(cElement).height();
return cHeight - $(this).outerHeight();
} : opts.offset;
// Update, or create new waypoint
if (ndx < 0) {
context.waypoints.push({
'element': $this,
'offset': null,
'options': opts
});
}
else {
context.waypoints[ndx].options = opts;
}
// Bind the function if it was passed in.
if (f) {
$this.bind(eventName, f);
}
// Bind the function in the handler option if it exists.
if (options && options.handler) {
$this.bind(eventName, options.handler);
}
});
// Need to re-sort+refresh the waypoints array after new elements are added.
$[wps]('refresh');
return this;
},
/*
jQuery.fn.waypoint('remove')
Passing the string 'remove' to .waypoint unregisters the elements as waypoints
and wipes any custom options, but leaves the waypoint.reached events bound.
Calling .waypoint again in the future would reregister the waypoint and the old
handlers would continue to work.
*/
remove: function() {
return this.each(function(i, el) {
var $el = $(el);
$.each(contexts, function(i, c) {
var ndx = waypointIndex($el, c);
if (ndx >= 0) {
c.waypoints.splice(ndx, 1);
if (!c.waypoints.length) {
c.element.unbind('scroll.waypoints resize.waypoints');
contexts.splice(i, 1);
}
}
});
});
},
/*
jQuery.fn.waypoint('destroy')
Passing the string 'destroy' to .waypoint will unbind all waypoint.reached
event handlers on those elements and unregisters them as waypoints.
*/
destroy: function() {
return this.unbind(eventName)[wp]('remove');
}
},
/*
Methods used by the jQuery object extension.
*/
jQMethods = {
/*
jQuery.waypoints('refresh')
This will force a recalculation of each waypoints trigger point based on
its offset option and context. This is called automatically whenever the window
(or other defined context) is resized, new waypoints are added, or a waypoints
options are modified. If your project is changing the DOM or page layout without
doing one of these things, you may want to manually call this refresh.
*/
refresh: function() {
$.each(contexts, function(i, c) {
var isWin = $.isWindow(c.element[0]),
contextOffset = isWin ? 0 : c.element.offset().top,
contextHeight = isWin ? $[wps]('viewportHeight') : c.element.height(),
contextScroll = isWin ? 0 : c.element.scrollTop();
$.each(c.waypoints, function(j, o) {
/* $.each isn't safe from element removal due to triggerOnce.
Should rewrite the loop but this is way easier. */
if (!o) return;
// Adjustment is just the offset if it's a px value
var adjustment = o.options.offset,
oldOffset = o.offset;
// Set adjustment to the return value if offset is a function.
if (typeof o.options.offset === "function") {
adjustment = o.options.offset.apply(o.element);
}
// Calculate the adjustment if offset is a percentage.
else if (typeof o.options.offset === "string") {
var amount = parseFloat(o.options.offset);
adjustment = o.options.offset.indexOf("%") ?
Math.ceil(contextHeight * (amount / 100)) : amount;
}
/*
Set the element offset to the window scroll offset, less
all our adjustments.
*/
o.offset = o.element.offset().top - contextOffset
+ contextScroll - adjustment;
/*
An element offset change across the current scroll point triggers
the event, just as if we scrolled past it unless prevented by an
optional flag.
*/
if (o.options.onlyOnScroll) return;
if (oldOffset !== null && c.oldScroll > oldOffset && c.oldScroll <= o.offset) {
triggerWaypoint(o, ['up']);
}
else if (oldOffset !== null && c.oldScroll < oldOffset && c.oldScroll >= o.offset) {
triggerWaypoint(o, ['down']);
}
/* For new waypoints added after load, check that down should have
already been triggered */
else if (!oldOffset && c.element.scrollTop() > o.offset) {
triggerWaypoint(o, ['down']);
}
});
// Keep waypoints sorted by offset value.
c.waypoints.sort(function(a, b) {
return a.offset - b.offset;
});
});
},
/*
jQuery.waypoints('viewportHeight')
This will return the height of the viewport, adjusting for inconsistencies
that come with calling $(window).height() in iOS. Recommended for use
within any offset functions.
*/
viewportHeight: function() {
return (window.innerHeight ? window.innerHeight : $w.height());
},
/*
jQuery.waypoints()
This will return a jQuery object with a collection of all registered waypoint
elements.
$('.post').waypoint();
$('.ad-unit').waypoint(function(event, direction) {
// Passed an ad unit
});
console.log($.waypoints());
The example above would log a jQuery object containing all .post and .ad-unit
elements.
*/
aggregate: function() {
var points = $();
$.each(contexts, function(i, c) {
$.each(c.waypoints, function(i, e) {
points = points.add(e.element);
});
});
return points;
}
};
/*
fn extension. Delegates to appropriate method.
*/
$.fn[wp] = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof method === "function" || !method) {
return methods.init.apply(this, arguments);
}
else if (typeof method === "object") {
return methods.init.apply(this, [null, method]);
}
else {
$.error( 'Method ' + method + ' does not exist on jQuery ' + wp );
}
};
/*
The default options object that is extended when calling .waypoint. It has the
following properties:
context
string | element | jQuery*
default: window
The context defines which scrollable element the waypoint belongs to and acts
within. The default, window, means the waypoint offset is calculated with relation
to the whole viewport. You can set this to another element to use the waypoints
within that element. Accepts a selector string, *but if you use jQuery 1.6+ it
also accepts a raw HTML element or jQuery object.
continuous
boolean
default: true
If true, and multiple waypoints are triggered in one scroll, this waypoint will
trigger even if it is not the last waypoint reached. If false, it will only
trigger if it is the last waypoint.
handler
function
default: undefined
An alternative way to bind functions to the waypoint, without using the function
as the first argument to the waypoint function.
offset
number | string | function
default: 0
Determines how far the top of the element must be from the top of the browser
window to trigger a waypoint. It can be a number, which is taken as a number
of pixels, a string representing a percentage of the viewport height, or a
function that will return a number of pixels.
onlyOnScroll
boolean
default: false
If true, this waypoint will not trigger if an offset change during a refresh
causes it to pass the current scroll point.
triggerOnce
boolean
default: false
If true, the waypoint will be destroyed when triggered.
An offset of 250 would trigger the waypoint when the top of the element is 250px
from the top of the viewport. Negative values for any offset work as you might
expect. A value of -100 would trigger the waypoint when the element is 100px above
the top of the window.
offset: '100%'
A string percentage will determine the pixel offset based on the height of the
window. When resizing the window, this offset will automatically be recalculated
without needing to call $.waypoints('refresh').
// The bottom of the element is in view
offset: function() {
return $.waypoints('viewportHeight') - $(this).outerHeight();
}
Offset can take a function, which must return a number of pixels from the top of
the window. The this value will always refer to the raw HTML element of the
waypoint. As with % values, functions are recalculated automatically when the
window resizes. For more on recalculating offsets, see $.waypoints('refresh').
An offset value of 'bottom-in-view' will act as an alias for the function in the
example above, as this is a common usage.
offset: 'bottom-in-view'
You can see this alias in use on the Scroll Analytics example page.
The triggerOnce flag, if true, will destroy the waypoint after the first trigger.
This is just a shortcut for calling .waypoint('destroy') within the waypoint
handler. This is useful in situations such as scroll analytics, where you only
want to record an event once for each page visit.
The context option lets you use Waypoints within an element other than the window.
You can define the context with a selector string and the waypoint will act within
the nearest ancestor that matches this selector.
$('.something-scrollable .waypoint').waypoint({
context: '.something-scrollable'
});
You can see this in action on the Dial Controls example.
The handler option gives authors an alternative way to bind functions when
creating a waypoint. In place of:
$('.item').waypoint(function(event, direction) {
// make things happen
});
You may instead write:
$('.item').waypoint({
handler: function(event, direction) {
// make things happen
}
});
*/
$.fn[wp].defaults = {
continuous: true,
offset: 0,
triggerOnce: false,
context: window
};
/*
jQuery object extension. Delegates to appropriate methods above.
*/
$[wps] = function(method) {
if (jQMethods[method]) {
return jQMethods[method].apply(this);
}
else {
return jQMethods['aggregate']();
}
};
/*
$.waypoints.settings
Settings object that determines some of the plugins behavior.
resizeThrottle
number
default: 200
For performance reasons, the refresh performed during resizes is
throttled. This value is the rate-limit in milliseconds between resize
refreshes. For more information on throttling, check out Ben Almans
throttle / debounce plugin.
http://benalman.com/projects/jquery-throttle-debounce-plugin/
scrollThrottle
number
default: 100
For performance reasons, checking for any crossed waypoints during a
scroll event is throttled. This value is the rate-limit in milliseconds
between scroll checks. For more information on throttling, check out Ben
Almans throttle / debounce plugin.
http://benalman.com/projects/jquery-throttle-debounce-plugin/
*/
$[wps].settings = {
resizeThrottle: 200,
scrollThrottle: 100
};
$w.load(function() {
// Calculate everything once on load.
$[wps]('refresh');
});
})(jQuery, 'waypoint', 'waypoints', window);

View file

@ -0,0 +1,90 @@
/* ==========================================================
* bootstrap-alert.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-dismiss="alert"]'
, Alert = function (el) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype.close = function (e) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.trigger(e = $.Event('close'))
if (e.isDefaultPrevented()) return
$parent.removeClass('in')
function removeElement() {
$parent
.trigger('closed')
.remove()
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(function () {
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
})
}(window.jQuery);

View file

@ -0,0 +1,96 @@
/* ============================================================
* bootstrap-button.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* BUTTON PUBLIC CLASS DEFINITION
* ============================== */
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype.setState = function (state) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
Button.prototype.toggle = function () {
var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$.fn.button.Constructor = Button
/* BUTTON DATA-API
* =============== */
$(function () {
$('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
$btn.button('toggle')
})
})
}(window.jQuery);

View file

@ -0,0 +1,169 @@
/* ==========================================================
* bootstrap-carousel.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* CAROUSEL CLASS DEFINITION
* ========================= */
var Carousel = function (element, options) {
this.$element = $(element)
this.options = options
this.options.slide && this.slide(this.options.slide)
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
.on('mouseleave', $.proxy(this.cycle, this))
}
Carousel.prototype = {
cycle: function (e) {
if (!e) this.paused = false
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
, to: function (pos) {
var $active = this.$element.find('.active')
, children = $active.parent().children()
, activePos = children.index($active)
, that = this
if (pos > (children.length - 1) || pos < 0) return
if (this.sliding) {
return this.$element.one('slid', function () {
that.to(pos)
})
}
if (activePos == pos) {
return this.pause().cycle()
}
return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
}
, pause: function (e) {
if (!e) this.paused = true
clearInterval(this.interval)
this.interval = null
return this
}
, next: function () {
if (this.sliding) return
return this.slide('next')
}
, prev: function () {
if (this.sliding) return
return this.slide('prev')
}
, slide: function (type, next) {
var $active = this.$element.find('.active')
, $next = next || $active[type]()
, isCycling = this.interval
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
, e = $.Event('slide')
this.sliding = true
isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
if ($next.hasClass('active')) return
if ($.support.transition && this.$element.hasClass('slide')) {
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
this.$element.one($.support.transition.end, function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
} else {
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger('slid')
}
isCycling && this.cycle()
return this
}
}
/* CAROUSEL PLUGIN DEFINITION
* ========================== */
$.fn.carousel = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
, options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (typeof option == 'string' || (option = options.slide)) data[option]()
else if (options.interval) data.cycle()
})
}
$.fn.carousel.defaults = {
interval: 5000
, pause: 'hover'
}
$.fn.carousel.Constructor = Carousel
/* CAROUSEL DATA-API
* ================= */
$(function () {
$('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
$target.carousel(options)
e.preventDefault()
})
})
}(window.jQuery);

View file

@ -0,0 +1,157 @@
/* =============================================================
* bootstrap-collapse.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* COLLAPSE PUBLIC CLASS DEFINITION
* ================================ */
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.collapse.defaults, options)
if (this.options.parent) {
this.$parent = $(this.options.parent)
}
this.options.toggle && this.toggle()
}
Collapse.prototype = {
constructor: Collapse
, dimension: function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
, show: function () {
var dimension
, scroll
, actives
, hasData
if (this.transitioning) return
dimension = this.dimension()
scroll = $.camelCase(['scroll', dimension].join('-'))
actives = this.$parent && this.$parent.find('> .accordion-group > .in')
if (actives && actives.length) {
hasData = actives.data('collapse')
if (hasData && hasData.transitioning) return
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
this.$element[dimension](0)
this.transition('addClass', $.Event('show'), 'shown')
this.$element[dimension](this.$element[0][scroll])
}
, hide: function () {
var dimension
if (this.transitioning) return
dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', $.Event('hide'), 'hidden')
this.$element[dimension](0)
}
, reset: function (size) {
var dimension = this.dimension()
this.$element
.removeClass('collapse')
[dimension](size || 'auto')
[0].offsetWidth
this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
return this
}
, transition: function (method, startEvent, completeEvent) {
var that = this
, complete = function () {
if (startEvent.type == 'show') that.reset()
that.transitioning = 0
that.$element.trigger(completeEvent)
}
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
this.transitioning = 1
this.$element[method]('in')
$.support.transition && this.$element.hasClass('collapse') ?
this.$element.one($.support.transition.end, complete) :
complete()
}
, toggle: function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
}
/* COLLAPSIBLE PLUGIN DEFINITION
* ============================== */
$.fn.collapse = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
, options = typeof option == 'object' && option
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.collapse.defaults = {
toggle: true
}
$.fn.collapse.Constructor = Collapse
/* COLLAPSIBLE DATA-API
* ==================== */
$(function () {
$('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$(target).collapse(option)
})
})
}(window.jQuery);

View file

@ -0,0 +1,100 @@
/* ============================================================
* bootstrap-dropdown.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-toggle="dropdown"]'
, Dropdown = function (element) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function (e) {
var $this = $(this)
, $parent
, selector
, isActive
if ($this.is('.disabled, :disabled')) return
selector = $this.attr('data-target')
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
isActive = $parent.hasClass('open')
clearMenus()
if (!isActive) $parent.toggleClass('open')
return false
}
}
function clearMenus() {
$(toggle).parent().removeClass('open')
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(function () {
$('html').on('click.dropdown.data-api', clearMenus)
$('body')
.on('click.dropdown', '.dropdown form', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
})
}(window.jQuery);

View file

@ -0,0 +1,218 @@
/* =========================================================
* bootstrap-modal.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
!function ($) {
"use strict"; // jshint ;_;
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function (content, options) {
this.options = options
this.$element = $(content)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
, e = $.Event('show')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
$('body').addClass('modal-open')
this.isShown = true
escape.call(this)
backdrop.call(this, function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(document.body) //don't move modals dom position
}
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element.addClass('in')
transition ?
that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
that.$element.trigger('shown')
})
}
, hide: function (e) {
e && e.preventDefault()
var that = this
e = $.Event('hide')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
$('body').removeClass('modal-open')
escape.call(this)
this.$element.removeClass('in')
$.support.transition && this.$element.hasClass('fade') ?
hideWithTransition.call(this) :
hideModal.call(this)
}
}
/* MODAL PRIVATE METHODS
* ===================== */
function hideWithTransition() {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
hideModal.call(that)
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
hideModal.call(that)
})
}
function hideModal(that) {
this.$element
.hide()
.trigger('hidden')
backdrop.call(this)
}
function backdrop(callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
if (this.options.backdrop != 'static') {
this.$backdrop.click($.proxy(this.hide, this))
}
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
removeBackdrop.call(this)
} else if (callback) {
callback()
}
}
function removeBackdrop() {
this.$backdrop.remove()
this.$backdrop = null
}
function escape() {
var that = this
if (this.isShown && this.options.keyboard) {
$(document).on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
$(document).off('keyup.dismiss.modal')
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
}
$.fn.modal.Constructor = Modal
/* MODAL DATA-API
* ============== */
$(function () {
$('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
e.preventDefault()
$target.modal(option)
})
})
}(window.jQuery);

View file

@ -0,0 +1,98 @@
/* ===========================================================
* bootstrap-popover.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* POPOVER PUBLIC CLASS DEFINITION
* =============================== */
var Popover = function ( element, options ) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.isHTML(content) ? 'html' : 'text'](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
return content
}
, tip: function () {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, content: ''
, template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}(window.jQuery);

View file

@ -0,0 +1,151 @@
/* =============================================================
* bootstrap-scrollspy.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* SCROLLSPY CLASS DEFINITION
* ========================== */
function ScrollSpy( element, options) {
var process = $.proxy(this.process, this)
, $element = $(element).is('body') ? $(window) : $(element)
, href
this.options = $.extend({}, $.fn.scrollspy.defaults, options)
this.$scrollElement = $element.on('scroll.scroll.data-api', process)
this.selector = (this.options.target
|| ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|| '') + ' .nav li > a'
this.$body = $('body')
this.refresh()
this.process()
}
ScrollSpy.prototype = {
constructor: ScrollSpy
, refresh: function () {
var self = this
, $targets
this.offsets = $([])
this.targets = $([])
$targets = this.$body
.find(this.selector)
.map(function () {
var $el = $(this)
, href = $el.data('target') || $el.attr('href')
, $href = /^#\w/.test(href) && $(href)
return ( $href
&& href.length
&& [[ $href.position().top, href ]] ) || null
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
self.offsets.push(this[0])
self.targets.push(this[1])
})
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
, scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
, maxScroll = scrollHeight - this.$scrollElement.height()
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
if (scrollTop >= maxScroll) {
return activeTarget != (i = targets.last()[0])
&& this.activate ( i )
}
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activate( targets[i] )
}
}
, activate: function (target) {
var active
, selector
this.activeTarget = target
$(this.selector)
.parent('.active')
.removeClass('active')
selector = this.selector
+ '[data-target="' + target + '"],'
+ this.selector + '[href="' + target + '"]'
active = $(selector)
.parent('li')
.addClass('active')
if (active.parent('.dropdown-menu')) {
active = active.closest('li.dropdown').addClass('active')
}
active.trigger('activate')
}
}
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollspy = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
, options = typeof option == 'object' && option
if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.scrollspy.Constructor = ScrollSpy
$.fn.scrollspy.defaults = {
offset: 10
}
/* SCROLLSPY DATA-API
* ================== */
$(function () {
$('[data-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
})
})
}(window.jQuery);

View file

@ -0,0 +1,135 @@
/* ========================================================
* bootstrap-tab.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ======================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function ( element ) {
this.element = $(element)
}
Tab.prototype = {
constructor: Tab
, show: function () {
var $this = this.element
, $ul = $this.closest('ul:not(.dropdown-menu)')
, selector = $this.attr('data-target')
, previous
, $target
, e
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
if ( $this.parent('li').hasClass('active') ) return
previous = $ul.find('.active a').last()[0]
e = $.Event('show', {
relatedTarget: previous
})
$this.trigger(e)
if (e.isDefaultPrevented()) return
$target = $(selector)
this.activate($this.parent('li'), $ul)
this.activate($target, $target.parent(), function () {
$this.trigger({
type: 'shown'
, relatedTarget: previous
})
})
}
, activate: function ( element, container, callback) {
var $active = container.find('> .active')
, transition = callback
&& $.support.transition
&& $active.hasClass('fade')
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
callback && callback()
}
transition ?
$active.one($.support.transition.end, next) :
next()
$active.removeClass('in')
}
}
/* TAB PLUGIN DEFINITION
* ===================== */
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tab')
if (!data) $this.data('tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tab.Constructor = Tab
/* TAB DATA-API
* ============ */
$(function () {
$('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
e.preventDefault()
$(this).tab('show')
})
})
}(window.jQuery);

View file

@ -0,0 +1,275 @@
/* ===========================================================
* bootstrap-tooltip.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function (element, options) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function (type, element, options) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function (options) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) return self.show()
clearTimeout(this.timeout)
self.hoverState = 'in'
this.timeout = setTimeout(function() {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
, leave: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (this.timeout) clearTimeout(this.timeout)
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.hoverState = 'out'
this.timeout = setTimeout(function() {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.remove()
.css({ top: 0, left: 0, display: 'block' })
.appendTo(inside ? this.$element : document.body)
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.css(tp)
.addClass(placement)
.addClass('in')
}
}
, isHTML: function(text) {
// html string detection logic adapted from jQuery
return typeof text != 'string'
|| ( text.charAt(0) === "<"
&& text.charAt( text.length - 1 ) === ">"
&& text.length >= 3
) || /^(?:[^<]*<[\w\W]+>[^>]*$)/.exec(text)
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.isHTML(title) ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).remove()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.remove()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.remove()
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function () {
this[this.tip().hasClass('in') ? 'hide' : 'show']()
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
, trigger: 'hover'
, title: ''
, delay: 0
}
}(window.jQuery);

View file

@ -0,0 +1,61 @@
/* ===================================================
* bootstrap-transition.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================== */
!function ($) {
$(function () {
"use strict"; // jshint ;_;
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
* ======================================================= */
$.support.transition = (function () {
var transitionEnd = (function () {
var el = document.createElement('bootstrap')
, transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd'
, 'MozTransition' : 'transitionend'
, 'OTransition' : 'oTransitionEnd'
, 'msTransition' : 'MSTransitionEnd'
, 'transition' : 'transitionend'
}
, name
for (name in transEndEventNames){
if (el.style[name] !== undefined) {
return transEndEventNames[name]
}
}
}())
return transitionEnd && {
end: transitionEnd
}
})()
})
}(window.jQuery);

View file

@ -0,0 +1,285 @@
/* =============================================================
* bootstrap-typeahead.js v2.0.4
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ============================================================ */
!function($){
"use strict"; // jshint ;_;
/* TYPEAHEAD PUBLIC CLASS DEFINITION
* ================================= */
var Typeahead = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.updater = this.options.updater || this.updater
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.shown = false
this.listen()
}
Typeahead.prototype = {
constructor: Typeahead
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
this.$element
.val(this.updater(val))
.change()
return this.hide()
}
, updater: function (item) {
return item
}
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
this.$menu.show()
this.shown = true
return this
}
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
, lookup: function (event) {
var that = this
, items
, q
this.query = this.$element.val()
if (!this.query) {
return this.shown ? this.hide() : this
}
items = $.grep(this.source, function (item) {
return that.matcher(item)
})
items = this.sorter(items)
if (!items.length) {
return this.shown ? this.hide() : this
}
return this.render(items.slice(0, this.options.items)).show()
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
while (item = items.shift()) {
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~item.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
, highlighter: function (item) {
var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
, render: function (items) {
var that = this
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', item)
i.find('a').html(that.highlighter(item))
return i[0]
})
items.first().addClass('active')
this.$menu.html(items)
return this
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
next.addClass('active')
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
if (!prev.length) {
prev = this.$menu.find('li').last()
}
prev.addClass('active')
}
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
if ($.browser.webkit || $.browser.msie) {
this.$element.on('keydown', $.proxy(this.keypress, this))
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, keyup: function (e) {
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
break
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
case 27: // escape
if (!this.shown) return
this.hide()
break
default:
this.lookup()
}
e.stopPropagation()
e.preventDefault()
}
, keypress: function (e) {
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
if (e.type != 'keydown') break
e.preventDefault()
this.prev()
break
case 40: // down arrow
if (e.type != 'keydown') break
e.preventDefault()
this.next()
break
}
e.stopPropagation()
}
, blur: function (e) {
var that = this
setTimeout(function () { that.hide() }, 150)
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
}
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
$.fn.typeahead = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
* ================== */
$(function () {
$('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
})
}(window.jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff