2014-09-14 11:48:12 +00:00
/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
! function ( a , b ) { "object" == typeof module && "object" == typeof module . exports ? module . exports = a . document ? b ( a , ! 0 ) : function ( a ) { if ( ! a . document ) throw new Error ( "jQuery requires a window with a document" ) ; return b ( a ) } : b ( a ) } ( "undefined" != typeof window ? window : this , function ( a , b ) { var c = [ ] , d = c . slice , e = c . concat , f = c . push , g = c . indexOf , h = { } , i = h . toString , j = h . hasOwnProperty , k = { } , l = a . document , m = "2.1.1" , n = function ( a , b ) { return new n . fn . init ( a , b ) } , o = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g , p = /^-ms-/ , q = /-([\da-z])/gi , r = function ( a , b ) { return b . toUpperCase ( ) } ; n . fn = n . prototype = { jquery : m , constructor : n , selector : "" , length : 0 , toArray : function ( ) { return d . call ( this ) } , get : function ( a ) { return null != a ? 0 > a ? this [ a + this . length ] : this [ a ] : d . call ( this ) } , pushStack : function ( a ) { var b = n . merge ( this . constructor ( ) , a ) ; return b . prevObject = this , b . context = this . context , b } , each : function ( a , b ) { return n . each ( this , a , b ) } , map : function ( a ) { return this . pushStack ( n . map ( this , function ( b , c ) { return a . call ( b , c , b ) } ) ) } , slice : function ( ) { return this . pushStack ( d . apply ( this , arguments ) ) } , first : function ( ) { return this . eq ( 0 ) } , last : function ( ) { return this . eq ( - 1 ) } , eq : function ( a ) { var b = this . length , c = + a + ( 0 > a ? b : 0 ) ; return this . pushStack ( c >= 0 && b > c ? [ this [ c ] ] : [ ] ) } , end : function ( ) { return this . prevObject || this . constructor ( null ) } , push : f , sort : c . sort , splice : c . splice } , n . extend = n . fn . extend = function ( ) { var a , b , c , d , e , f , g = arguments [ 0 ] || { } , h = 1 , i = arguments . length , j = ! 1 ; for ( "boolean" == typeof g && ( j = g , g = arguments [ h ] || { } , h ++ ) , "object" == typeof g || n . isFunction ( g ) || ( g = { } ) , h === i && ( g = this , h -- ) ; i > h ; h ++ ) if ( null != ( a = arguments [ h ] ) ) for ( b in a ) c = g [ b ] , d = a [ b ] , g !== d && ( j && d && ( n . isPlainObject ( d ) || ( e = n . isArray ( d ) ) ) ? ( e ? ( e = ! 1 , f = c && n . isArray ( c ) ? c : [ ] ) : f = c && n . isPlainObject ( c ) ? c : { } , g [ b ] = n . extend ( j , f , d ) ) : void 0 !== d && ( g [ b ] = d ) ) ; return g } , n . extend ( { expando : "jQuery" + ( m + Math . random ( ) ) . replace ( /\D/g , "" ) , isReady : ! 0 , error : function ( a ) { throw new Error ( a ) } , noop : function ( ) { } , isFunction : function ( a ) { return "function" === n . type ( a ) } , isArray : Array . isArray , isWindow : function ( a ) { return null != a && a === a . window } , isNumeric : function ( a ) { return ! n . isArray ( a ) && a - parseFloat ( a ) >= 0 } , isPlainObject : function ( a ) { return "object" !== n . type ( a ) || a . nodeType || n . isWindow ( a ) ? ! 1 : a . constructor && ! j . call ( a . constructor . prototype , "isPrototypeOf" ) ? ! 1 : ! 0 } , isEmptyObject : function ( a ) { var b ; for ( b in a ) return ! 1 ; return ! 0 } , type : function ( a ) { return null == a ? a + "" : "object" == typeof a || "function" == typeof a ? h [ i . call ( a ) ] || "object" : typeof a } , globalEval : function ( a ) { var b , c = eval ; a = n . trim ( a ) , a && ( 1 === a . indexOf ( "use strict" ) ? ( b = l . createElement ( "script" ) , b . text = a , l . head . appendChild ( b ) . parentNode . removeChild ( b ) ) : c ( a ) ) } , camelCase : function ( a ) { return a . replace ( p , "ms-" ) . replace ( q , r ) } , nodeName : function ( a , b ) { return a . nodeName && a . nodeName . toLowerCase ( ) === b . toLowerCase ( ) } , each : function ( a , b , c ) { var d , e = 0 , f = a . length , g = s ( a ) ; if ( c ) { if ( g ) { for ( ; f > e ; e ++ ) if ( d = b . apply ( a [ e ] , c ) , d === ! 1 ) break } else for ( e in a ) if ( d = b . apply ( a [ e ] , c ) , d === ! 1 ) break } else if ( g ) { for ( ; f > e ; e ++ ) if ( d = b . call ( a [ e ] , e , a [ e ] ) , d === ! 1 ) break } else for ( e in a ) if ( d = b . call ( a [ e ] , e , a [ e ] ) , d === ! 1 ) break ; return a } , trim : function ( a ) { return null == a ? "" : ( a + "" ) . replace ( o , "" ) } , makeArray : function ( a , b ) { var c = b || [ ] ; return null != a && ( s ( Object ( a ) ) ? n . merge ( c , "string" == typeof a ? [ a ] : a ) : f . call ( c , a ) ) , c } , inArray : function ( a , b , c ) { return null == b ? - 1 : g . call ( b , a , c ) } , merge : function ( a , b ) { for ( var c = + b . length , d = 0 , e = a . length ; c > d ; d ++ ) a [ e ++ ] = b [ d ] ; return a . length = e , a } , grep : function ( a , b , c ) { for ( var d , e = [ ] , f = 0 , g = a . length , h = ! c ; g > f ; f ++ ) d = ! b ( a [ f ] , f ) , d !== h && e . push ( a [ f ] ) ; return e } , map : function ( a , b , c ) { var d , f = 0 , g = a . length , h = s ( a ) , i = [ ] ; if ( h ) for ( ; g > f ; f ++ ) d = b ( a [ f ] , f , c ) , null != d && i . push ( d ) ; else for ( f in a ) d = b ( a [ f ] , f , c ) , null != d && i . push ( d ) ; return e . apply ( [ ] , i ) } , guid : 1 , proxy : function ( a , b ) { var c , e , f ; return "string" == typeof b && ( c = a [ b ] , b = a , a = c ) , n . isFunction ( a ) ? ( e = d . call ( arguments , 2 ) , f = function ( ) { return a . apply ( b || this , e . concat ( d . call ( arguments ) ) ) } , f . guid = a . guid = a . guid || n . guid ++ , f ) : void 0 } , now : Date . now , support : k } ) , n . each ( "Boolean Number String Function Array Date RegExp Object Error" . split ( " " ) , function ( a , b ) { h [ "[object " + b + "]" ] = b . toLowerCase ( ) } ) ; function s ( a ) { var b = a . length , c = n . type ( a ) ; return "function" === c || n . isWindow ( a ) ? ! 1 : 1 === a . nodeType && b ? ! 0 : "array" === c || 0 === b || " num
} , _data : function ( a , b , c ) { return L . access ( a , b , c ) } , _removeData : function ( a , b ) { L . remove ( a , b ) } } ) , n . fn . extend ( { data : function ( a , b ) { var c , d , e , f = this [ 0 ] , g = f && f . attributes ; if ( void 0 === a ) { if ( this . length && ( e = M . get ( f ) , 1 === f . nodeType && ! L . get ( f , "hasDataAttrs" ) ) ) { c = g . length ; while ( c -- ) g [ c ] && ( d = g [ c ] . name , 0 === d . indexOf ( "data-" ) && ( d = n . camelCase ( d . slice ( 5 ) ) , P ( f , d , e [ d ] ) ) ) ; L . set ( f , "hasDataAttrs" , ! 0 ) } return e } return "object" == typeof a ? this . each ( function ( ) { M . set ( this , a ) } ) : J ( this , function ( b ) { var c , d = n . camelCase ( a ) ; if ( f && void 0 === b ) { if ( c = M . get ( f , a ) , void 0 !== c ) return c ; if ( c = M . get ( f , d ) , void 0 !== c ) return c ; if ( c = P ( f , d , void 0 ) , void 0 !== c ) return c } else this . each ( function ( ) { var c = M . get ( this , d ) ; M . set ( this , d , b ) , - 1 !== a . indexOf ( "-" ) && void 0 !== c && M . set ( this , a , b ) } ) } , null , b , arguments . length > 1 , null , ! 0 ) } , removeData : function ( a ) { return this . each ( function ( ) { M . remove ( this , a ) } ) } } ) , n . extend ( { queue : function ( a , b , c ) { var d ; return a ? ( b = ( b || "fx" ) + "queue" , d = L . get ( a , b ) , c && ( ! d || n . isArray ( c ) ? d = L . access ( a , b , n . makeArray ( c ) ) : d . push ( c ) ) , d || [ ] ) : void 0 } , dequeue : function ( a , b ) { b = b || "fx" ; var c = n . queue ( a , b ) , d = c . length , e = c . shift ( ) , f = n . _queueHooks ( a , b ) , g = function ( ) { n . dequeue ( a , b ) } ; "inprogress" === e && ( e = c . shift ( ) , d -- ) , e && ( "fx" === b && c . unshift ( "inprogress" ) , delete f . stop , e . call ( a , g , f ) ) , ! d && f && f . empty . fire ( ) } , _queueHooks : function ( a , b ) { var c = b + "queueHooks" ; return L . get ( a , c ) || L . access ( a , c , { empty : n . Callbacks ( "once memory" ) . add ( function ( ) { L . remove ( a , [ b + "queue" , c ] ) } ) } ) } } ) , n . fn . extend ( { queue : function ( a , b ) { var c = 2 ; return "string" != typeof a && ( b = a , a = "fx" , c -- ) , arguments . length < c ? n . queue ( this [ 0 ] , a ) : void 0 === b ? this : this . each ( function ( ) { var c = n . queue ( this , a , b ) ; n . _queueHooks ( this , a ) , "fx" === a && "inprogress" !== c [ 0 ] && n . dequeue ( this , a ) } ) } , dequeue : function ( a ) { return this . each ( function ( ) { n . dequeue ( this , a ) } ) } , clearQueue : function ( a ) { return this . queue ( a || "fx" , [ ] ) } , promise : function ( a , b ) { var c , d = 1 , e = n . Deferred ( ) , f = this , g = this . length , h = function ( ) { -- d || e . resolveWith ( f , [ f ] ) } ; "string" != typeof a && ( b = a , a = void 0 ) , a = a || "fx" ; while ( g -- ) c = L . get ( f [ g ] , a + "queueHooks" ) , c && c . empty && ( d ++ , c . empty . add ( h ) ) ; return h ( ) , e . promise ( b ) } } ) ; var Q = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ . source , R = [ "Top" , "Right" , "Bottom" , "Left" ] , S = function ( a , b ) { return a = b || a , "none" === n . css ( a , "display" ) || ! n . contains ( a . ownerDocument , a ) } , T = /^(?:checkbox|radio)$/i ; ! function ( ) { var a = l . createDocumentFragment ( ) , b = a . appendChild ( l . createElement ( "div" ) ) , c = l . createElement ( "input" ) ; c . setAttribute ( "type" , "radio" ) , c . setAttribute ( "checked" , "checked" ) , c . setAttribute ( "name" , "t" ) , b . appendChild ( c ) , k . checkClone = b . cloneNode ( ! 0 ) . cloneNode ( ! 0 ) . lastChild . checked , b . innerHTML = "<textarea>x</textarea>" , k . noCloneChecked = ! ! b . cloneNode ( ! 0 ) . lastChild . defaultValue } ( ) ; var U = "undefined" ; k . focusinBubbles = "onfocusin" in a ; var V = /^key/ , W = /^(?:mouse|pointer|contextmenu)|click/ , X = /^(?:focusinfocus|focusoutblur)$/ , Y = /^([^.]*)(?:\.(.+)|)$/ ; function Z ( ) { return ! 0 } function $ ( ) { return ! 1 } function _ ( ) { try { return l . activeElement } catch ( a ) { } } n . event = { global : { } , add : function ( a , b , c , d , e ) { var f , g , h , i , j , k , l , m , o , p , q , r = L . get ( a ) ; if ( r ) { c . handler && ( f = c , c = f . handler , e = f . selector ) , c . guid || ( c . guid = n . guid ++ ) , ( i = r . events ) || ( i = r . events = { } ) , ( g = r . handle ) || ( g = r . handle = function ( b ) { return typeof n !== U && n . event . triggered !== b . type ? n . event . dispatch . apply ( a , arguments ) : void 0 } ) , b = ( b || "" ) . match ( E ) || [ "" ] , j = b . length ; while ( j -- ) h = Y . exec ( b [ j ] ) || [ ] , o = q = h [ 1 ] , p = ( h [ 2 ] || "" ) . split ( "." ) . sort ( ) , o && ( l = n . event . special [ o ] || { } , o = ( e ? l . delegateType : l . bindType ) || o , l = n . event . special [ o ] || { } , k = n . extend ( { type : o , origType : q , data : d , handler : c , guid : c . guid , selector : e , needsContext : e && n . expr . match . needsContext . test ( e ) , namespace : p . join ( "." ) } , f ) , ( m = i [ o ] ) || ( m = i [ o ] = [ ] , m . delegateCount = 0 , l . setup && l . setup . call ( a , d , p , g ) !== ! 1 || a . addEventListener && a . addEventListener ( o , g , ! 1 ) ) , l . add && ( l . add . call ( a , k ) , k . handler . guid || ( k . handler . guid = c . guid ) ) , e ? m . splice ( m . delegateCount ++ , 0 , k ) : m . push ( k ) , n . event . global [ o ] = ! 0 ) } } , remove : function ( a , b , c , d , e ) { var f , g , h , i , j , k , l , m , o , p , q , r = L . hasData ( a ) && L . get ( a ) ; if ( r && ( i = r . events ) ) { b = ( b || "" ) . match ( E ) || [ "" ] , j = b . length ; while ( j -- ) if ( h = Y . exec ( b [ j ] ) || [ ] , o = q = h [ 1 ] , p = ( h [ 2 ] || "" ) . split ( "." ) . sort ( ) , o ) { l = n . event . special [ o ] || { } , o = ( d ? l . delegateType : l . bindType ) || o , m = i [ o ] || [ ] , h = h [ 2 ] && new
} , removeAttr : function ( a , b ) { var c , d , e = 0 , f = b && b . match ( E ) ; if ( f && 1 === a . nodeType ) while ( c = f [ e ++ ] ) d = n . propFix [ c ] || c , n . expr . match . bool . test ( c ) && ( a [ d ] = ! 1 ) , a . removeAttribute ( c ) } , attrHooks : { type : { set : function ( a , b ) { if ( ! k . radioValue && "radio" === b && n . nodeName ( a , "input" ) ) { var c = a . value ; return a . setAttribute ( "type" , b ) , c && ( a . value = c ) , b } } } } } ) , Zb = { set : function ( a , b , c ) { return b === ! 1 ? n . removeAttr ( a , c ) : a . setAttribute ( c , c ) , c } } , n . each ( n . expr . match . bool . source . match ( /\w+/g ) , function ( a , b ) { var c = $b [ b ] || n . find . attr ; $b [ b ] = function ( a , b , d ) { var e , f ; return d || ( f = $b [ b ] , $b [ b ] = e , e = null != c ( a , b , d ) ? b . toLowerCase ( ) : null , $b [ b ] = f ) , e } } ) ; var _b = /^(?:input|select|textarea|button)$/i ; n . fn . extend ( { prop : function ( a , b ) { return J ( this , n . prop , a , b , arguments . length > 1 ) } , removeProp : function ( a ) { return this . each ( function ( ) { delete this [ n . propFix [ a ] || a ] } ) } } ) , n . extend ( { propFix : { "for" : "htmlFor" , "class" : "className" } , prop : function ( a , b , c ) { var d , e , f , g = a . nodeType ; if ( a && 3 !== g && 8 !== g && 2 !== g ) return f = 1 !== g || ! n . isXMLDoc ( a ) , f && ( b = n . propFix [ b ] || b , e = n . propHooks [ b ] ) , void 0 !== c ? e && "set" in e && void 0 !== ( d = e . set ( a , c , b ) ) ? d : a [ b ] = c : e && "get" in e && null !== ( d = e . get ( a , b ) ) ? d : a [ b ] } , propHooks : { tabIndex : { get : function ( a ) { return a . hasAttribute ( "tabindex" ) || _b . test ( a . nodeName ) || a . href ? a . tabIndex : - 1 } } } } ) , k . optSelected || ( n . propHooks . selected = { get : function ( a ) { var b = a . parentNode ; return b && b . parentNode && b . parentNode . selectedIndex , null } } ) , n . each ( [ "tabIndex" , "readOnly" , "maxLength" , "cellSpacing" , "cellPadding" , "rowSpan" , "colSpan" , "useMap" , "frameBorder" , "contentEditable" ] , function ( ) { n . propFix [ this . toLowerCase ( ) ] = this } ) ; var ac = /[\t\r\n\f]/g ; n . fn . extend ( { addClass : function ( a ) { var b , c , d , e , f , g , h = "string" == typeof a && a , i = 0 , j = this . length ; if ( n . isFunction ( a ) ) return this . each ( function ( b ) { n ( this ) . addClass ( a . call ( this , b , this . className ) ) } ) ; if ( h ) for ( b = ( a || "" ) . match ( E ) || [ ] ; j > i ; i ++ ) if ( c = this [ i ] , d = 1 === c . nodeType && ( c . className ? ( " " + c . className + " " ) . replace ( ac , " " ) : " " ) ) { f = 0 ; while ( e = b [ f ++ ] ) d . indexOf ( " " + e + " " ) < 0 && ( d += e + " " ) ; g = n . trim ( d ) , c . className !== g && ( c . className = g ) } return this } , removeClass : function ( a ) { var b , c , d , e , f , g , h = 0 === arguments . length || "string" == typeof a && a , i = 0 , j = this . length ; if ( n . isFunction ( a ) ) return this . each ( function ( b ) { n ( this ) . removeClass ( a . call ( this , b , this . className ) ) } ) ; if ( h ) for ( b = ( a || "" ) . match ( E ) || [ ] ; j > i ; i ++ ) if ( c = this [ i ] , d = 1 === c . nodeType && ( c . className ? ( " " + c . className + " " ) . replace ( ac , " " ) : "" ) ) { f = 0 ; while ( e = b [ f ++ ] ) while ( d . indexOf ( " " + e + " " ) >= 0 ) d = d . replace ( " " + e + " " , " " ) ; g = a ? n . trim ( d ) : "" , c . className !== g && ( c . className = g ) } return this } , toggleClass : function ( a , b ) { var c = typeof a ; return "boolean" == typeof b && "string" === c ? b ? this . addClass ( a ) : this . removeClass ( a ) : this . each ( n . isFunction ( a ) ? function ( c ) { n ( this ) . toggleClass ( a . call ( this , c , this . className , b ) , b ) } : function ( ) { if ( "string" === c ) { var b , d = 0 , e = n ( this ) , f = a . match ( E ) || [ ] ; while ( b = f [ d ++ ] ) e . hasClass ( b ) ? e . removeClass ( b ) : e . addClass ( b ) } else ( c === U || "boolean" === c ) && ( this . className && L . set ( this , "__className__" , this . className ) , this . className = this . className || a === ! 1 ? "" : L . get ( this , "__className__" ) || "" ) } ) } , hasClass : function ( a ) { for ( var b = " " + a + " " , c = 0 , d = this . length ; d > c ; c ++ ) if ( 1 === this [ c ] . nodeType && ( " " + this [ c ] . className + " " ) . replace ( ac , " " ) . indexOf ( b ) >= 0 ) return ! 0 ; return ! 1 } } ) ; var bc = /\r/g ; n . fn . extend ( { val : function ( a ) { var b , c , d , e = this [ 0 ] ; { if ( arguments . length ) return d = n . isFunction ( a ) , this . each ( function ( c ) { var e ; 1 === this . nodeType && ( e = d ? a . call ( this , c , n ( this ) . val ( ) ) : a , null == e ? e = "" : "number" == typeof e ? e += "" : n . isArray ( e ) && ( e = n . map ( e , function ( a ) { return null == a ? "" : a + "" } ) ) , b = n . valHooks [ this . type ] || n . valHooks [ this . nodeName . toLowerCase ( ) ] , b && "set" in b && void 0 !== b . set ( this , e , "value" ) || ( this . value = e ) ) } ) ; if ( e ) return b = n . valHooks [ e . type ] || n . valHooks [ e . nodeName . toLowerCase ( ) ] , b && "get" in b && void 0 !== ( c = b . get ( e , "value" ) ) ? c : ( c = e . value , "string" == typeof c ? c . replace ( bc , "" ) : null == c ? "" : c ) } } } ) , n . extend ( { valHooks : { option : { get : function ( a ) { var b = n . find . attr ( a , "value" ) ; return null != b ? b : n . trim ( n . text ( a ) ) } } , select : { get : function ( a ) { for ( var b , c , d = a . options , e = a . selectedIndex , f = "select-one" === a . type || 0 > e , g = f ? null : [ ] , h = f ? e + 1 : d . length , i = 0 > e ? h : f ? e : 0 ; h > i ; i ++ ) if ( c = d [ i ] , ! ( ! c . selected && i !== e || ( k . optDisabled ? c . disabled : null !== c . getAttribute ( "disabled" ) ) || c . par
//# sourceMappingURL=jquery.min.map
! function ( a ) { "use strict" ; function b ( a , b ) { var c = ( 65535 & a ) + ( 65535 & b ) , d = ( a >> 16 ) + ( b >> 16 ) + ( c >> 16 ) ; return d << 16 | 65535 & c } function c ( a , b ) { return a << b | a >>> 32 - b } function d ( a , d , e , f , g , h ) { return b ( c ( b ( b ( d , a ) , b ( f , h ) ) , g ) , e ) } function e ( a , b , c , e , f , g , h ) { return d ( b & c | ~ b & e , a , b , f , g , h ) } function f ( a , b , c , e , f , g , h ) { return d ( b & e | c & ~ e , a , b , f , g , h ) } function g ( a , b , c , e , f , g , h ) { return d ( b ^ c ^ e , a , b , f , g , h ) } function h ( a , b , c , e , f , g , h ) { return d ( c ^ ( b | ~ e ) , a , b , f , g , h ) } function i ( a , c ) { a [ c >> 5 ] |= 128 << c % 32 , a [ ( c + 64 >>> 9 << 4 ) + 14 ] = c ; var d , i , j , k , l , m = 1732584193 , n = - 271733879 , o = - 1732584194 , p = 271733878 ; for ( d = 0 ; d < a . length ; d += 16 ) i = m , j = n , k = o , l = p , m = e ( m , n , o , p , a [ d ] , 7 , - 680876936 ) , p = e ( p , m , n , o , a [ d + 1 ] , 12 , - 389564586 ) , o = e ( o , p , m , n , a [ d + 2 ] , 17 , 606105819 ) , n = e ( n , o , p , m , a [ d + 3 ] , 22 , - 1044525330 ) , m = e ( m , n , o , p , a [ d + 4 ] , 7 , - 176418897 ) , p = e ( p , m , n , o , a [ d + 5 ] , 12 , 1200080426 ) , o = e ( o , p , m , n , a [ d + 6 ] , 17 , - 1473231341 ) , n = e ( n , o , p , m , a [ d + 7 ] , 22 , - 45705983 ) , m = e ( m , n , o , p , a [ d + 8 ] , 7 , 1770035416 ) , p = e ( p , m , n , o , a [ d + 9 ] , 12 , - 1958414417 ) , o = e ( o , p , m , n , a [ d + 10 ] , 17 , - 42063 ) , n = e ( n , o , p , m , a [ d + 11 ] , 22 , - 1990404162 ) , m = e ( m , n , o , p , a [ d + 12 ] , 7 , 1804603682 ) , p = e ( p , m , n , o , a [ d + 13 ] , 12 , - 40341101 ) , o = e ( o , p , m , n , a [ d + 14 ] , 17 , - 1502002290 ) , n = e ( n , o , p , m , a [ d + 15 ] , 22 , 1236535329 ) , m = f ( m , n , o , p , a [ d + 1 ] , 5 , - 165796510 ) , p = f ( p , m , n , o , a [ d + 6 ] , 9 , - 1069501632 ) , o = f ( o , p , m , n , a [ d + 11 ] , 14 , 643717713 ) , n = f ( n , o , p , m , a [ d ] , 20 , - 373897302 ) , m = f ( m , n , o , p , a [ d + 5 ] , 5 , - 701558691 ) , p = f ( p , m , n , o , a [ d + 10 ] , 9 , 38016083 ) , o = f ( o , p , m , n , a [ d + 15 ] , 14 , - 660478335 ) , n = f ( n , o , p , m , a [ d + 4 ] , 20 , - 405537848 ) , m = f ( m , n , o , p , a [ d + 9 ] , 5 , 568446438 ) , p = f ( p , m , n , o , a [ d + 14 ] , 9 , - 1019803690 ) , o = f ( o , p , m , n , a [ d + 3 ] , 14 , - 187363961 ) , n = f ( n , o , p , m , a [ d + 8 ] , 20 , 1163531501 ) , m = f ( m , n , o , p , a [ d + 13 ] , 5 , - 1444681467 ) , p = f ( p , m , n , o , a [ d + 2 ] , 9 , - 51403784 ) , o = f ( o , p , m , n , a [ d + 7 ] , 14 , 1735328473 ) , n = f ( n , o , p , m , a [ d + 12 ] , 20 , - 1926607734 ) , m = g ( m , n , o , p , a [ d + 5 ] , 4 , - 378558 ) , p = g ( p , m , n , o , a [ d + 8 ] , 11 , - 2022574463 ) , o = g ( o , p , m , n , a [ d + 11 ] , 16 , 1839030562 ) , n = g ( n , o , p , m , a [ d + 14 ] , 23 , - 35309556 ) , m = g ( m , n , o , p , a [ d + 1 ] , 4 , - 1530992060 ) , p = g ( p , m , n , o , a [ d + 4 ] , 11 , 1272893353 ) , o = g ( o , p , m , n , a [ d + 7 ] , 16 , - 155497632 ) , n = g ( n , o , p , m , a [ d + 10 ] , 23 , - 1094730640 ) , m = g ( m , n , o , p , a [ d + 13 ] , 4 , 681279174 ) , p = g ( p , m , n , o , a [ d ] , 11 , - 358537222 ) , o = g ( o , p , m , n , a [ d + 3 ] , 16 , - 722521979 ) , n = g ( n , o , p , m , a [ d + 6 ] , 23 , 76029189 ) , m = g ( m , n , o , p , a [ d + 9 ] , 4 , - 640364487 ) , p = g ( p , m , n , o , a [ d + 12 ] , 11 , - 421815835 ) , o = g ( o , p , m , n , a [ d + 15 ] , 16 , 530742520 ) , n = g ( n , o , p , m , a [ d + 2 ] , 23 , - 995338651 ) , m = h ( m , n , o , p , a [ d ] , 6 , - 198630844 ) , p = h ( p , m , n , o , a [ d + 7 ] , 10 , 1126891415 ) , o = h ( o , p , m , n , a [ d + 14 ] , 15 , - 1416354905 ) , n = h ( n , o , p , m , a [ d + 5 ] , 21 , - 57434055 ) , m = h ( m , n , o , p , a [ d + 12 ] , 6 , 1700485571 ) , p = h ( p , m , n , o , a [ d + 3 ] , 10 , - 1894986606 ) , o = h ( o , p , m , n , a [ d + 10 ] , 15 , - 1051523 ) , n = h ( n , o , p , m , a [ d + 1 ] , 21 , - 2054922799 ) , m = h ( m , n , o , p , a [ d + 8 ] , 6 , 1873313359 ) , p = h ( p , m , n , o , a [ d + 15 ] , 10 , - 30611744 ) , o = h ( o , p , m , n , a [ d + 6 ] , 15 , - 1560198380 ) , n = h ( n , o , p , m , a [ d + 13 ] , 21 , 1309151649 ) , m = h ( m , n , o , p , a [ d + 4 ] , 6 , - 145523070 ) , p = h ( p , m , n , o , a [ d + 11 ] , 10 , - 1120210379 ) , o = h ( o , p , m , n , a [ d + 2 ] , 15 , 718787259 ) , n = h ( n , o , p , m , a [ d + 9 ] , 21 , - 343485551 ) , m = b ( m , i ) , n = b ( n , j ) , o = b ( o , k ) , p = b ( p , l ) ; return [ m , n , o , p ] } function j ( a ) { var b , c = "" ; for ( b = 0 ; b < 32 * a . length ; b += 8 ) c += String . fromCharCode ( a [ b >> 5 ] >>> b % 32 & 255 ) ; return c } function k ( a ) { var b , c = [ ] ; for ( c [ ( a . length >> 2 ) - 1 ] = void 0 , b = 0 ; b < c . length ; b += 1 ) c [ b ] = 0 ; for ( b = 0 ; b < 8 * a . length ; b += 8 ) c [ b >> 5 ] |= ( 255 & a . charCodeAt ( b / 8 ) ) << b % 32 ; return c } function l ( a ) { return j ( i ( k ( a ) , 8 * a . length ) ) } function m ( a , b ) { var c , d , e = k ( a ) , f = [ ] , g = [ ] ; for ( f [ 15 ] = g [ 15 ] = void 0 , e . length > 16 && ( e = i ( e , 8 * a . length ) ) , c = 0 ; 16 > c ; c += 1 ) f [ c ] = 909522486 ^ e [ c ] , g [ c ] = 1549556828 ^ e [ c ] ; return d = i ( f . concat ( k ( b ) ) , 512 + 8 * b . length ) , j ( i ( g . concat ( d ) , 640 ) ) } function n ( a ) { var b , c , d = "0123456789abcdef" , e = "" ; for ( c = 0 ; c < a . length ; c += 1 ) b = a . charCodeAt ( c ) , e += d . charAt ( b >>> 4 & 15 ) + d . charAt ( 15 & b ) ; return e } function o ( a ) { return unescape ( encodeURIComponent ( a ) ) } function p ( a ) { return l ( o ( a ) ) } function q ( a ) { return n ( p ( a ) ) } function r ( a , b ) { return m ( o ( a ) , o ( b ) ) } function s ( a , b ) { return n ( r ( a , b ) ) } function t ( a , b , c ) { return b ? c ? r ( b , a ) : s ( b , a ) : c ? p ( a ) : q ( a ) } "function" == typeof define && define . amd ? define ( function ( ) { return t } ) : a . md5 = t } ( this ) ;
/* mousetrap v1.4.6 craig.is/killing/mice */
( function ( J , r , f ) { function s ( a , b , d ) { a . addEventListener ? a . addEventListener ( b , d , ! 1 ) : a . attachEvent ( "on" + b , d ) } function A ( a ) { if ( "keypress" == a . type ) { var b = String . fromCharCode ( a . which ) ; a . shiftKey || ( b = b . toLowerCase ( ) ) ; return b } return h [ a . which ] ? h [ a . which ] : B [ a . which ] ? B [ a . which ] : String . fromCharCode ( a . which ) . toLowerCase ( ) } function t ( a ) { a = a || { } ; var b = ! 1 , d ; for ( d in n ) a [ d ] ? b = ! 0 : n [ d ] = 0 ; b || ( u = ! 1 ) } function C ( a , b , d , c , e , v ) { var g , k , f = [ ] , h = d . type ; if ( ! l [ a ] ) return [ ] ; "keyup" == h && w ( a ) && ( b = [ a ] ) ; for ( g = 0 ; g < l [ a ] . length ; ++ g ) if ( k =
l [ a ] [ g ] , ! ( ! c && k . seq && n [ k . seq ] != k . level || h != k . action || ( "keypress" != h || d . metaKey || d . ctrlKey ) && b . sort ( ) . join ( "," ) !== k . modifiers . sort ( ) . join ( "," ) ) ) { var m = c && k . seq == c && k . level == v ; ( ! c && k . combo == e || m ) && l [ a ] . splice ( g , 1 ) ; f . push ( k ) } return f } function K ( a ) { var b = [ ] ; a . shiftKey && b . push ( "shift" ) ; a . altKey && b . push ( "alt" ) ; a . ctrlKey && b . push ( "ctrl" ) ; a . metaKey && b . push ( "meta" ) ; return b } function x ( a , b , d , c ) { m . stopCallback ( b , b . target || b . srcElement , d , c ) || ! 1 !== a ( b , d ) || ( b . preventDefault ? b . preventDefault ( ) : b . returnValue = ! 1 , b . stopPropagation ?
b . stopPropagation ( ) : b . cancelBubble = ! 0 ) } function y ( a ) { "number" !== typeof a . which && ( a . which = a . keyCode ) ; var b = A ( a ) ; b && ( "keyup" == a . type && z === b ? z = ! 1 : m . handleKey ( b , K ( a ) , a ) ) } function w ( a ) { return "shift" == a || "ctrl" == a || "alt" == a || "meta" == a } function L ( a , b , d , c ) { function e ( b ) { return function ( ) { u = b ; ++ n [ a ] ; clearTimeout ( D ) ; D = setTimeout ( t , 1E3 ) } } function v ( b ) { x ( d , b , a ) ; "keyup" !== c && ( z = A ( b ) ) ; setTimeout ( t , 10 ) } for ( var g = n [ a ] = 0 ; g < b . length ; ++ g ) { var f = g + 1 === b . length ? v : e ( c || E ( b [ g + 1 ] ) . action ) ; F ( b [ g ] , f , c , a , g ) } } function E ( a , b ) { var d ,
c , e , f = [ ] ; d = "+" === a ? [ "+" ] : a . split ( "+" ) ; for ( e = 0 ; e < d . length ; ++ e ) c = d [ e ] , G [ c ] && ( c = G [ c ] ) , b && "keypress" != b && H [ c ] && ( c = H [ c ] , f . push ( "shift" ) ) , w ( c ) && f . push ( c ) ; d = c ; e = b ; if ( ! e ) { if ( ! p ) { p = { } ; for ( var g in h ) 95 < g && 112 > g || h . hasOwnProperty ( g ) && ( p [ h [ g ] ] = g ) } e = p [ d ] ? "keydown" : "keypress" } "keypress" == e && f . length && ( e = "keydown" ) ; return { key : c , modifiers : f , action : e } } function F ( a , b , d , c , e ) { q [ a + ":" + d ] = b ; a = a . replace ( /\s+/g , " " ) ; var f = a . split ( " " ) ; 1 < f . length ? L ( a , f , b , d ) : ( d = E ( a , d ) , l [ d . key ] = l [ d . key ] || [ ] , C ( d . key , d . modifiers , { type : d . action } ,
c , a , e ) , l [ d . key ] [ c ? "unshift" : "push" ] ( { callback : b , modifiers : d . modifiers , action : d . action , seq : c , level : e , combo : a } ) ) } var h = { 8 : "backspace" , 9 : "tab" , 13 : "enter" , 16 : "shift" , 17 : "ctrl" , 18 : "alt" , 20 : "capslock" , 27 : "esc" , 32 : "space" , 33 : "pageup" , 34 : "pagedown" , 35 : "end" , 36 : "home" , 37 : "left" , 38 : "up" , 39 : "right" , 40 : "down" , 45 : "ins" , 46 : "del" , 91 : "meta" , 93 : "meta" , 224 : "meta" } , B = { 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'" } , H = { "~" : "`" , "!" : "1" ,
"@" : "2" , "#" : "3" , $ : "4" , "%" : "5" , "^" : "6" , "&" : "7" , "*" : "8" , "(" : "9" , ")" : "0" , _ : "-" , "+" : "=" , ":" : ";" , '"' : "'" , "<" : "," , ">" : "." , "?" : "/" , "|" : "\\" } , G = { option : "alt" , command : "meta" , "return" : "enter" , escape : "esc" , mod : /Mac|iPod|iPhone|iPad/ . test ( navigator . platform ) ? "meta" : "ctrl" } , p , l = { } , q = { } , n = { } , D , z = ! 1 , I = ! 1 , u = ! 1 ; for ( f = 1 ; 20 > f ; ++ f ) h [ 111 + f ] = "f" + f ; for ( f = 0 ; 9 >= f ; ++ f ) h [ f + 96 ] = f ; s ( r , "keypress" , y ) ; s ( r , "keydown" , y ) ; s ( r , "keyup" , y ) ; var m = { bind : function ( a , b , d ) { a = a instanceof Array ? a : [ a ] ; for ( var c = 0 ; c < a . length ; ++ c ) F ( a [ c ] , b , d ) ; return this } ,
unbind : function ( a , b ) { return m . bind ( a , function ( ) { } , b ) } , trigger : function ( a , b ) { if ( q [ a + ":" + b ] ) q [ a + ":" + b ] ( { } , a ) ; return this } , reset : function ( ) { l = { } ; q = { } ; return this } , stopCallback : function ( a , b ) { return - 1 < ( " " + b . className + " " ) . indexOf ( " mousetrap " ) ? ! 1 : "INPUT" == b . tagName || "SELECT" == b . tagName || "TEXTAREA" == b . tagName || b . isContentEditable } , handleKey : function ( a , b , d ) { var c = C ( a , b , d ) , e ; b = { } ; var f = 0 , g = ! 1 ; for ( e = 0 ; e < c . length ; ++ e ) c [ e ] . seq && ( f = Math . max ( f , c [ e ] . level ) ) ; for ( e = 0 ; e < c . length ; ++ e ) c [ e ] . seq ? c [ e ] . level == f && ( g = ! 0 ,
b [ c [ e ] . seq ] = 1 , x ( c [ e ] . callback , d , c [ e ] . combo , c [ e ] . seq ) ) : g || x ( c [ e ] . callback , d , c [ e ] . combo ) ; c = "keypress" == d . type && I ; d . type != u || w ( a ) || c || t ( b ) ; I = g && "keydown" == d . type } } ; J . Mousetrap = m ; "function" === typeof define && define . amd && define ( m ) } ) ( window , document ) ;
Mousetrap = function ( a ) { var d = { } , e = a . stopCallback ; a . stopCallback = function ( b , c , a ) { return d [ a ] ? ! 1 : e ( b , c , a ) } ; a . bindGlobal = function ( b , c , e ) { a . bind ( b , c , e ) ; if ( b instanceof Array ) for ( c = 0 ; c < b . length ; c ++ ) d [ b [ c ] ] = ! 0 ; else d [ b ] = ! 0 } ; return a } ( Mousetrap ) ;
/* ToolTips */
( function ( c ) { function b ( e , d ) { return ( typeof e == "function" ) ? ( e . call ( d ) ) : e } function a ( e , d ) { this . $element = c ( e ) ; this . options = d ; this . enabled = true ; this . fixTitle ( ) } a . prototype = { show : function ( ) { var g = this . getTitle ( ) ; if ( g && this . enabled ) { var f = this . tip ( ) ; f . find ( ".tipsy-inner" ) [ this . options . html ? "html" : "text" ] ( g ) ; f [ 0 ] . className = "tipsy" ; f . remove ( ) . css ( { top : 0 , left : 0 , visibility : "hidden" , display : "block" } ) . prependTo ( document . body ) ; var j = c . extend ( { } , this . $element . offset ( ) , { width : this . $element [ 0 ] . offsetWidth , height : this . $element [ 0 ] . offsetHeight } ) ;
var d = f [ 0 ] . offsetWidth , i = f [ 0 ] . offsetHeight , h = b ( this . options . gravity , this . $element [ 0 ] ) ; var e ; switch ( h . charAt ( 0 ) ) { case "n" : e = { top : j . top + j . height + this . options . offset , left : j . left + j . width / 2 - d / 2 } ; break ; case "s" : e = { top : j . top - i - this . options . offset , left : j . left + j . width / 2 - d / 2 } ; break ; case "e" : e = { top : j . top + j . height / 2 - i / 2 , left : j . left - d - this . options . offset } ; break ; case "w" : e = { top : j . top + j . height / 2 - i / 2 , left : j . left + j . width + this . options . offset } ; break } if ( h . length == 2 ) { if ( h . charAt ( 1 ) == "w" ) { e . left = j . left + j . width / 2 - 15 } else { e . left = j . left + j . width / 2 - d + 15
} } f . css ( e ) . addClass ( "tipsy-" + h ) ; f . find ( ".tipsy-arrow" ) [ 0 ] . className = "tipsy-arrow tipsy-arrow-" + h . charAt ( 0 ) ; if ( this . options . className ) { f . addClass ( b ( this . options . className , this . $element [ 0 ] ) ) } if ( this . options . fade ) { f . stop ( ) . css ( { opacity : 0 , display : "block" , visibility : "visible" } ) . animate ( { opacity : this . options . opacity } ) } else { f . css ( { visibility : "visible" , opacity : this . options . opacity } ) } } } , hide : function ( ) { if ( this . options . fade ) { this . tip ( ) . stop ( ) . fadeOut ( function ( ) { c ( this ) . remove ( ) } ) } else { this . tip ( ) . remove ( ) } } , fixTitle : function ( ) { var d = this . $element ;
if ( d . attr ( "title" ) || typeof ( d . attr ( "original-title" ) ) != "string" ) { d . attr ( "original-title" , d . attr ( "title" ) || "" ) . removeAttr ( "title" ) } } , getTitle : function ( ) { var f , d = this . $element , e = this . options ; this . fixTitle ( ) ; var f , e = this . options ; if ( typeof e . title == "string" ) { f = d . attr ( e . title == "title" ? "original-title" : e . title ) } else { if ( typeof e . title == "function" ) { f = e . title . call ( d [ 0 ] ) } } f = ( "" + f ) . replace ( /(^\s*|\s*$)/ , "" ) ; return f || e . fallback } , tip : function ( ) { if ( ! this . $tip ) { this . $tip = c ( '<div class="tipsy"></div>' ) . html ( '<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>' )
} return this . $tip } , 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 } } ; c . fn . tipsy = function ( h ) { if ( h === true ) { return this . data ( "tipsy" ) } else { if ( typeof h == "string" ) { var j = this . data ( "tipsy" ) ; if ( j ) { j [ h ] ( ) } return this } } h = c . extend ( { } , c . fn . tipsy . defaults , h ) ; function g ( l ) { var m = c . data ( l , "tipsy" ) ; if ( ! m ) { m = new a ( l , c . fn . tipsy . elementOptions ( l , h ) ) ;
c . data ( l , "tipsy" , m ) } return m } function k ( ) { var l = g ( this ) ; l . hoverState = "in" ; if ( h . delayIn == 0 ) { l . show ( ) } else { l . fixTitle ( ) ; setTimeout ( function ( ) { if ( l . hoverState == "in" ) { l . show ( ) } } , h . delayIn ) } } function f ( ) { var l = g ( this ) ; l . hoverState = "out" ; if ( h . delayOut == 0 ) { l . hide ( ) } else { setTimeout ( function ( ) { if ( l . hoverState == "out" ) { l . hide ( ) } } , h . delayOut ) } } if ( ! h . live ) { this . each ( function ( ) { g ( this ) } ) } if ( h . trigger != "manual" ) { var d = h . live ? "live" : "bind" , i = h . trigger == "hover" ? "mouseenter" : "focus" , e = h . trigger == "hover" ? "mouseleave" : "blur" ;
this [ d ] ( i , k ) [ d ] ( e , f ) } return this } ; c . fn . tipsy . defaults = { className : null , delayIn : 0 , delayOut : 0 , fade : false , fallback : "" , gravity : "n" , html : false , live : false , offset : 0 , opacity : 0.8 , title : "title" , trigger : "hover" } ; c . fn . tipsy . elementOptions = function ( e , d ) { return c . metadata ? c . extend ( { } , d , c ( e ) . metadata ( ) ) : d } ; c . fn . tipsy . autoNS = function ( ) { return c ( this ) . offset ( ) . top > ( c ( document ) . scrollTop ( ) + c ( window ) . height ( ) / 2 ) ? "s" : "n" } ; c . fn . tipsy . autoWE = function ( ) { return c ( this ) . offset ( ) . left > ( c ( document ) . scrollLeft ( ) + c ( window ) . width ( ) / 2 ) ? "e" : "w"
} ; c . fn . tipsy . autoBounds = function ( e , d ) { return function ( ) { var f = { ns : d [ 0 ] , ew : ( d . length > 1 ? d [ 1 ] : false ) } , i = c ( document ) . scrollTop ( ) + e , g = c ( document ) . scrollLeft ( ) + e , h = c ( this ) ; if ( h . offset ( ) . top < i ) { f . ns = "n" } if ( h . offset ( ) . left < g ) { f . ew = "w" } if ( c ( window ) . width ( ) + c ( document ) . scrollLeft ( ) - h . offset ( ) . left < e ) { f . ew = "e" } if ( c ( window ) . height ( ) + c ( document ) . scrollTop ( ) - h . offset ( ) . top < e ) { f . ns = "s" } return f . ns + ( f . ew ? f . ew : "" ) } } } ) ( jQuery ) ;
/* Browser Detection */
var BrowserDetect = { init : function ( ) { this . browser = this . searchString ( this . dataBrowser ) || "An unknown browser" ; this . version = this . searchVersion ( navigator . userAgent ) || this . searchVersion ( navigator . appVersion ) || "an unknown version" ; this . OS = this . searchString ( this . dataOS ) || "an unknown OS" } , searchString : function ( d ) { for ( var a = 0 ; a < d . length ; a ++ ) { var b = d [ a ] . string ; var c = d [ a ] . prop ; this . versionSearchString = d [ a ] . versionSearch || d [ a ] . identity ; if ( b ) { if ( b . indexOf ( d [ a ] . subString ) != - 1 ) { return d [ a ] . identity } } else { if ( c ) { return d [ a ] . identity } } } } , searchVersion : function ( b ) { var a = b . indexOf ( this . versionSearchString ) ; if ( a == - 1 ) { return } return parseFloat ( b . substring ( a + this . versionSearchString . length + 1 ) ) } , dataBrowser : [ { string : navigator . userAgent , subString : "Chrome" , identity : "Chrome" } , { string : navigator . userAgent , subString : "OmniWeb" , versionSearch : "OmniWeb/" , identity : "OmniWeb" } , { string : navigator . vendor , subString : "Apple" , identity : "Safari" , versionSearch : "Version" } , { prop : window . opera , identity : "Opera" } , { string : navigator . vendor , subString : "iCab" , identity : "iCab" } , { string : navigator . vendor , subString : "KDE" , identity : "Konqueror" } , { string : navigator . userAgent , subString : "Firefox" , identity : "Firefox" } , { string : navigator . vendor , subString : "Camino" , identity : "Camino" } , { string : navigator . userAgent , subString : "Netscape" , identity : "Netscape" } , { string : navigator . userAgent , subString : "MSIE" , identity : "Explorer" , versionSearch : "MSIE" } , { string : navigator . userAgent , subString : "Gecko" , identity : "Mozilla" , versionSearch : "rv" } , { string : navigator . userAgent , subString : "Mozilla" , identity : "Netscape" , versionSearch : "Mozilla" } ] , dataOS : [ { string : navigator . platform , subString : "Win" , identity : "Windows" } , { string : navigator . platform , subString : "Mac" , identity : "Mac" } , { string : navigator . userAgent , subString : "iPhone" , identity : "iPhone/iPod" } , { string : navigator . platform , subString : "Linux" , identity : "Linux" } ] } ; BrowserDetect . init ( ) ;
function mobileBrowser ( ) { if ( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i . test ( navigator . userAgent ) ) return true ; else return false ; }
/* GET */
function gup ( b ) { b = b . replace ( /[\[]/ , "\\[" ) . replace ( /[\]]/ , "\\]" ) ; var a = "[\\?&]" + b + "=([^&#]*)" , d = new RegExp ( a ) , c = d . exec ( window . location . href ) ; if ( c === null ) { return "" } else { return c [ 1 ] } } ;
/*! jQuery Retina Plugin */
( function ( a ) { a . fn . retina = function ( c ) { var d = { "retina-background" : false , "retina-suffix" : "@2x" } ; if ( c ) { a . extend ( d , c ) } var b = function ( f , g ) { var e = new Image ( ) ; e . onload = function ( ) { g ( e ) } ; e . src = f } ; if ( window . devicePixelRatio > 1 ) { this . each ( function ( ) { var e = a ( this ) ; if ( this . tagName . toLowerCase ( ) == "img" && e . attr ( "src" ) ) { var g = e . attr ( "src" ) . replace ( /\.(?!.*\.)/ , d [ "retina-suffix" ] + "." ) ; b ( g , function ( h ) { e . attr ( "src" , h . src ) ; var i = a ( "<div>" ) . append ( e . clone ( ) ) . remove ( ) . html ( ) ; if ( ! ( /(width|height)=["']\d+["']/ . test ( i ) ) ) { e . attr ( "width" , h . width / 2 )
} } ) } if ( d [ "retina-background" ] ) { var f = e . css ( "background-image" ) ; if ( /^url\(.*\)$/ . test ( f ) ) { var g = f . substring ( 4 , f . length - 1 ) . replace ( /\.(?!.*\.)/ , d [ "retina-suffix" ] + "." ) ; b ( g , function ( h ) { e . css ( "background-image" , "url(" + h . src + ")" ) ; if ( e . css ( "background-size" ) == "auto auto" ) { e . css ( "background-size" , ( h . width / 2 ) + "px auto" ) } } ) } } } ) } } } ) ( jQuery ) ;
( function ( $ ) {
var Swipe = function ( el ) {
var self = this ;
this . el = $ ( el ) ;
this . pos = { start : { x : 0 , y : 0 } , end : { x : 0 , y : 0 } } ;
this . startTime ;
el . on ( 'touchstart' , function ( e ) { self . touchStart ( e ) ; } ) ;
el . on ( 'touchmove' , function ( e ) { self . touchMove ( e ) ; } ) ;
el . on ( 'touchend' , function ( e ) { self . swipeEnd ( ) ; } ) ;
el . on ( 'mousedown' , function ( e ) { self . mouseDown ( e ) ; } ) ;
} ;
Swipe . prototype = {
touchStart : function ( e ) {
var touch = e . originalEvent . touches [ 0 ] ;
this . swipeStart ( e , touch . pageX , touch . pageY ) ;
} ,
touchMove : function ( e ) {
var touch = e . originalEvent . touches [ 0 ] ;
this . swipeMove ( e , touch . pageX , touch . pageY ) ;
} ,
mouseDown : function ( e ) {
var self = this ;
this . swipeStart ( e , e . pageX , e . pageY ) ;
this . el . on ( 'mousemove' , function ( e ) { self . mouseMove ( e ) ; } ) ;
this . el . on ( 'mouseup' , function ( ) { self . mouseUp ( ) ; } ) ;
} ,
mouseMove : function ( e ) {
this . swipeMove ( e , e . pageX , e . pageY ) ;
} ,
mouseUp : function ( e ) {
this . swipeEnd ( e ) ;
this . el . off ( 'mousemove' ) ;
this . el . off ( 'mouseup' ) ;
} ,
swipeStart : function ( e , x , y ) {
this . pos . start . x = x ;
this . pos . start . y = y ;
this . pos . end . x = x ;
this . pos . end . y = y ;
this . startTime = new Date ( ) . getTime ( ) ;
this . trigger ( 'swipeStart' , e ) ;
} ,
swipeMove : function ( e , x , y ) {
this . pos . end . x = x ;
this . pos . end . y = y ;
this . trigger ( 'swipeMove' , e ) ;
} ,
swipeEnd : function ( e ) {
this . trigger ( 'swipeEnd' , e ) ;
} ,
trigger : function ( e , originalEvent ) {
var self = this ;
var
event = $ . Event ( e ) ,
x = self . pos . start . x - self . pos . end . x ,
y = self . pos . end . y - self . pos . start . y ,
radians = Math . atan2 ( y , x ) ,
direction = 'up' ,
distance = Math . round ( Math . sqrt ( Math . pow ( x , 2 ) + Math . pow ( y , 2 ) ) ) ,
angle = Math . round ( radians * 180 / Math . PI ) ,
speed = Math . round ( distance / ( new Date ( ) . getTime ( ) - self . startTime ) * 1000 ) ;
if ( angle < 0 ) {
angle = 360 - Math . abs ( angle ) ;
}
if ( ( angle <= 45 && angle >= 0 ) || ( angle <= 360 && angle >= 315 ) ) {
direction = 'left' ;
} else if ( angle >= 135 && angle <= 225 ) {
direction = 'right' ;
} else if ( angle > 45 && angle < 135 ) {
direction = 'down' ;
}
event . originalEvent = originalEvent ;
event . swipe = { x : x , y : y , direction : direction , distance : distance , angle : angle , speed : speed } ;
$ ( self . el ) . trigger ( event ) ;
}
} ;
$ . fn . swipe = function ( ) {
var swipe = new Swipe ( this ) ;
return this ;
} ;
} ) ( jQuery ) ;
/ * *
* @ name Album Module
* @ description Takes care of every action an album can handle and execute .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
album = {
json : null ,
getID : function ( ) {
var id ;
if ( photo . json ) id = photo . json . album ;
else if ( album . json ) id = album . json . id ;
else id = $ ( ".album:hover, .album.active" ) . attr ( "data-id" ) ;
// Search
if ( ! id ) id = $ ( ".album:hover, .album.active" ) . attr ( "data-id" ) ;
if ( ! id ) id = $ ( ".photo:hover, .photo.active" ) . attr ( "data-album-id" ) ;
if ( id ) return id ;
else return false ;
} ,
load : function ( albumID , refresh ) {
var startTime ,
params ,
durationTime ,
waitTime ;
password . get ( albumID , function ( ) {
if ( ! refresh ) {
loadingBar . show ( ) ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomOut" ) ;
lychee . animate ( ".divider" , "fadeOut" ) ;
}
startTime = new Date ( ) . getTime ( ) ;
params = "getAlbum&albumID=" + albumID + "&password=" + password . value ;
lychee . api ( params , function ( data ) {
if ( data === "Warning: Album private!" ) {
if ( document . location . hash . replace ( "#" , "" ) . split ( "/" ) [ 1 ] != undefined ) {
// Display photo only
lychee . setMode ( "view" ) ;
} else {
// Album not public
lychee . content . show ( ) ;
lychee . goto ( "" ) ;
}
return false ;
}
if ( data === "Warning: Wrong password!" ) {
album . load ( albumID , refresh ) ;
return false ;
}
album . json = data ;
durationTime = ( new Date ( ) . getTime ( ) - startTime ) ;
if ( durationTime > 300 ) waitTime = 0 ; else if ( refresh ) waitTime = 0 ; else waitTime = 300 - durationTime ;
if ( ! visible . albums ( ) && ! visible . photo ( ) && ! visible . album ( ) ) waitTime = 0 ;
setTimeout ( function ( ) {
view . album . init ( ) ;
if ( ! refresh ) {
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomIn" ) ;
view . header . mode ( "album" ) ;
}
} , waitTime ) ;
} ) ;
} ) ;
} ,
parse : function ( photo ) {
if ( ! album . json . title ) album . json . title = "Untitled" ;
} ,
add : function ( ) {
var title ,
params ,
buttons ,
isNumber = function ( n ) { return ! isNaN ( parseFloat ( n ) ) && isFinite ( n ) } ;
buttons = [
[ "Create Album" , function ( ) {
title = $ ( ".message input.text" ) . val ( ) ;
if ( title . length === 0 ) title = "Untitled" ;
modal . close ( ) ;
params = "addAlbum&title=" + escape ( encodeURI ( title ) ) ;
lychee . api ( params , function ( data ) {
if ( data === true ) data = 1 ; // Avoid first album to be true
if ( data !== false && isNumber ( data ) ) lychee . goto ( data ) ;
else lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "New Album" , "Enter a title for this album: <input class='text' type='text' maxlength='30' placeholder='Title' value='Untitled'>" , buttons ) ;
} ,
delete : function ( albumIDs ) {
var params ,
buttons ,
albumTitle ;
if ( ! albumIDs ) return false ;
if ( albumIDs instanceof Array === false ) albumIDs = [ albumIDs ] ;
buttons = [
[ "" , function ( ) {
params = "deleteAlbum&albumIDs=" + albumIDs ;
lychee . api ( params , function ( data ) {
if ( visible . albums ( ) ) {
albumIDs . forEach ( function ( id , index , array ) {
albums . json . num -- ;
view . albums . content . delete ( id ) ;
} ) ;
} else lychee . goto ( "" ) ;
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "" , function ( ) { } ]
] ;
if ( albumIDs . toString ( ) === "0" ) {
buttons [ 0 ] [ 0 ] = "Clear Unsorted" ;
buttons [ 1 ] [ 0 ] = "Keep Unsorted" ;
modal . show ( "Clear Unsorted" , "Are you sure you want to delete all photos from 'Unsorted'?<br>This action can't be undone!" , buttons ) ;
} else if ( albumIDs . length === 1 ) {
buttons [ 0 ] [ 0 ] = "Delete Album and Photos" ;
buttons [ 1 ] [ 0 ] = "Keep Album" ;
// Get title
if ( album . json ) albumTitle = album . json . title ;
else if ( albums . json ) albumTitle = albums . json . content [ albumIDs ] . title ;
modal . show ( "Delete Album" , "Are you sure you want to delete the album '" + albumTitle + "' and all of the photos it contains? This action can't be undone!" , buttons ) ;
} else {
buttons [ 0 ] [ 0 ] = "Delete Albums and Photos" ;
buttons [ 1 ] [ 0 ] = "Keep Albums" ;
modal . show ( "Delete Albums" , "Are you sure you want to delete all " + albumIDs . length + " selected albums and all of the photos they contain? This action can't be undone!" , buttons ) ;
}
} ,
setTitle : function ( albumIDs ) {
var oldTitle = "" ,
newTitle ,
params ,
buttons ;
if ( ! albumIDs ) return false ;
if ( albumIDs instanceof Array === false ) albumIDs = [ albumIDs ] ;
if ( albumIDs . length === 1 ) {
// Get old title if only one album is selected
if ( album . json ) oldTitle = album . json . title ;
else if ( albums . json ) oldTitle = albums . json . content [ albumIDs ] . title ;
if ( ! oldTitle ) oldTitle = "" ;
oldTitle = oldTitle . replace ( "'" , "'" ) ;
}
buttons = [
[ "Set Title" , function ( ) {
// Get input
newTitle = $ ( ".message input.text" ) . val ( ) ;
// Remove html from input
newTitle = lychee . removeHTML ( newTitle ) ;
// Set to Untitled when empty
newTitle = ( newTitle === "" ) ? "Untitled" : newTitle ;
if ( visible . album ( ) ) {
album . json . title = newTitle ;
view . album . title ( ) ;
} else if ( visible . albums ( ) ) {
albumIDs . forEach ( function ( id , index , array ) {
albums . json . content [ id ] . title = newTitle ;
view . albums . content . title ( id ) ;
} ) ;
}
params = "setAlbumTitle&albumIDs=" + albumIDs + "&title=" + escape ( encodeURI ( newTitle ) ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
if ( albumIDs . length === 1 ) modal . show ( "Set Title" , "Enter a new title for this album: <input class='text' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>" , buttons ) ;
else modal . show ( "Set Titles" , "Enter a title for all " + albumIDs . length + " selected album: <input class='text' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>" , buttons ) ;
} ,
setDescription : function ( photoID ) {
var oldDescription = album . json . description . replace ( "'" , "'" ) ,
description ,
params ,
buttons ;
buttons = [
[ "Set Description" , function ( ) {
// Get input
description = $ ( ".message input.text" ) . val ( ) ;
// Remove html from input
description = lychee . removeHTML ( description ) ;
if ( visible . album ( ) ) {
album . json . description = description ;
view . album . description ( ) ;
}
params = "setAlbumDescription&albumID=" + photoID + "&description=" + escape ( encodeURI ( description ) ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Set Description" , "Please enter a description for this album: <input class='text' type='text' maxlength='800' placeholder='Description' value='" + oldDescription + "'>" , buttons ) ;
} ,
setPublic : function ( albumID , e ) {
var params ,
password = "" ,
listed = false ,
downloadable = false ;
albums . refresh ( ) ;
if ( ! visible . message ( ) && album . json . public == 0 ) {
modal . show ( "Share Album" , "This album will be shared with the following properties:</p><form><div class='choice'><input type='checkbox' name='listed' value='listed' checked><h2>Visible</h2><p>Listed to visitors of your Lychee.</p></div><div class='choice'><input type='checkbox' name='downloadable' value='downloadable'><h2>Downloadable</h2><p>Visitors of your Lychee can download this album.</p></div><div class='choice'><input type='checkbox' name='password' value='password'><h2>Password protected</h2><p>Only accessible with a valid password.<input class='text' type='password' placeholder='password' value='' style='display: none;'></p></div></form><p style='display: none;'>" , [ [ "Share Album" , function ( ) { album . setPublic ( album . getID ( ) , e ) } ] , [ "Cancel" , function ( ) { } ] ] , - 170 ) ;
$ ( ".message .choice input[name='password']" ) . on ( "change" , function ( ) {
if ( $ ( this ) . prop ( 'checked' ) === true ) $ ( ".message .choice input.text" ) . show ( ) ;
else $ ( ".message .choice input.text" ) . hide ( ) ;
} ) ;
return true ;
}
if ( visible . message ( ) ) {
if ( $ ( ".message .choice input[name='password']:checked" ) . val ( ) === "password" ) {
password = md5 ( $ ( ".message input.text" ) . val ( ) ) ;
album . json . password = 1 ;
} else {
password = "" ;
album . json . password = 0 ;
}
if ( $ ( ".message .choice input[name='listed']:checked" ) . val ( ) === "listed" ) listed = true ;
if ( $ ( ".message .choice input[name='downloadable']:checked" ) . val ( ) === "downloadable" ) downloadable = true ;
}
params = "setAlbumPublic&albumID=" + albumID + "&password=" + password + "&visible=" + listed + "&downloadable=" + downloadable ;
if ( visible . album ( ) ) {
album . json . public = ( album . json . public == 0 ) ? 1 : 0 ;
album . json . password = ( album . json . public == 0 ) ? 0 : album . json . password ;
view . album . public ( ) ;
view . album . password ( ) ;
if ( album . json . public == 1 ) contextMenu . shareAlbum ( albumID , e ) ;
}
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ,
share : function ( service ) {
var link = "" ,
url = location . href ;
switch ( service ) {
case 0 :
link = "https://twitter.com/share?url=" + encodeURI ( url ) ;
break ;
case 1 :
link = "http://www.facebook.com/sharer.php?u=" + encodeURI ( url ) + "&t=" + encodeURI ( album . json . title ) ;
break ;
case 2 :
link = "mailto:?subject=" + encodeURI ( album . json . title ) + "&body=" + encodeURI ( url ) ;
break ;
default :
link = "" ;
break ;
}
if ( link . length > 5 ) location . href = link ;
} ,
getArchive : function ( albumID ) {
var link ;
if ( location . href . indexOf ( "index.html" ) > 0 ) link = location . href . replace ( location . hash , "" ) . replace ( "index.html" , "php/api.php?function=getAlbumArchive&albumID=" + albumID ) ;
else link = location . href . replace ( location . hash , "" ) + "php/api.php?function=getAlbumArchive&albumID=" + albumID ;
if ( lychee . publicMode ) link += "&password=" + password . value ;
location . href = link ;
}
} ;
/ * *
* @ name Albums Module
* @ description Takes care of every action albums can handle and execute .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
albums = {
json : null ,
load : function ( ) {
var startTime ,
durationTime ,
waitTime ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomOut" ) ;
lychee . animate ( ".divider" , "fadeOut" ) ;
startTime = new Date ( ) . getTime ( ) ;
if ( this . json == null ) {
lychee . api ( "getAlbums" , function ( data ) {
/* Smart Albums */
data . unsortedAlbum = {
id : 0 ,
title : "Unsorted" ,
sysdate : data . unsortedNum + " photos" ,
unsorted : 1 ,
thumb0 : data . unsortedThumb0 ,
thumb1 : data . unsortedThumb1 ,
thumb2 : data . unsortedThumb2
} ;
data . starredAlbum = {
id : "f" ,
title : "Starred" ,
sysdate : data . starredNum + " photos" ,
star : 1 ,
thumb0 : data . starredThumb0 ,
thumb1 : data . starredThumb1 ,
thumb2 : data . starredThumb2
} ;
data . publicAlbum = {
id : "s" ,
title : "Public" ,
sysdate : data . publicNum + " photos" ,
public : 1 ,
thumb0 : data . publicThumb0 ,
thumb1 : data . publicThumb1 ,
thumb2 : data . publicThumb2
} ;
data . recentAlbum = {
id : "r" ,
title : "Recent" ,
sysdate : data . recentNum + " photos" ,
recent : 1 ,
thumb0 : data . recentThumb0 ,
thumb1 : data . recentThumb1 ,
thumb2 : data . recentThumb2
} ;
albums . json = data ;
durationTime = ( new Date ( ) . getTime ( ) - startTime ) ;
if ( durationTime > 300 ) waitTime = 0 ; else waitTime = 300 - durationTime ;
if ( ! visible . albums ( ) && ! visible . photo ( ) && ! visible . album ( ) ) waitTime = 0 ;
if ( visible . album ( ) && lychee . content . html ( ) === "" ) waitTime = 0 ;
setTimeout ( function ( ) {
view . header . mode ( "albums" ) ;
view . albums . init ( ) ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomIn" ) ;
} , waitTime ) ;
} ) ;
} else {
view . header . mode ( "albums" ) ;
view . albums . init ( ) ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomIn" ) ;
}
} ,
parse : function ( album ) {
if ( album . password && lychee . publicMode ) {
album . thumb0 = "assets/img/password.svg" ;
album . thumb1 = "assets/img/password.svg" ;
album . thumb2 = "assets/img/password.svg" ;
} else {
if ( ! album . thumb0 ) album . thumb0 = "assets/img/no_images.svg" ;
if ( ! album . thumb1 ) album . thumb1 = "assets/img/no_images.svg" ;
if ( ! album . thumb2 ) album . thumb2 = "assets/img/no_images.svg" ;
}
} ,
refresh : function ( ) {
this . json = null ;
}
} ;
/ * *
* @ name Build Module
* @ description This module is used to generate HTML - Code .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
build = {
divider : function ( title ) {
return "<div class='divider fadeIn'><h1>" + title + "</h1></div>" ;
} ,
editIcon : function ( id ) {
return "<div id='" + id + "' class='edit'><a class='icon-pencil'></a></div>" ;
} ,
multiselect : function ( top , left ) {
return "<div id='multiselect' style='top: " + top + "px; left: " + left + "px;'></div>" ;
} ,
album : function ( albumJSON ) {
if ( ! albumJSON ) return "" ;
var album = "" ,
longTitle = "" ,
title = albumJSON . title ,
typeThumb = "" ;
if ( title != null && title . length > 18 ) {
title = albumJSON . title . substr ( 0 , 18 ) + "..." ;
longTitle = albumJSON . title ;
}
if ( albumJSON . thumb0 . split ( '.' ) . pop ( ) === "svg" ) typeThumb = "nonretina" ;
album += "<div class='album' data-id='" + albumJSON . id + "' data-password='" + albumJSON . password + "'>" ;
album += "<img src='" + albumJSON . thumb2 + "' width='200' height='200' alt='thumb' data-type='nonretina'>" ;
album += "<img src='" + albumJSON . thumb1 + "' width='200' height='200' alt='thumb' data-type='nonretina'>" ;
album += "<img src='" + albumJSON . thumb0 + "' width='200' height='200' alt='thumb' data-type='" + typeThumb + "'>" ;
album += "<div class='overlay'>" ;
if ( albumJSON . password && ! lychee . publicMode ) album += "<h1><span class='icon-lock'></span> " + title + "</h1>" ;
else album += "<h1 title='" + longTitle + "'>" + title + "</h1>" ;
album += "<a>" + albumJSON . sysdate + "</a>" ;
album += "</div>" ;
if ( ! lychee . publicMode ) {
if ( albumJSON . star == 1 ) album += "<a class='badge red icon-star'></a>" ;
if ( albumJSON . public == 1 ) album += "<a class='badge red icon-share'></a>" ;
if ( albumJSON . unsorted == 1 ) album += "<a class='badge red icon-reorder'></a>" ;
if ( albumJSON . recent == 1 ) album += "<a class='badge red icon-time'></a>" ;
}
album += "</div>" ;
return album ;
} ,
photo : function ( photoJSON ) {
if ( ! photoJSON ) return "" ;
var photo = "" ,
longTitle = "" ,
title = photoJSON . title ;
if ( title != null && title . length > 18 ) {
title = photoJSON . title . substr ( 0 , 18 ) + "..." ;
longTitle = photoJSON . title ;
}
photo += "<div class='photo' data-album-id='" + photoJSON . album + "' data-id='" + photoJSON . id + "'>" ;
photo += "<img src='" + photoJSON . thumbUrl + "' width='200' height='200' alt='thumb'>" ;
photo += "<div class='overlay'>" ;
photo += "<h1 title='" + longTitle + "'>" + title + "</h1>" ;
if ( photoJSON . cameraDate == 1 ) {
photo += "<a><span class='icon-camera' title='Photo Date'></span>" + photoJSON . sysdate + "</a>" ;
} else {
photo += "<a>" + photoJSON . sysdate + "</a>" ;
}
photo += "</div>" ;
if ( photoJSON . star == 1 ) photo += "<a class='badge red icon-star'></a>" ;
if ( ! lychee . publicMode && photoJSON . public == 1 && album . json . public != 1 ) photo += "<a class='badge red icon-share'></a>" ;
photo += "</div>" ;
return photo ;
} ,
imageview : function ( photoJSON , isSmall , visibleControls ) {
if ( ! photoJSON ) return "" ;
var view = "" ;
view += "<div class='arrow_wrapper previous'><a id='previous' class='icon-caret-left'></a></div>" ;
view += "<div class='arrow_wrapper next'><a id='next' class='icon-caret-right'></a></div>" ;
if ( isSmall ) {
if ( visibleControls )
view += "<div id='image' class='small' style='background-image: url(" + photoJSON . url + "); width: " + photoJSON . width + "px; height: " + photoJSON . height + "px; margin-top: -" + parseInt ( photoJSON . height / 2 - 20 ) + "px; margin-left: -" + photoJSON . width / 2 + "px;'></div>" ;
else
view += "<div id='image' class='small' style='background-image: url(" + photoJSON . url + "); width: " + photoJSON . width + "px; height: " + photoJSON . height + "px; margin-top: -" + parseInt ( photoJSON . height / 2 ) + "px; margin-left: -" + photoJSON . width / 2 + "px;'></div>" ;
} else {
if ( visibleControls )
view += "<div id='image' style='background-image: url(" + photoJSON . url + ")'></div>" ;
else
view += "<div id='image' style='background-image: url(" + photoJSON . url + ");' class='full'></div>" ;
}
return view ;
} ,
no _content : function ( typ ) {
var no _content = "" ;
no _content += "<div class='no_content fadeIn'>" ;
no _content += "<a class='icon icon-" + typ + "'></a>" ;
if ( typ === "search" ) no _content += "<p>No results</p>" ;
else if ( typ === "share" ) no _content += "<p>No public albums</p>" ;
else if ( typ === "cog" ) no _content += "<p>No configuration</p>" ;
no _content += "</div>" ;
return no _content ;
} ,
modal : function ( title , text , button , marginTop , closeButton ) {
var modal = "" ,
custom _style = "" ;
if ( marginTop ) custom _style = "style='margin-top: " + marginTop + "px;'" ;
modal += "<div class='message_overlay fadeIn'>" ;
modal += "<div class='message center'" + custom _style + ">" ;
modal += "<h1>" + title + "</h1>" ;
if ( closeButton !== false ) {
modal += "<a class='close icon-remove-sign'></a>" ;
}
modal += "<p>" + text + "</p>" ;
$ . each ( button , function ( index ) {
if ( this [ 0 ] !== "" ) {
if ( index === 0 ) modal += "<a class='button active'>" + this [ 0 ] + "</a>" ;
else modal += "<a class='button'>" + this [ 0 ] + "</a>" ;
}
} ) ;
modal += "</div>" ;
modal += "</div>" ;
return modal ;
} ,
signInModal : function ( ) {
var modal = "" ;
modal += "<div class='message_overlay'>" ;
modal += "<div class='message center'>" ;
modal += "<h1><a class='icon-lock'></a> Sign In</h1>" ;
modal += "<a class='close icon-remove-sign'></a>" ;
modal += "<div class='sign_in'>" ;
modal += "<input id='username' type='text' name='username' value='' placeholder='username'>" ;
modal += "<input id='password' type='password' name='password' value='' placeholder='password'>" ;
modal += "</div>" ;
modal += "<div id='version'>Version " + lychee . version + "<span> – <a target='_blank' href='" + lychee . updateURL + "'>Update available!</a><span></div>" ;
modal += "<a onclick='lychee.login()' class='button active'>Sign in</a>" ;
modal += "</div>" ;
modal += "</div>" ;
return modal ;
} ,
uploadModal : function ( title , files ) {
var modal = "" ;
modal += "<div class='upload_overlay fadeIn'>" ;
modal += "<div class='upload_message center'>" ;
modal += "<h1>" + title + "</h1>" ;
modal += "<a class='close icon-remove-sign'></a>" ;
modal += "<div class='rows'>" ;
for ( var i = 0 ; i < files . length ; i ++ ) {
if ( files [ i ] . name . length > 40 ) files [ i ] . name = files [ i ] . name . substr ( 0 , 17 ) + "..." + files [ i ] . name . substr ( files [ i ] . name . length - 20 , 20 ) ;
modal += "<div class='row'>" ;
modal += "<a class='name'>" + lychee . escapeHTML ( files [ i ] . name ) + "</a>" ;
if ( files [ i ] . supported === true ) modal += "<a class='status'></a>" ;
else modal += "<a class='status error'>Not supported</a>" ;
modal += "<p class='notice'></p>" ;
modal += "</div>" ;
}
modal += "</div>" ;
modal += "</div>" ;
modal += "</div>" ;
return modal ;
} ,
contextMenu : function ( items ) {
var menu = "" ;
menu += "<div class='contextmenu_bg'></div>" ;
menu += "<div class='contextmenu'>" ;
menu += "<table>" ;
menu += "<tbody>" ;
$ . each ( items , function ( index ) {
if ( items [ index ] [ 0 ] === "separator" && items [ index ] [ 1 ] === - 1 ) menu += "<tr class='separator'></tr>" ;
else if ( items [ index ] [ 1 ] === - 1 ) menu += "<tr class='no_hover'><td>" + items [ index ] [ 0 ] + "</td></tr>" ;
else if ( items [ index ] [ 2 ] != undefined ) menu += "<tr><td onclick='" + items [ index ] [ 2 ] + "; window.contextMenu.close();'>" + items [ index ] [ 0 ] + "</td></tr>" ;
else menu += "<tr><td onclick='window.contextMenu.fns[" + items [ index ] [ 1 ] + "](); window.contextMenu.close();'>" + items [ index ] [ 0 ] + "</td></tr>" ;
} ) ;
menu += "</tbody>" ;
menu += "</table>" ;
menu += "</div>" ;
return menu ;
} ,
tags : function ( tags , forView ) {
var html = "" ,
editTagsHTML = ( forView === true || lychee . publicMode ) ? "" : " " + build . editIcon ( "edit_tags" ) ;
if ( tags !== "" ) {
tags = tags . split ( "," ) ;
tags . forEach ( function ( tag , index , array ) {
html += "<a class='tag'>" + tag + "<span class='icon-remove' data-index='" + index + "'></span></a>" ;
} ) ;
html += editTagsHTML ;
} else {
html = "<div class='empty'>No Tags" + editTagsHTML + "</div>" ;
}
return html ;
} ,
infoboxPhoto : function ( photoJSON , forView ) {
if ( ! photoJSON ) return "" ;
var infobox = "" ,
public ,
editTitleHTML ,
editDescriptionHTML ,
infos ;
infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>" ;
infobox += "<div class='wrapper'>" ;
switch ( photoJSON . public ) {
case "0" :
public = "No" ;
break ;
case "1" :
public = "Yes" ;
break ;
case "2" :
public = "Yes (Album)" ;
break ;
default :
public = "-" ;
break ;
}
editTitleHTML = ( forView === true || lychee . publicMode ) ? "" : " " + build . editIcon ( "edit_title" ) ;
editDescriptionHTML = ( forView === true || lychee . publicMode ) ? "" : " " + build . editIcon ( "edit_description" ) ;
infos = [
[ "" , "Basics" ] ,
[ "Title" , photoJSON . title + editTitleHTML ] ,
[ "Uploaded" , photoJSON . sysdate ] ,
[ "Description" , photoJSON . description + editDescriptionHTML ] ,
[ "" , "Image" ] ,
[ "Size" , photoJSON . size ] ,
[ "Format" , photoJSON . type ] ,
[ "Resolution" , photoJSON . width + " x " + photoJSON . height ] ,
[ "Tags" , build . tags ( photoJSON . tags , forView ) ]
] ;
if ( ( photoJSON . takestamp + photoJSON . make + photoJSON . model + photoJSON . shutter + photoJSON . aperture + photoJSON . focal + photoJSON . iso ) != "0" ) {
infos = infos . concat ( [
[ "" , "Camera" ] ,
[ "Captured" , photoJSON . takedate ] ,
[ "Make" , photoJSON . make ] ,
[ "Type/Model" , photoJSON . model ] ,
[ "Shutter Speed" , photoJSON . shutter ] ,
[ "Aperture" , photoJSON . aperture ] ,
[ "Focal Length" , photoJSON . focal ] ,
[ "ISO" , photoJSON . iso ]
] ) ;
}
infos = infos . concat ( [
[ "" , "Share" ] ,
[ "Public" , public ]
] ) ;
$ . each ( infos , function ( index ) {
if ( infos [ index ] [ 1 ] === "" || infos [ index ] [ 1 ] === undefined || infos [ index ] [ 1 ] === null ) infos [ index ] [ 1 ] = "-" ;
switch ( infos [ index ] [ 0 ] ) {
case "" : // Separator
infobox += "</table>" ;
infobox += "<div class='separator'><h1>" + infos [ index ] [ 1 ] + "</h1></div>" ;
infobox += "<table>" ;
break ;
case "Tags" : // Tags
if ( forView !== true && ! lychee . publicMode ) {
infobox += "</table>" ;
infobox += "<div class='separator'><h1>" + infos [ index ] [ 0 ] + "</h1></div>" ;
infobox += "<div id='tags'>" + infos [ index ] [ 1 ] + "</div>" ;
}
break ;
default : // Item
infobox += "<tr>" ;
infobox += "<td>" + infos [ index ] [ 0 ] + "</td>" ;
infobox += "<td class='attr_" + infos [ index ] [ 0 ] . toLowerCase ( ) + "'>" + infos [ index ] [ 1 ] + "</td>" ;
infobox += "</tr>" ;
break ;
}
} ) ;
infobox += "</table>" ;
infobox += "<div class='bumper'></div>" ;
infobox += "</div>" ;
return infobox ;
} ,
infoboxAlbum : function ( albumJSON , forView ) {
if ( ! albumJSON ) return "" ;
var infobox = "" ,
public = "-" ,
password = "-" ,
downloadable = "-" ,
editTitleHTML ,
editDescriptionHTML ,
infos ;
infobox += "<div class='header'><h1>About</h1><a class='icon-remove-sign'></a></div>" ;
infobox += "<div class='wrapper'>" ;
switch ( albumJSON . public ) {
case "0" :
public = "No" ;
break ;
case "1" :
public = "Yes" ;
break ;
}
switch ( albumJSON . password ) {
case false :
password = "No" ;
break ;
case true :
password = "Yes" ;
break ;
}
switch ( albumJSON . downloadable ) {
case "0" :
downloadable = "No" ;
break ;
case "1" :
downloadable = "Yes" ;
break ;
}
editTitleHTML = ( forView === true || lychee . publicMode ) ? "" : " " + build . editIcon ( "edit_title_album" ) ;
editDescriptionHTML = ( forView === true || lychee . publicMode ) ? "" : " " + build . editIcon ( "edit_description_album" ) ;
infos = [
[ "" , "Basics" ] ,
[ "Title" , albumJSON . title + editTitleHTML ] ,
[ "Description" , albumJSON . description + editDescriptionHTML ] ,
[ "" , "Album" ] ,
[ "Created" , albumJSON . sysdate ] ,
[ "Images" , albumJSON . num ] ,
[ "" , "Share" ] ,
[ "Public" , public ] ,
[ "Downloadable" , downloadable ] ,
[ "Password" , password ]
] ;
$ . each ( infos , function ( index ) {
if ( infos [ index ] [ 1 ] === "" || infos [ index ] [ 1 ] === undefined || infos [ index ] [ 1 ] === null ) infos [ index ] [ 1 ] = "-" ;
if ( infos [ index ] [ 0 ] === "" ) {
infobox += "</table>" ;
infobox += "<div class='separator'><h1>" + infos [ index ] [ 1 ] + "</h1></div>" ;
infobox += "<table id='infos'>" ;
} else {
infobox += "<tr>" ;
infobox += "<td>" + infos [ index ] [ 0 ] + "</td>" ;
infobox += "<td class='attr_" + infos [ index ] [ 0 ] . toLowerCase ( ) + "'>" + infos [ index ] [ 1 ] + "</td>" ;
infobox += "</tr>" ;
}
} ) ;
infobox += "</table>" ;
infobox += "<div class='bumper'></div>" ;
infobox += "</div>" ;
return infobox ;
}
} ;
/ * *
* @ name ContextMenu Module
* @ description This module is used for the context menu .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
contextMenu = {
fns : null ,
show : function ( items , mouse _x , mouse _y , orientation ) {
contextMenu . close ( ) ;
$ ( "body" )
. css ( "overflow" , "hidden" )
. append ( build . contextMenu ( items ) ) ;
// Do not leave the screen
if ( ( mouse _x + $ ( ".contextmenu" ) . outerWidth ( true ) ) > $ ( "html" ) . width ( ) ) orientation = "left" ;
if ( ( mouse _y + $ ( ".contextmenu" ) . outerHeight ( true ) ) > $ ( "html" ) . height ( ) ) mouse _y -= ( mouse _y + $ ( ".contextmenu" ) . outerHeight ( true ) - $ ( "html" ) . height ( ) ) ;
if ( mouse _x > $ ( document ) . width ( ) ) mouse _x = $ ( document ) . width ( ) ;
if ( mouse _x < 0 ) mouse _x = 0 ;
if ( mouse _y > $ ( document ) . height ( ) ) mouse _y = $ ( document ) . height ( ) ;
if ( mouse _y < 0 ) mouse _y = 0 ;
if ( orientation === "left" ) mouse _x -= $ ( ".contextmenu" ) . outerWidth ( true ) ;
if ( mouse _x === null ||
mouse _x === undefined ||
isNaN ( mouse _x ) ||
mouse _y === null ||
mouse _y === undefined ||
isNaN ( mouse _y ) ) {
mouse _x = "10px" ;
mouse _y = "10px" ;
}
$ ( ".contextmenu" ) . css ( {
"top" : mouse _y ,
"left" : mouse _x ,
"opacity" : 0.98
} ) ;
} ,
add : function ( e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
upload . notify ( ) ;
contextMenu . fns = [
function ( ) { $ ( "#upload_files" ) . click ( ) } ,
function ( ) { upload . start . url ( ) } ,
function ( ) { upload . start . dropbox ( ) } ,
function ( ) { upload . start . server ( ) } ,
function ( ) { album . add ( ) }
] ;
items = [
[ "<a class='icon-picture'></a> Upload Photo" , 0 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-link'></a> Import from Link" , 1 ] ,
[ "<a class='icon-folder-open'></a> Import from Dropbox" , 2 ] ,
[ "<a class='icon-hdd'></a> Import from Server" , 3 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-folder-close'></a> New Album" , 4 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "left" ) ;
} ,
settings : function ( e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
contextMenu . fns = [
function ( ) { settings . setLogin ( ) } ,
function ( ) { settings . setSorting ( ) } ,
function ( ) { settings . setDropboxKey ( ) } ,
function ( ) { window . open ( lychee . website ) ; } ,
function ( ) { window . open ( "plugins/check/" ) ; } ,
function ( ) { window . open ( "plugins/displaylog/" ) ; } ,
function ( ) { lychee . logout ( ) }
] ;
items = [
[ "<a class='icon-user'></a> Change Login" , 0 ] ,
[ "<a class='icon-sort'></a> Change Sorting" , 1 ] ,
[ "<a class='icon-folder-open'></a> Set Dropbox" , 2 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-info-sign'></a> About Lychee" , 3 ] ,
[ "<a class='icon-dashboard'></a> Diagnostics" , 4 ] ,
[ "<a class='icon-list'></a> Show Log" , 5 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-signout'></a> Sign Out" , 6 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
} ,
album : function ( albumID , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
if ( albumID === "0" || albumID === "f" || albumID === "s" || albumID === "r" ) return false ;
contextMenu . fns = [
function ( ) { album . setTitle ( [ albumID ] ) } ,
function ( ) { album . delete ( [ albumID ] ) }
] ;
items = [
[ "<a class='icon-edit'></a> Rename" , 0 ] ,
[ "<a class='icon-trash'></a> Delete" , 1 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
$ ( ".album[data-id='" + albumID + "']" ) . addClass ( "active" ) ;
} ,
albumMulti : function ( albumIDs , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
multiselect . stopResize ( ) ;
contextMenu . fns = [
function ( ) { album . setTitle ( albumIDs ) } ,
function ( ) { album . delete ( albumIDs ) } ,
] ;
items = [
[ "<a class='icon-edit'></a> Rename All" , 0 ] ,
[ "<a class='icon-trash'></a> Delete All" , 1 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
} ,
photo : function ( photoID , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
contextMenu . fns = [
function ( ) { photo . setStar ( [ photoID ] ) } ,
function ( ) { photo . editTags ( [ photoID ] ) } ,
function ( ) { photo . setTitle ( [ photoID ] ) } ,
function ( ) { contextMenu . move ( [ photoID ] , e , "right" ) } ,
function ( ) { photo . delete ( [ photoID ] ) }
] ;
items = [
[ "<a class='icon-star'></a> Star" , 0 ] ,
[ "<a class='icon-tags'></a> Tags" , 1 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-edit'></a> Rename" , 2 ] ,
[ "<a class='icon-folder-open'></a> Move" , 3 ] ,
[ "<a class='icon-trash'></a> Delete" , 4 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
$ ( ".photo[data-id='" + photoID + "']" ) . addClass ( "active" ) ;
} ,
photoMulti : function ( photoIDs , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
multiselect . stopResize ( ) ;
contextMenu . fns = [
function ( ) { photo . setStar ( photoIDs ) } ,
function ( ) { photo . editTags ( photoIDs ) } ,
function ( ) { photo . setTitle ( photoIDs ) } ,
function ( ) { contextMenu . move ( photoIDs , e , "right" ) } ,
function ( ) { photo . delete ( photoIDs ) }
] ;
items = [
[ "<a class='icon-star'></a> Star All" , 0 ] ,
[ "<a class='icon-tags'></a> Tag All" , 1 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-edit'></a> Rename All" , 2 ] ,
[ "<a class='icon-folder-open'></a> Move All" , 3 ] ,
[ "<a class='icon-trash'></a> Delete All" , 4 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
} ,
photoMore : function ( photoID , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items ;
contextMenu . fns = [
function ( ) { window . open ( photo . getDirectLink ( ) ) } ,
function ( ) { photo . getArchive ( photoID ) }
] ;
items = [ [ "<a class='icon-resize-full'></a> Full Photo" , 0 ] ] ;
if ( ( album . json && album . json . downloadable && album . json . downloadable === "1" && lychee . publicMode ) || ! lychee . publicMode ) items . push ( [ "<a class='icon-circle-arrow-down'></a> Download" , 1 ] ) ;
contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
} ,
move : function ( photoIDs , e , orientation ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY - $ ( document ) . scrollTop ( ) ,
items = [ ] ;
contextMenu . close ( true ) ;
if ( album . getID ( ) !== "0" ) {
items = [
[ "Unsorted" , 0 , "photo.setAlbum([" + photoIDs + "], 0)" ] ,
[ "separator" , - 1 ]
] ;
}
lychee . api ( "getAlbums" , function ( data ) {
if ( data . num === 0 ) {
items = [ [ "New Album" , 0 , "album.add()" ] ] ;
} else {
$ . each ( data . content , function ( index ) {
if ( this . id != album . getID ( ) ) items . push ( [ this . title , 0 , "photo.setAlbum([" + photoIDs + "], " + this . id + ")" ] ) ;
} ) ;
}
if ( ! visible . photo ( ) ) contextMenu . show ( items , mouse _x , mouse _y , "right" ) ;
else contextMenu . show ( items , mouse _x , mouse _y , "left" ) ;
} ) ;
} ,
sharePhoto : function ( photoID , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY ,
items ;
mouse _y -= $ ( document ) . scrollTop ( ) ;
contextMenu . fns = [
function ( ) { photo . setPublic ( photoID ) } ,
function ( ) { photo . share ( photoID , 0 ) } ,
function ( ) { photo . share ( photoID , 1 ) } ,
function ( ) { photo . share ( photoID , 2 ) } ,
function ( ) { photo . share ( photoID , 3 ) } ,
function ( ) { window . open ( photo . getDirectLink ( ) ) }
] ;
link = photo . getViewLink ( photoID ) ;
if ( photo . json . public === "2" ) link = location . href ;
items = [
[ "<input readonly id='link' value='" + link + "'>" , - 1 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-eye-close'></a> Make Private" , 0 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-twitter'></a> Twitter" , 1 ] ,
[ "<a class='icon-facebook'></a> Facebook" , 2 ] ,
[ "<a class='icon-envelope'></a> Mail" , 3 ] ,
[ "<a class='icon-hdd'></a> Dropbox" , 4 ] ,
[ "<a class='icon-link'></a> Direct Link" , 5 ]
] ;
contextMenu . show ( items , mouse _x , mouse _y , "left" ) ;
$ ( ".contextmenu input" ) . focus ( ) . select ( ) ;
} ,
shareAlbum : function ( albumID , e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY ,
items ;
mouse _y -= $ ( document ) . scrollTop ( ) ;
contextMenu . fns = [
function ( ) { album . setPublic ( albumID ) } ,
function ( ) { album . share ( 0 ) } ,
function ( ) { album . share ( 1 ) } ,
function ( ) { album . share ( 2 ) } ,
function ( ) { password . remove ( albumID ) }
] ;
items = [
[ "<input readonly id='link' value='" + location . href + "'>" , - 1 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-eye-close'></a> Make Private" , 0 ] ,
[ "separator" , - 1 ] ,
[ "<a class='icon-twitter'></a> Twitter" , 1 ] ,
[ "<a class='icon-facebook'></a> Facebook" , 2 ] ,
[ "<a class='icon-envelope'></a> Mail" , 3 ] ,
] ;
contextMenu . show ( items , mouse _x , mouse _y , "left" ) ;
$ ( ".contextmenu input" ) . focus ( ) . select ( ) ;
} ,
close : function ( leaveSelection ) {
if ( ! visible . contextMenu ( ) ) return false ;
contextMenu . fns = [ ] ;
$ ( ".contextmenu_bg, .contextmenu" ) . remove ( ) ;
$ ( "body" ) . css ( "overflow" , "auto" ) ;
if ( leaveSelection !== true ) {
$ ( ".photo.active, .album.active" ) . removeClass ( "active" ) ;
if ( visible . multiselect ( ) ) multiselect . close ( ) ;
}
}
} ;
/ * *
* @ name Init Module
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
$ ( document ) . ready ( function ( ) {
/* Event Name */
var event _name = ( mobileBrowser ( ) ) ? "touchend" : "click" ;
/* Disable ContextMenu */
$ ( document ) . bind ( "contextmenu" , function ( e ) { e . preventDefault ( ) } ) ;
/* Tooltips */
if ( ! mobileBrowser ( ) ) $ ( ".tools" ) . tipsy ( { gravity : 'n' , fade : false , delayIn : 0 , opacity : 1 } ) ;
/* Multiselect */
$ ( "#content" ) . on ( "mousedown" , multiselect . show ) ;
$ ( document ) . on ( "mouseup" , multiselect . getSelection ) ;
/* Header */
$ ( "#hostedwith" ) . on ( event _name , function ( ) { window . open ( lychee . website ) } ) ;
$ ( "#button_signin" ) . on ( event _name , lychee . loginDialog ) ;
$ ( "#button_settings" ) . on ( "click" , contextMenu . settings ) ;
$ ( "#button_share" ) . on ( event _name , function ( e ) {
if ( photo . json . public == 1 || photo . json . public == 2 ) contextMenu . sharePhoto ( photo . getID ( ) , e ) ;
else photo . setPublic ( photo . getID ( ) , e ) ;
} ) ;
$ ( "#button_share_album" ) . on ( event _name , function ( e ) {
if ( album . json . public == 1 ) contextMenu . shareAlbum ( album . getID ( ) , e ) ;
else album . setPublic ( album . getID ( ) , e ) ;
} ) ;
$ ( "#button_more" ) . on ( event _name , function ( e ) { contextMenu . photoMore ( photo . getID ( ) , e ) } ) ;
$ ( "#button_trash_album" ) . on ( event _name , function ( ) { album . delete ( [ album . getID ( ) ] ) } ) ;
$ ( "#button_move" ) . on ( event _name , function ( e ) { contextMenu . move ( [ photo . getID ( ) ] , e ) } ) ;
$ ( "#button_trash" ) . on ( event _name , function ( ) { photo . delete ( [ photo . getID ( ) ] ) } ) ;
$ ( "#button_info_album" ) . on ( event _name , function ( ) { view . infobox . show ( ) } ) ;
$ ( "#button_info" ) . on ( event _name , function ( ) { view . infobox . show ( ) } ) ;
$ ( "#button_archive" ) . on ( event _name , function ( ) { album . getArchive ( album . getID ( ) ) } ) ;
$ ( "#button_star" ) . on ( event _name , function ( ) { photo . setStar ( [ photo . getID ( ) ] ) } ) ;
/* Search */
$ ( "#search" ) . on ( "keyup click" , function ( ) { search . find ( $ ( this ) . val ( ) ) } ) ;
/* Clear Search */
$ ( "#clearSearch" ) . on ( event _name , function ( ) {
$ ( "#search" ) . focus ( ) ;
search . reset ( ) ;
} ) ;
/* Back Buttons */
$ ( "#button_back_home" ) . on ( event _name , function ( ) { lychee . goto ( "" ) } ) ;
$ ( "#button_back" ) . on ( event _name , function ( ) { lychee . goto ( album . getID ( ) ) } ) ;
/* Image View */
lychee . imageview
. on ( event _name , ".arrow_wrapper.previous" , photo . previous )
. on ( event _name , ".arrow_wrapper.next" , photo . next ) ;
/* Infobox */
$ ( "#infobox" )
. on ( event _name , ".header a" , function ( ) { view . infobox . hide ( ) } )
. on ( event _name , "#edit_title_album" , function ( ) { album . setTitle ( [ album . getID ( ) ] ) } )
. on ( event _name , "#edit_description_album" , function ( ) { album . setDescription ( album . getID ( ) ) } )
. on ( event _name , "#edit_title" , function ( ) { photo . setTitle ( [ photo . getID ( ) ] ) } )
. on ( event _name , "#edit_description" , function ( ) { photo . setDescription ( photo . getID ( ) ) } )
. on ( event _name , "#edit_tags" , function ( ) { photo . editTags ( [ photo . getID ( ) ] ) } )
. on ( event _name , "#tags .tag span" , function ( ) { photo . deleteTag ( photo . getID ( ) , $ ( this ) . data ( 'index' ) ) } ) ;
/* Keyboard */
Mousetrap
. bind ( 'left' , function ( ) { if ( visible . photo ( ) ) $ ( "#imageview a#previous" ) . click ( ) } )
. bind ( 'right' , function ( ) { if ( visible . photo ( ) ) $ ( "#imageview a#next" ) . click ( ) } )
. bind ( [ 'u' , 'ctrl+u' ] , function ( ) { $ ( "#upload_files" ) . click ( ) } )
. bind ( [ 's' , 'ctrl+s' , 'f' , 'ctrl+f' ] , function ( e ) {
if ( visible . photo ( ) ) {
$ ( "#button_star" ) . click ( ) ;
} else if ( visible . albums ( ) ) {
e . preventDefault ( ) ;
$ ( "#search" ) . focus ( ) ;
}
} )
. bind ( [ 'r' , 'ctrl+r' ] , function ( e ) {
e . preventDefault ( ) ;
if ( visible . album ( ) ) album . setTitle ( album . getID ( ) ) ;
else if ( visible . photo ( ) ) photo . setTitle ( [ photo . getID ( ) ] ) ;
} )
. bind ( [ 'd' , 'ctrl+d' ] , function ( e ) {
e . preventDefault ( ) ;
if ( visible . photo ( ) ) photo . setDescription ( photo . getID ( ) ) ;
else if ( visible . album ( ) ) album . setDescription ( album . getID ( ) ) ;
} )
. bind ( [ 't' , 'ctrl+t' ] , function ( e ) {
if ( visible . photo ( ) ) {
e . preventDefault ( ) ;
photo . editTags ( [ photo . getID ( ) ] ) ;
}
} )
. bind ( [ 'i' , 'ctrl+i' ] , function ( ) {
if ( visible . infobox ( ) ) view . infobox . hide ( ) ;
else if ( visible . infoboxbutton ( ) ) view . infobox . show ( ) ;
} )
. bind ( [ 'command+backspace' , 'ctrl+backspace' ] , function ( ) {
if ( visible . photo ( ) && ! visible . message ( ) ) photo . delete ( [ photo . getID ( ) ] ) ;
else if ( visible . album ( ) && ! visible . message ( ) ) album . delete ( [ album . getID ( ) ] ) ;
} ) ;
Mousetrap . bindGlobal ( 'enter' , function ( ) {
if ( $ ( ".message .button.active" ) . length ) $ ( ".message .button.active" ) . addClass ( "pressed" ) . click ( )
} ) ;
Mousetrap . bindGlobal ( [ 'esc' , 'command+up' ] , function ( e ) {
e . preventDefault ( ) ;
if ( visible . message ( ) && $ ( ".message .close" ) . length > 0 ) modal . close ( ) ;
else if ( visible . contextMenu ( ) ) contextMenu . close ( ) ;
else if ( visible . infobox ( ) ) view . infobox . hide ( ) ;
else if ( visible . photo ( ) ) lychee . goto ( album . getID ( ) ) ;
else if ( visible . album ( ) ) lychee . goto ( "" ) ;
else if ( visible . albums ( ) && $ ( "#search" ) . val ( ) . length !== 0 ) search . reset ( ) ;
} ) ;
if ( mobileBrowser ( ) ) {
$ ( document )
/* Fullscreen on mobile */
. on ( 'touchend' , '#image' , function ( e ) {
if ( swipe . obj === null || ( swipe . offset >= - 5 && swipe . offset <= 5 ) ) {
if ( visible . controls ( ) ) view . header . hide ( e , 0 ) ;
else view . header . show ( ) ;
}
} )
/* Swipe on mobile */
. swipe ( ) . on ( 'swipeStart' , function ( ) { if ( visible . photo ( ) ) swipe . start ( $ ( "#image" ) ) } )
. swipe ( ) . on ( 'swipeMove' , function ( e ) { if ( visible . photo ( ) ) swipe . move ( e . swipe ) } )
. swipe ( ) . on ( 'swipeEnd' , function ( e ) { if ( visible . photo ( ) ) swipe . stop ( e . swipe , photo . previous , photo . next ) } ) ;
}
/* Document */
$ ( document )
/* Login */
. on ( "keyup" , "#password" , function ( ) { if ( $ ( this ) . val ( ) . length > 0 ) $ ( this ) . removeClass ( "error" ) } )
/* Header */
. on ( event _name , "#title.editable" , function ( ) {
if ( visible . photo ( ) ) photo . setTitle ( [ photo . getID ( ) ] ) ;
else album . setTitle ( [ album . getID ( ) ] ) ;
} )
/* Navigation */
. on ( "click" , ".album" , function ( ) { lychee . goto ( $ ( this ) . attr ( "data-id" ) ) } )
. on ( "click" , ".photo" , function ( ) { lychee . goto ( album . getID ( ) + "/" + $ ( this ) . attr ( "data-id" ) ) } )
/* Modal */
. on ( event _name , ".message .close" , modal . close )
. on ( event _name , ".message .button:first" , function ( ) { if ( modal . fns !== null ) modal . fns [ 0 ] ( ) ; if ( ! visible . signin ( ) ) modal . close ( ) } )
. on ( event _name , ".message .button:last" , function ( ) { if ( modal . fns !== null ) modal . fns [ 1 ] ( ) ; if ( ! visible . signin ( ) ) modal . close ( ) } )
/* Add Dialog */
. on ( event _name , ".button_add" , function ( e ) { contextMenu . add ( e ) } )
/* Context Menu */
. on ( "contextmenu" , ".photo" , function ( e ) { contextMenu . photo ( photo . getID ( ) , e ) } )
. on ( "contextmenu" , ".album" , function ( e ) { contextMenu . album ( album . getID ( ) , e ) } )
. on ( event _name , ".contextmenu_bg" , contextMenu . close )
. on ( "contextmenu" , ".contextmenu_bg" , contextMenu . close )
/* Infobox */
. on ( event _name , "#infobox_overlay" , view . infobox . hide )
/* Upload */
. on ( "change" , "#upload_files" , function ( ) { modal . close ( ) ; upload . start . local ( this . files ) } )
. on ( event _name , ".upload_message a.close" , upload . close )
. on ( "dragover" , function ( e ) { e . preventDefault ( ) ; } , false )
. on ( "drop" , function ( e ) {
e . stopPropagation ( ) ;
e . preventDefault ( ) ;
if ( e . originalEvent . dataTransfer . files . length > 0 ) upload . start . local ( e . originalEvent . dataTransfer . files ) ;
else if ( e . originalEvent . dataTransfer . getData ( 'Text' ) . length > 3 ) upload . start . url ( e . originalEvent . dataTransfer . getData ( 'Text' ) ) ;
return true ;
} ) ;
/* Init */
lychee . init ( ) ;
} ) ;
/ * *
* @ name LoadingBar Module
* @ description This module is used to show and hide the loading bar .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
loadingBar = {
status : null ,
show : function ( status , errorText ) {
if ( status === 'error' ) {
// Set status
loadingBar . status = 'error' ;
// Parse text
if ( errorText ) errorText = errorText . replace ( '<br>' , '' ) ;
if ( ! errorText ) errorText = 'Whoops, it looks like something went wrong. Please reload the site and try again!' ;
// Move header down
if ( visible . controls ( ) ) lychee . header . addClass ( 'error' ) ;
// Modify loading
lychee . loadingBar
. removeClass ( 'loading uploading error' )
. addClass ( status )
. html ( '<h1>Error: <span>' + errorText + '</span></h1>' )
. show ( )
. css ( 'height' , '40px' ) ;
// Set timeout
clearTimeout ( lychee . loadingBar . data ( 'timeout' ) ) ;
lychee . loadingBar . data ( 'timeout' , setTimeout ( function ( ) { loadingBar . hide ( true ) } , 3000 ) ) ;
return true ;
}
if ( loadingBar . status === null ) {
// Set status
loadingBar . status = 'loading' ;
// Set timeout
clearTimeout ( lychee . loadingBar . data ( 'timeout' ) ) ;
lychee . loadingBar . data ( 'timeout' , setTimeout ( function ( ) {
// Move header down
if ( visible . controls ( ) ) lychee . header . addClass ( 'loading' ) ;
// Modify loading
lychee . loadingBar
. removeClass ( 'loading uploading error' )
. addClass ( 'loading' )
. show ( ) ;
} , 1000 ) ) ;
return true ;
}
} ,
hide : function ( force ) {
if ( ( loadingBar . status !== 'error' && loadingBar . status !== null ) || force ) {
// Remove status
loadingBar . status = null ;
// Move header up
if ( visible . controls ( ) ) lychee . header . removeClass ( 'error loading' ) ;
// Modify loading
lychee . loadingBar
. html ( '' )
. css ( 'height' , '3px' ) ;
// Set timeout
clearTimeout ( lychee . loadingBar . data ( 'timeout' ) ) ;
setTimeout ( function ( ) { lychee . loadingBar . hide ( ) } , 300 ) ;
}
}
} ;
/ * *
* @ name Lychee Module
* @ description This module provides the basic functions of Lychee .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
var lychee = {
title : "" ,
version : "2.6.1" ,
version _code : "020601" ,
api _path : "php/api.php" ,
update _path : "http://lychee.electerious.com/version/index.php" ,
updateURL : "https://github.com/electerious/Lychee" ,
website : "http://lychee.electerious.com" ,
publicMode : false ,
viewMode : false ,
debugMode : false ,
username : "" ,
checkForUpdates : false ,
sorting : "" ,
location : "" ,
dropbox : false ,
dropboxKey : '' ,
loadingBar : $ ( "#loading" ) ,
header : $ ( "header" ) ,
content : $ ( "#content" ) ,
imageview : $ ( "#imageview" ) ,
infobox : $ ( "#infobox" ) ,
init : function ( ) {
var params ;
params = "init&version=" + lychee . version _code ;
lychee . api ( params , function ( data ) {
if ( data . loggedIn !== true ) {
lychee . setMode ( "public" ) ;
} else {
lychee . username = data . config . username || '' ;
lychee . sorting = data . config . sorting || '' ;
lychee . dropboxKey = data . config . dropboxKey || '' ;
lychee . location = data . config . location || '' ;
}
// No configuration
if ( data === "Warning: No configuration!" ) {
lychee . header . hide ( ) ;
lychee . content . hide ( ) ;
$ ( "body" ) . append ( build . no _content ( "cog" ) ) ;
settings . createConfig ( ) ;
return true ;
}
// No login
if ( data . config . login === false ) {
settings . createLogin ( ) ;
}
lychee . checkForUpdates = data . config . checkForUpdates ;
$ ( window ) . bind ( "popstate" , lychee . load ) ;
lychee . load ( ) ;
} ) ;
} ,
api : function ( params , callback , loading ) {
if ( loading === undefined ) loadingBar . show ( ) ;
$ . ajax ( {
type : "POST" ,
url : lychee . api _path ,
data : "function=" + params ,
dataType : "text" ,
success : function ( data ) {
setTimeout ( function ( ) { loadingBar . hide ( ) } , 100 ) ;
if ( typeof data === "string" && data . substring ( 0 , 7 ) === "Error: " ) {
lychee . error ( data . substring ( 7 , data . length ) , params , data ) ;
upload . close ( true ) ;
return false ;
}
if ( data === "1" ) data = true ;
else if ( data === "" ) data = false ;
if ( typeof data === "string" && data . substring ( 0 , 1 ) === "{" && data . substring ( data . length - 1 , data . length ) === "}" ) data = $ . parseJSON ( data ) ;
if ( lychee . debugMode ) console . log ( data ) ;
callback ( data ) ;
} ,
error : function ( jqXHR , textStatus , errorThrown ) {
lychee . error ( "Server error or API not found." , params , errorThrown ) ;
upload . close ( true ) ;
}
} ) ;
} ,
login : function ( ) {
var user = $ ( "input#username" ) . val ( ) ,
password = md5 ( $ ( "input#password" ) . val ( ) ) ,
params ;
params = "login&user=" + user + "&password=" + password ;
lychee . api ( params , function ( data ) {
if ( data === true ) {
localStorage . setItem ( "lychee_username" , user ) ;
window . location . reload ( ) ;
} else {
$ ( "#password" ) . val ( "" ) . addClass ( "error" ) . focus ( ) ;
$ ( ".message .button.active" ) . removeClass ( "pressed" ) ;
}
} ) ;
} ,
loginDialog : function ( ) {
var local _username ;
$ ( "body" ) . append ( build . signInModal ( ) ) ;
$ ( "#username" ) . focus ( ) ;
if ( localStorage ) {
local _username = localStorage . getItem ( "lychee_username" ) ;
if ( local _username !== null ) {
if ( local _username . length > 0 ) $ ( "#username" ) . val ( local _username ) ;
$ ( "#password" ) . focus ( ) ;
}
}
if ( lychee . checkForUpdates === "1" ) lychee . getUpdate ( ) ;
} ,
logout : function ( ) {
lychee . api ( "logout" , function ( data ) {
window . location . reload ( ) ;
} ) ;
} ,
goto : function ( url ) {
if ( url === undefined ) url = "#" ;
else url = "#" + url ;
history . pushState ( null , null , url ) ;
lychee . load ( ) ;
} ,
load : function ( ) {
var albumID = "" ,
photoID = "" ,
hash = document . location . hash . replace ( "#" , "" ) . split ( "/" ) ;
$ ( ".no_content" ) . remove ( ) ;
contextMenu . close ( ) ;
multiselect . close ( ) ;
if ( hash [ 0 ] !== undefined ) albumID = hash [ 0 ] ;
if ( hash [ 1 ] !== undefined ) photoID = hash [ 1 ] ;
if ( albumID && photoID ) {
// Trash data
//albums.json = null;
photo . json = null ;
// Show Photo
if ( lychee . content . html ( ) === "" || ( $ ( "#search" ) . length && $ ( "#search" ) . val ( ) . length !== 0 ) ) {
lychee . content . hide ( ) ;
album . load ( albumID , true ) ;
}
photo . load ( photoID , albumID ) ;
photo . preloadNext ( photoID , albumID ) ;
} else if ( albumID ) {
// Trash data
//albums.json = null;
photo . json = null ;
// Show Album
if ( visible . photo ( ) ) view . photo . hide ( ) ;
if ( album . json && albumID == album . json . id ) view . album . title ( ) ;
else album . load ( albumID ) ;
} else {
// Trash data
//albums.json = null;
album . json = null ;
photo . json = null ;
search . code = "" ;
// Show Albums
if ( visible . album ( ) ) view . album . hide ( ) ;
if ( visible . photo ( ) ) view . photo . hide ( ) ;
albums . load ( ) ;
}
} ,
getUpdate : function ( ) {
$ . ajax ( {
url : lychee . update _path ,
success : function ( data ) { if ( parseInt ( data ) > parseInt ( lychee . version _code ) ) $ ( "#version span" ) . show ( ) ; }
} ) ;
} ,
setTitle : function ( title , editable ) {
if ( lychee . title === "" ) lychee . title = document . title ;
if ( title === "Albums" ) document . title = lychee . title ;
else document . title = lychee . title + " - " + title ;
if ( editable ) $ ( "#title" ) . addClass ( "editable" ) ;
else $ ( "#title" ) . removeClass ( "editable" ) ;
$ ( "#title" ) . html ( title ) ;
} ,
setMode : function ( mode ) {
$ ( "#button_settings, #button_settings, #button_search, #search, #button_trash_album, #button_share_album, .button_add, .button_divider" ) . remove ( ) ;
$ ( "#button_trash, #button_move, #button_share, #button_star" ) . remove ( ) ;
$ ( document )
. on ( "mouseenter" , "#title.editable" , function ( ) { $ ( this ) . removeClass ( "editable" ) } )
. off ( "click" , "#title.editable" )
. off ( "touchend" , "#title.editable" )
. off ( "contextmenu" , ".photo" )
. off ( "contextmenu" , ".album" )
. off ( "drop" ) ;
Mousetrap
. unbind ( [ 'u' , 'ctrl+u' ] )
. unbind ( [ 's' , 'ctrl+s' ] )
. unbind ( [ 'r' , 'ctrl+r' ] )
. unbind ( [ 'd' , 'ctrl+d' ] )
. unbind ( [ 't' , 'ctrl+t' ] )
. unbind ( [ 'command+backspace' , 'ctrl+backspace' ] ) ;
if ( mode === "public" ) {
$ ( "header #button_signin, header #hostedwith" ) . show ( ) ;
lychee . publicMode = true ;
} else if ( mode === "view" ) {
Mousetrap . unbind ( 'esc' ) ;
$ ( "#button_back, a#next, a#previous" ) . remove ( ) ;
$ ( ".no_content" ) . remove ( ) ;
lychee . publicMode = true ;
lychee . viewMode = true ;
}
} ,
animate : function ( obj , animation ) {
var animations = [
[ "fadeIn" , "fadeOut" ] ,
[ "contentZoomIn" , "contentZoomOut" ]
] ;
if ( ! obj . jQuery ) obj = $ ( obj ) ;
for ( var i = 0 ; i < animations . length ; i ++ ) {
for ( var x = 0 ; x < animations [ i ] . length ; x ++ ) {
if ( animations [ i ] [ x ] == animation ) {
obj . removeClass ( animations [ i ] [ 0 ] + " " + animations [ i ] [ 1 ] ) . addClass ( animation ) ;
return true ;
}
}
}
return false ;
} ,
escapeHTML : function ( s ) {
return s . replace ( /&/g , '&' )
. replace ( /"/g , '"' )
. replace ( /</g , '<' )
. replace ( />/g , '>' ) ;
} ,
loadDropbox : function ( callback ) {
if ( ! lychee . dropbox && lychee . dropboxKey ) {
loadingBar . show ( ) ;
var g = document . createElement ( "script" ) ,
s = document . getElementsByTagName ( "script" ) [ 0 ] ;
g . src = "https://www.dropbox.com/static/api/1/dropins.js" ;
g . id = "dropboxjs" ;
g . type = "text/javascript" ;
g . async = "true" ;
g . setAttribute ( "data-app-key" , lychee . dropboxKey ) ;
g . onload = g . onreadystatechange = function ( ) {
var rs = this . readyState ;
if ( rs && rs !== "complete" && rs !== "loaded" ) return ;
lychee . dropbox = true ;
loadingBar . hide ( ) ;
callback ( ) ;
} ;
s . parentNode . insertBefore ( g , s ) ;
} else if ( lychee . dropbox && lychee . dropboxKey ) {
callback ( ) ;
} else {
settings . setDropboxKey ( callback ) ;
}
} ,
removeHTML : function ( html ) {
var tmp = document . createElement ( "DIV" ) ;
tmp . innerHTML = html ;
return tmp . textContent || tmp . innerText ;
} ,
error : function ( errorThrown , params , data ) {
console . error ( {
description : errorThrown ,
params : params ,
response : data
} ) ;
loadingBar . show ( "error" , errorThrown ) ;
}
} ;
/ * *
* @ name Modal Module
* @ description Build , show and hide a modal .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
modal = {
fns : null ,
show : function ( title , text , buttons , marginTop , closeButton ) {
if ( ! buttons ) {
buttons = [
[ "" , function ( ) { } ] ,
[ "" , function ( ) { } ]
] ;
}
modal . fns = [ buttons [ 0 ] [ 1 ] , buttons [ 1 ] [ 1 ] ] ;
$ ( "body" ) . append ( build . modal ( title , text , buttons , marginTop , closeButton ) ) ;
$ ( ".message input:first-child" ) . focus ( ) . select ( ) ;
} ,
close : function ( ) {
modal . fns = null ;
$ ( ".message_overlay" ) . removeClass ( "fadeIn" ) . css ( "opacity" , 0 ) ;
setTimeout ( function ( ) { $ ( ".message_overlay" ) . remove ( ) } , 300 ) ;
}
} ;
/ * *
* @ name Multiselect Module
* @ description Select multiple albums or photos .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
multiselect = {
position : {
top : null ,
right : null ,
bottom : null ,
left : null
} ,
show : function ( e ) {
if ( mobileBrowser ( ) ) return false ;
if ( lychee . publicMode ) return false ;
if ( visible . search ( ) ) return false ;
if ( $ ( '.album:hover, .photo:hover' ) . length !== 0 ) return false ;
if ( visible . multiselect ( ) ) $ ( '#multiselect' ) . remove ( ) ;
multiselect . position . top = e . pageY ;
multiselect . position . right = - 1 * ( e . pageX - $ ( document ) . width ( ) ) ;
multiselect . position . bottom = - 1 * ( multiselect . position . top - $ ( window ) . height ( ) ) ;
multiselect . position . left = e . pageX ;
$ ( 'body' ) . append ( build . multiselect ( multiselect . position . top , multiselect . position . left ) ) ;
$ ( document ) . on ( 'mousemove' , multiselect . resize ) ;
} ,
resize : function ( e ) {
var mouse _x = e . pageX ,
mouse _y = e . pageY ,
newHeight ,
newWidth ;
if ( multiselect . position . top === null ||
multiselect . position . right === null ||
multiselect . position . bottom === null ||
multiselect . position . left === null ) return false ;
if ( mouse _y >= multiselect . position . top ) {
// Do not leave the screen
newHeight = e . pageY - multiselect . position . top ;
if ( ( multiselect . position . top + newHeight ) >= $ ( document ) . height ( ) )
newHeight -= ( multiselect . position . top + newHeight ) - $ ( document ) . height ( ) + 2 ;
$ ( '#multiselect' ) . css ( {
top : multiselect . position . top ,
bottom : 'inherit' ,
height : newHeight
} ) ;
} else {
$ ( '#multiselect' ) . css ( {
top : 'inherit' ,
bottom : multiselect . position . bottom ,
height : multiselect . position . top - e . pageY
} ) ;
}
if ( mouse _x >= multiselect . position . left ) {
// Do not leave the screen
newWidth = e . pageX - multiselect . position . left ;
if ( ( multiselect . position . left + newWidth ) >= $ ( document ) . width ( ) )
newWidth -= ( multiselect . position . left + newWidth ) - $ ( document ) . width ( ) + 2 ;
$ ( '#multiselect' ) . css ( {
right : 'inherit' ,
left : multiselect . position . left ,
width : newWidth
} ) ;
} else {
$ ( '#multiselect' ) . css ( {
right : multiselect . position . right ,
left : 'inherit' ,
width : multiselect . position . left - e . pageX
} ) ;
}
} ,
stopResize : function ( ) {
$ ( document ) . off ( 'mousemove' ) ;
} ,
getSize : function ( ) {
if ( ! visible . multiselect ( ) ) return false ;
return {
top : $ ( '#multiselect' ) . offset ( ) . top ,
left : $ ( '#multiselect' ) . offset ( ) . left ,
width : parseInt ( $ ( '#multiselect' ) . css ( 'width' ) . replace ( 'px' , '' ) ) ,
height : parseInt ( $ ( '#multiselect' ) . css ( 'height' ) . replace ( 'px' , '' ) )
} ;
} ,
getSelection : function ( e ) {
var tolerance = 150 ,
id ,
ids = [ ] ,
offset ,
size = multiselect . getSize ( ) ;
if ( visible . contextMenu ( ) ) return false ;
if ( ! visible . multiselect ( ) ) return false ;
$ ( '.photo, .album' ) . each ( function ( ) {
offset = $ ( this ) . offset ( ) ;
if ( offset . top >= ( size . top - tolerance ) &&
offset . left >= ( size . left - tolerance ) &&
( offset . top + 206 ) <= ( size . top + size . height + tolerance ) &&
( offset . left + 206 ) <= ( size . left + size . width + tolerance ) ) {
id = $ ( this ) . data ( 'id' ) ;
if ( id !== '0' && id !== 0 && id !== 'f' && id !== 's' && id !== 'r' && id !== null & id !== undefined ) {
ids . push ( id ) ;
$ ( this ) . addClass ( 'active' ) ;
}
}
} ) ;
if ( ids . length !== 0 && visible . album ( ) ) contextMenu . photoMulti ( ids , e ) ;
else if ( ids . length !== 0 && visible . albums ( ) ) contextMenu . albumMulti ( ids , e ) ;
else multiselect . close ( ) ;
} ,
close : function ( ) {
multiselect . stopResize ( ) ;
multiselect . position . top = null ;
multiselect . position . right = null ;
multiselect . position . bottom = null ;
multiselect . position . left = null ;
lychee . animate ( '#multiselect' , 'fadeOut' ) ;
setTimeout ( function ( ) {
$ ( '#multiselect' ) . remove ( ) ;
} , 300 ) ;
}
} ;
/ * *
* @ name Password Module
* @ description Controls the access to password - protected albums and photos .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
password = {
value : "" ,
get : function ( albumID , callback ) {
var passwd = $ ( ".message input.text" ) . val ( ) ,
params ;
if ( ! lychee . publicMode ) callback ( ) ;
else if ( album . json && album . json . password == false ) callback ( ) ;
else if ( albums . json && albums . json . content [ albumID ] . password == false ) callback ( ) ;
else if ( ! albums . json && ! album . json ) {
// Continue without password
album . json = { password : true } ;
callback ( "" ) ;
} else if ( passwd == undefined ) {
// Request password
password . getDialog ( albumID , callback ) ;
} else {
// Check password
params = "checkAlbumAccess&albumID=" + albumID + "&password=" + md5 ( passwd ) ;
lychee . api ( params , function ( data ) {
if ( data === true ) {
password . value = md5 ( passwd ) ;
callback ( ) ;
} else {
lychee . goto ( "" ) ;
loadingBar . show ( "error" , "Access denied. Wrong password!" ) ;
}
} ) ;
}
} ,
getDialog : function ( albumID , callback ) {
var buttons ;
buttons = [
[ "Enter" , function ( ) { password . get ( albumID , callback ) } ] ,
[ "Cancel" , lychee . goto ]
] ;
modal . show ( "<a class='icon-lock'></a> Enter Password" , "This album is protected by a password. Enter the password below to view the photos of this album: <input class='text' type='password' placeholder='password' value=''>" , buttons , - 110 , false ) ;
}
} ;
/ * *
* @ name Photo Module
* @ description Takes care of every action a photo can handle and execute .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
cache = null ;
photo = {
json : null ,
getID : function ( ) {
var id ;
if ( photo . json ) id = photo . json . id ;
else id = $ ( ".photo:hover, .photo.active" ) . attr ( "data-id" ) ;
if ( id ) return id ;
else return false ;
} ,
load : function ( photoID , albumID ) {
var params ,
checkPasswd ;
params = "getPhoto&photoID=" + photoID + "&albumID=" + albumID + "&password=" + password . value ;
lychee . api ( params , function ( data ) {
if ( data === "Warning: Wrong password!" ) {
checkPasswd = function ( ) {
if ( password . value !== "" ) photo . load ( photoID , albumID ) ;
else setTimeout ( checkPasswd , 250 ) ;
} ;
checkPasswd ( ) ;
return false ;
}
photo . json = data ;
if ( ! visible . photo ( ) ) view . photo . show ( ) ;
view . photo . init ( ) ;
lychee . imageview . show ( ) ;
setTimeout ( function ( ) { lychee . content . show ( ) } , 300 ) ;
} ) ;
} ,
//preload the next photo for better response time
preloadNext : function ( photoID ) {
if ( album . json &&
album . json . content &&
album . json . content [ photoID ] &&
album . json . content [ photoID ] . nextPhoto != "" ) {
var nextPhoto = album . json . content [ photoID ] . nextPhoto ;
var url = album . json . content [ nextPhoto ] . url ;
cache = new Image ( ) ;
cache . src = url ;
}
} ,
parse : function ( ) {
if ( ! photo . json . title ) photo . json . title = "Untitled" ;
} ,
previous : function ( animate ) {
var delay = 0 ;
if ( photo . getID ( ) !== false &&
album . json &&
album . json . content [ photo . getID ( ) ] &&
album . json . content [ photo . getID ( ) ] . previousPhoto !== "" ) {
if ( animate === true ) {
delay = 200 ;
$ ( "#image" ) . css ( {
WebkitTransform : 'translateX(100%)' ,
MozTransform : 'translateX(100%)' ,
transform : 'translateX(100%)' ,
opacity : 0
} ) ;
}
setTimeout ( function ( ) {
if ( photo . getID ( ) === false ) return false ;
lychee . goto ( album . getID ( ) + "/" + album . json . content [ photo . getID ( ) ] . previousPhoto )
} , delay ) ;
}
} ,
next : function ( animate ) {
var delay = 0 ;
if ( photo . getID ( ) !== false &&
album . json &&
album . json . content [ photo . getID ( ) ] &&
album . json . content [ photo . getID ( ) ] . nextPhoto !== "" ) {
if ( animate === true ) {
delay = 200 ;
$ ( "#image" ) . css ( {
WebkitTransform : 'translateX(-100%)' ,
MozTransform : 'translateX(-100%)' ,
transform : 'translateX(-100%)' ,
opacity : 0
} ) ;
}
setTimeout ( function ( ) {
if ( photo . getID ( ) === false ) return false ;
lychee . goto ( album . getID ( ) + "/" + album . json . content [ photo . getID ( ) ] . nextPhoto ) ;
} , delay ) ;
}
} ,
delete : function ( photoIDs ) {
var params ,
buttons ,
photoTitle ;
if ( ! photoIDs ) return false ;
if ( photoIDs instanceof Array === false ) photoIDs = [ photoIDs ] ;
if ( photoIDs . length === 1 ) {
// Get title if only one photo is selected
if ( visible . photo ( ) ) photoTitle = photo . json . title ;
else photoTitle = album . json . content [ photoIDs ] . title ;
if ( photoTitle === "" ) photoTitle = "Untitled" ;
}
buttons = [
[ "" , function ( ) {
photoIDs . forEach ( function ( id , index , array ) {
// Change reference for the next and previous photo
if ( album . json . content [ id ] . nextPhoto !== "" || album . json . content [ id ] . previousPhoto !== "" ) {
nextPhoto = album . json . content [ id ] . nextPhoto ;
previousPhoto = album . json . content [ id ] . previousPhoto ;
album . json . content [ previousPhoto ] . nextPhoto = nextPhoto ;
album . json . content [ nextPhoto ] . previousPhoto = previousPhoto ;
}
album . json . content [ id ] = null ;
view . album . content . delete ( id ) ;
} ) ;
// Only when search is not active
if ( ! visible . albums ( ) ) lychee . goto ( album . getID ( ) ) ;
params = "deletePhoto&photoIDs=" + photoIDs ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "" , function ( ) { } ]
] ;
if ( photoIDs . length === 1 ) {
buttons [ 0 ] [ 0 ] = "Delete Photo" ;
buttons [ 1 ] [ 0 ] = "Keep Photo" ;
modal . show ( "Delete Photo" , "Are you sure you want to delete the photo '" + photoTitle + "'?<br>This action can't be undone!" , buttons ) ;
} else {
buttons [ 0 ] [ 0 ] = "Delete Photos" ;
buttons [ 1 ] [ 0 ] = "Keep Photos" ;
modal . show ( "Delete Photos" , "Are you sure you want to delete all " + photoIDs . length + " selected photo?<br>This action can't be undone!" , buttons ) ;
}
} ,
setTitle : function ( photoIDs ) {
var oldTitle = "" ,
newTitle ,
params ,
buttons ;
if ( ! photoIDs ) return false ;
if ( photoIDs instanceof Array === false ) photoIDs = [ photoIDs ] ;
if ( photoIDs . length === 1 ) {
// Get old title if only one photo is selected
if ( photo . json ) oldTitle = photo . json . title ;
else if ( album . json ) oldTitle = album . json . content [ photoIDs ] . title ;
oldTitle = oldTitle . replace ( "'" , "'" ) ;
}
buttons = [
[ "Set Title" , function ( ) {
// Get input
newTitle = $ ( ".message input.text" ) . val ( ) ;
// Remove html from input
newTitle = lychee . removeHTML ( newTitle ) ;
if ( visible . photo ( ) ) {
photo . json . title = ( newTitle === "" ) ? "Untitled" : newTitle ;
view . photo . title ( ) ;
}
photoIDs . forEach ( function ( id , index , array ) {
album . json . content [ id ] . title = newTitle ;
view . album . content . title ( id ) ;
} ) ;
params = "setPhotoTitle&photoIDs=" + photoIDs + "&title=" + escape ( encodeURI ( newTitle ) ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
if ( photoIDs . length === 1 ) modal . show ( "Set Title" , "Enter a new title for this photo: <input class='text' type='text' maxlength='30' placeholder='Title' value='" + oldTitle + "'>" , buttons ) ;
else modal . show ( "Set Titles" , "Enter a title for all " + photoIDs . length + " selected photos: <input class='text' type='text' maxlength='30' placeholder='Title' value=''>" , buttons ) ;
} ,
setAlbum : function ( photoIDs , albumID ) {
var params ,
nextPhoto ,
previousPhoto ;
if ( ! photoIDs ) return false ;
if ( visible . photo ) lychee . goto ( album . getID ( ) ) ;
if ( photoIDs instanceof Array === false ) photoIDs = [ photoIDs ] ;
photoIDs . forEach ( function ( id , index , array ) {
// Change reference for the next and previous photo
if ( album . json . content [ id ] . nextPhoto !== "" || album . json . content [ id ] . previousPhoto !== "" ) {
nextPhoto = album . json . content [ id ] . nextPhoto ;
previousPhoto = album . json . content [ id ] . previousPhoto ;
album . json . content [ previousPhoto ] . nextPhoto = nextPhoto ;
album . json . content [ nextPhoto ] . previousPhoto = previousPhoto ;
}
album . json . content [ id ] = null ;
view . album . content . delete ( id ) ;
} ) ;
params = "setPhotoAlbum&photoIDs=" + photoIDs + "&albumID=" + albumID ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ,
setStar : function ( photoIDs ) {
var params ;
if ( ! photoIDs ) return false ;
if ( visible . photo ( ) ) {
photo . json . star = ( photo . json . star == 0 ) ? 1 : 0 ;
view . photo . star ( ) ;
}
photoIDs . forEach ( function ( id , index , array ) {
album . json . content [ id ] . star = ( album . json . content [ id ] . star == 0 ) ? 1 : 0 ;
view . album . content . star ( id ) ;
} ) ;
params = "setPhotoStar&photoIDs=" + photoIDs ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
albums . refresh ( ) ;
} ,
setPublic : function ( photoID , e ) {
var params ;
if ( photo . json . public == 2 ) {
modal . show ( "Public Album" , "This photo is located in a public album. To make this photo private or public, edit the visibility of the associated album." , [ [ "Show Album" , function ( ) { lychee . goto ( photo . json . original _album ) } ] , [ "Close" , function ( ) { } ] ] ) ;
return false ;
}
albums . refresh ( ) ;
if ( visible . photo ( ) ) {
photo . json . public = ( photo . json . public == 0 ) ? 1 : 0 ;
view . photo . public ( ) ;
if ( photo . json . public == 1 ) contextMenu . sharePhoto ( photoID , e ) ;
}
album . json . content [ photoID ] . public = ( album . json . content [ photoID ] . public == 0 ) ? 1 : 0 ;
view . album . content . public ( photoID ) ;
params = "setPhotoPublic&photoID=" + photoID ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ,
setDescription : function ( photoID ) {
var oldDescription = photo . json . description . replace ( "'" , "'" ) ,
description ,
params ,
buttons ;
buttons = [
[ "Set Description" , function ( ) {
// Get input
description = $ ( ".message input.text" ) . val ( ) ;
// Remove html from input
description = lychee . removeHTML ( description ) ;
if ( visible . photo ( ) ) {
photo . json . description = description ;
view . photo . description ( ) ;
}
params = "setPhotoDescription&photoID=" + photoID + "&description=" + escape ( encodeURI ( description ) ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Set Description" , "Enter a description for this photo: <input class='text' type='text' maxlength='800' placeholder='Description' value='" + oldDescription + "'>" , buttons ) ;
} ,
editTags : function ( photoIDs ) {
var oldTags = "" ,
tags = "" ,
buttons ;
if ( ! photoIDs ) return false ;
if ( photoIDs instanceof Array === false ) photoIDs = [ photoIDs ] ;
// Get tags
if ( visible . photo ( ) ) oldTags = photo . json . tags ;
if ( visible . album ( ) && photoIDs . length === 1 ) oldTags = album . json . content [ photoIDs ] . tags ;
if ( visible . album ( ) && photoIDs . length > 1 ) {
var same = true ;
photoIDs . forEach ( function ( id , index , array ) {
if ( album . json . content [ id ] . tags === album . json . content [ photoIDs [ 0 ] ] . tags && same === true ) same = true ;
else same = false ;
} ) ;
if ( same ) oldTags = album . json . content [ photoIDs [ 0 ] ] . tags ;
}
// Improve tags
oldTags = oldTags . replace ( /,/g , ', ' ) ;
buttons = [
[ "Set Tags" , function ( ) {
tags = $ ( ".message input.text" ) . val ( ) ;
photo . setTags ( photoIDs , tags ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
if ( photoIDs . length === 1 ) modal . show ( "Set Tags" , "Enter your tags for this photo. You can add multiple tags by separating them with a comma: <input class='text' type='text' maxlength='800' placeholder='Tags' value='" + oldTags + "'>" , buttons ) ;
else modal . show ( "Set Tags" , "Enter your tags for all " + photoIDs . length + " selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: <input class='text' type='text' maxlength='800' placeholder='Tags' value='" + oldTags + "'>" , buttons ) ;
} ,
setTags : function ( photoIDs , tags ) {
var params ;
if ( ! photoIDs ) return false ;
if ( photoIDs instanceof Array === false ) photoIDs = [ photoIDs ] ;
// Parse tags
tags = tags . replace ( /(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g , ',' ) ;
tags = tags . replace ( /,$|^,|(\ ){0,}$/g , '' ) ;
// Remove html from input
tags = lychee . removeHTML ( tags ) ;
if ( visible . photo ( ) ) {
photo . json . tags = tags ;
view . photo . tags ( ) ;
}
photoIDs . forEach ( function ( id , index , array ) {
album . json . content [ id ] . tags = tags ;
} ) ;
params = "setPhotoTags&photoIDs=" + photoIDs + "&tags=" + tags ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ,
deleteTag : function ( photoID , index ) {
var tags ;
// Remove
tags = photo . json . tags . split ( ',' ) ;
tags . splice ( index , 1 ) ;
// Save
photo . json . tags = tags . toString ( ) ;
photo . setTags ( [ photoID ] , photo . json . tags ) ;
} ,
share : function ( photoID , service ) {
var link = "" ,
url = photo . getViewLink ( photoID ) ,
filename = "unknown" ;
switch ( service ) {
case 0 :
link = "https://twitter.com/share?url=" + encodeURI ( url ) ;
break ;
case 1 :
link = "http://www.facebook.com/sharer.php?u=" + encodeURI ( url ) + "&t=" + encodeURI ( photo . json . title ) ;
break ;
case 2 :
link = "mailto:?subject=" + encodeURI ( photo . json . title ) + "&body=" + encodeURI ( url ) ;
break ;
case 3 :
lychee . loadDropbox ( function ( ) {
filename = photo . json . title + "." + photo . getDirectLink ( ) . split ( '.' ) . pop ( ) ;
Dropbox . save ( photo . getDirectLink ( ) , filename ) ;
} ) ;
break ;
default :
link = "" ;
break ;
}
if ( link . length > 5 ) location . href = link ;
} ,
isSmall : function ( ) {
var size = {
width : false ,
height : false
} ;
if ( photo . json . width < $ ( window ) . width ( ) - 60 ) size . width = true ;
if ( photo . json . height < $ ( window ) . height ( ) - 100 ) size . height = true ;
if ( size . width && size . height ) return true ;
else return false ;
} ,
getArchive : function ( photoID ) {
var link ;
if ( location . href . indexOf ( "index.html" ) > 0 ) link = location . href . replace ( location . hash , "" ) . replace ( "index.html" , "php/api.php?function=getPhotoArchive&photoID=" + photoID ) ;
else link = location . href . replace ( location . hash , "" ) + "php/api.php?function=getPhotoArchive&photoID=" + photoID ;
if ( lychee . publicMode ) link += "&password=" + password . value ;
location . href = link ;
} ,
getDirectLink : function ( ) {
return $ ( "#imageview #image" ) . css ( "background-image" ) . replace ( /"/g , "" ) . replace ( /url\(|\)$/ig , "" ) ;
} ,
getViewLink : function ( photoID ) {
if ( location . href . indexOf ( "index.html" ) > 0 ) return location . href . replace ( "index.html" + location . hash , "view.php?p=" + photoID ) ;
else return location . href . replace ( location . hash , "view.php?p=" + photoID ) ;
}
} ;
/ * *
* @ name Search Module
* @ description Searches through your photos and albums .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
search = {
code : null ,
find : function ( term ) {
var params ,
albumsData = "" ,
photosData = "" ,
code ;
clearTimeout ( $ ( window ) . data ( "timeout" ) ) ;
$ ( window ) . data ( "timeout" , setTimeout ( function ( ) {
if ( $ ( "#search" ) . val ( ) . length !== 0 ) {
params = "search&term=" + term ;
lychee . api ( params , function ( data ) {
if ( data && data . albums ) {
albums . json = { content : data . albums } ;
$ . each ( albums . json . content , function ( ) {
albums . parse ( this ) ;
albumsData += build . album ( this ) ;
} ) ;
}
if ( data && data . photos ) {
album . json = { content : data . photos } ;
$ . each ( album . json . content , function ( ) {
photosData += build . photo ( this ) ;
} ) ;
}
if ( albumsData === "" && photosData === "" ) code = "error" ;
else if ( albumsData === "" ) code = build . divider ( "Photos" ) + photosData ;
else if ( photosData === "" ) code = build . divider ( "Albums" ) + albumsData ;
else code = build . divider ( "Photos" ) + photosData + build . divider ( "Albums" ) + albumsData ;
if ( search . code !== md5 ( code ) ) {
$ ( ".no_content" ) . remove ( ) ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomOut" ) ;
lychee . animate ( ".divider" , "fadeOut" ) ;
search . code = md5 ( code ) ;
setTimeout ( function ( ) {
if ( code === "error" ) $ ( "body" ) . append ( build . no _content ( "search" ) ) ;
else {
lychee . content . html ( code ) ;
lychee . animate ( ".album:nth-child(-n+50), .photo:nth-child(-n+50)" , "contentZoomIn" ) ;
$ ( "img[data-type!='svg']" ) . retina ( ) ;
}
} , 300 ) ;
}
} ) ;
} else search . reset ( ) ;
} , 250 ) ) ;
} ,
reset : function ( ) {
$ ( "#search" ) . val ( "" ) ;
$ ( ".no_content" ) . remove ( ) ;
if ( search . code !== "" ) {
// Trash data
albums . json = null ;
album . json = null ;
photo . json = null ;
search . code = "" ;
lychee . animate ( ".divider" , "fadeOut" ) ;
albums . load ( ) ;
}
}
} ;
/ * *
* @ name Settings Module
* @ description Lets you change settings .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
var settings = {
createConfig : function ( ) {
var dbName ,
dbUser ,
dbPassword ,
dbHost ,
buttons ,
params ;
buttons = [
[ "Connect" , function ( ) {
dbHost = $ ( ".message input.text#dbHost" ) . val ( ) ;
dbUser = $ ( ".message input.text#dbUser" ) . val ( ) ;
dbPassword = $ ( ".message input.text#dbPassword" ) . val ( ) ;
dbName = $ ( ".message input.text#dbName" ) . val ( ) ;
if ( dbHost . length < 1 ) dbHost = "localhost" ;
if ( dbName . length < 1 ) dbName = "lychee" ;
params = "dbCreateConfig&dbName=" + escape ( dbName ) + "&dbUser=" + escape ( dbUser ) + "&dbPassword=" + escape ( dbPassword ) + "&dbHost=" + escape ( dbHost ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) {
// Configuration failed
setTimeout ( function ( ) {
// Connection failed
if ( data . indexOf ( "Warning: Connection failed!" ) !== - 1 ) {
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createConfig , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Connection Failed" , "Unable to connect to host database because access was denied. Double-check your host, username and password and ensure that access from your current location is permitted." , buttons , null , false ) ;
return false ;
}
// Creation failed
if ( data . indexOf ( "Warning: Creation failed!" ) !== - 1 ) {
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createConfig , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Creation Failed" , "Unable to create the database. Double-check your host, username and password and ensure that the specified user has the rights to modify and add content to the database." , buttons , null , false ) ;
return false ;
}
// Could not create file
if ( data . indexOf ( "Warning: Could not create file!" ) !== - 1 ) {
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createConfig , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Saving Failed" , "Unable to save this configuration. Permission denied in <b>'data/'</b>. Please set the read, write and execute rights for others in <b>'data/'</b> and <b>'uploads/'</b>. Take a look the readme for more information." , buttons , null , false ) ;
return false ;
}
// Something went wrong
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createConfig , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Configuration Failed" , "Something unexpected happened. Please try again and check your installation and server. Take a look the readme for more information." , buttons , null , false ) ;
return false ;
} , 400 ) ;
} else {
// Configuration successful
window . location . reload ( ) ;
}
} ) ;
} ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Configuration" , "Enter your database connection details below: <input id='dbHost' class='text less' type='text' placeholder='Host (optional)' value=''><input id='dbUser' class='text less' type='text' placeholder='Username' value=''><input id='dbPassword' class='text more' type='password' placeholder='Password' value=''><br>Lychee will create its own database. If required, you can enter the name of an existing database instead:<input id='dbName' class='text more' type='text' placeholder='Database (optional)' value=''>" , buttons , - 215 , false ) ;
} ,
createLogin : function ( ) {
var username ,
password ,
params ,
buttons ;
buttons = [
[ "Create Login" , function ( ) {
username = $ ( ".message input.text#username" ) . val ( ) ;
password = $ ( ".message input.text#password" ) . val ( ) ;
if ( username . length < 1 || password . length < 1 ) {
setTimeout ( function ( ) {
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createLogin , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Wrong Input" , "The username or password you entered is not long enough. Please try again with another username and password!" , buttons , null , false ) ;
return false ;
} , 400 ) ;
} else {
params = "setLogin&username=" + escape ( username ) + "&password=" + md5 ( password ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) {
setTimeout ( function ( ) {
buttons = [
[ "Retry" , function ( ) { setTimeout ( settings . createLogin , 400 ) } ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Creation Failed" , "Unable to save login. Please try again with another username and password!" , buttons , null , false ) ;
return false ;
} , 400 ) ;
}
} ) ;
}
} ] ,
[ "" , function ( ) { } ]
] ;
modal . show ( "Create Login" , "Enter a username and password for your installation: <input id='username' class='text less' type='text' placeholder='New Username' value=''><input id='password' class='text' type='password' placeholder='New Password' value=''>" , buttons , - 122 , false ) ;
} ,
setLogin : function ( ) {
var old _password ,
username ,
password ,
params ,
buttons ;
buttons = [
[ "Change Login" , function ( ) {
old _password = $ ( ".message input.text#old_password" ) . val ( ) ;
username = $ ( ".message input.text#username" ) . val ( ) ;
password = $ ( ".message input.text#password" ) . val ( ) ;
if ( old _password . length < 1 ) {
loadingBar . show ( "error" , "Your old password was entered incorrectly. Please try again!" ) ;
return false ;
}
if ( username . length < 1 ) {
loadingBar . show ( "error" , "Your new username was entered incorrectly. Please try again!" ) ;
return false ;
}
if ( password . length < 1 ) {
loadingBar . show ( "error" , "Your new password was entered incorrectly. Please try again!" ) ;
return false ;
}
params = "setLogin&oldPassword=" + md5 ( old _password ) + "&username=" + escape ( username ) + "&password=" + md5 ( password ) ;
lychee . api ( params , function ( data ) {
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Change Login" , "Enter your current password: <input id='old_password' class='text more' type='password' placeholder='Current Password' value=''><br>Your username and password will be changed to the following: <input id='username' class='text less' type='text' placeholder='New Username' value=''><input id='password' class='text' type='password' placeholder='New Password' value=''>" , buttons , - 171 ) ;
} ,
setSorting : function ( ) {
var buttons ,
sorting ,
params ;
buttons = [
[ "Change Sorting" , function ( ) {
sorting [ 0 ] = $ ( "select#settings_type" ) . val ( ) ;
sorting [ 1 ] = $ ( "select#settings_order" ) . val ( ) ;
params = "setSorting&type=" + sorting [ 0 ] + "&order=" + sorting [ 1 ] ;
lychee . api ( params , function ( data ) {
if ( data === true ) {
lychee . sorting = "ORDER BY " + sorting [ 0 ] + " " + sorting [ 1 ] ;
lychee . load ( ) ;
} else lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Change Sorting" ,
" Sort photos by \
< select id = 'settings_type' > \
< option value = 'id' > Upload Time < / o p t i o n > \
< option value = 'takestamp' > Take Date < / o p t i o n > \
< option value = 'title' > Title < / o p t i o n > \
< option value = 'description' > Description < / o p t i o n > \
< option value = 'public' > Public < / o p t i o n > \
< option value = 'star' > Star < / o p t i o n > \
< option value = 'type' > Photo Format < / o p t i o n > \
< / s e l e c t > \
in an \
< select id = 'settings_order' > \
< option value = 'ASC' > Ascending < / o p t i o n > \
< option value = 'DESC' > Descending < / o p t i o n > \
< / s e l e c t > \
order . \
" , buttons ) ;
if ( lychee . sorting !== "" ) {
sorting = lychee . sorting . replace ( "ORDER BY " , "" ) . split ( " " ) ;
$ ( "select#settings_type" ) . val ( sorting [ 0 ] ) ;
$ ( "select#settings_order" ) . val ( sorting [ 1 ] ) ;
}
} ,
setDropboxKey : function ( callback ) {
var buttons ,
params ,
key ;
buttons = [
[ "Set Key" , function ( ) {
key = $ ( ".message input.text#key" ) . val ( ) ;
params = "setDropboxKey&key=" + key ;
lychee . api ( params , function ( data ) {
if ( data === true ) {
lychee . dropboxKey = key ;
if ( callback ) lychee . loadDropbox ( callback ) ;
} else lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Set Dropbox Key" , "In order to import photos from your Dropbox, you need a valid drop-ins app key from <a href='https://www.dropbox.com/developers/apps/create'>their website</a>. Generate yourself a personal key and enter it below: <input id='key' class='text' type='text' placeholder='Dropbox API Key' value='" + lychee . dropboxKey + "'>" , buttons ) ;
}
} ;
/ * *
* @ name Swipe Module
* @ description Swipes and moves an object .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
swipe = {
obj : null ,
tolerance : 150 ,
offset : 0 ,
start : function ( obj , tolerance ) {
if ( obj ) swipe . obj = obj ;
if ( tolerance ) swipe . tolerance = tolerance ;
return true ;
} ,
move : function ( e ) {
if ( swipe . obj === null ) return false ;
swipe . offset = - 1 * e . x ;
swipe . obj . css ( {
WebkitTransform : 'translateX(' + swipe . offset + 'px)' ,
MozTransform : 'translateX(' + swipe . offset + 'px)' ,
transform : 'translateX(' + swipe . offset + 'px)'
} ) ;
} ,
stop : function ( e , left , right ) {
if ( e . x <= - swipe . tolerance ) left ( true ) ;
else if ( e . x >= swipe . tolerance ) right ( true ) ;
else if ( swipe . obj !== null ) {
swipe . obj . css ( {
WebkitTransform : 'translateX(0px)' ,
MozTransform : 'translateX(0px)' ,
transform : 'translateX(0px)'
} ) ;
}
swipe . obj = null ;
swipe . offset = 0 ;
}
} ;
/ * *
* @ name Album Module
* @ description Takes care of every action an album can handle and execute .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
upload = {
show : function ( title , files , callback ) {
upload . close ( true ) ;
$ ( "body" ) . append ( build . uploadModal ( title , files ) ) ;
if ( callback != null && callback != undefined ) callback ( ) ;
} ,
notify : function ( title , text ) {
var popup ;
if ( ! text || text === "" ) text = "You can now manage your new photo(s)." ;
if ( ! window . webkitNotifications ) return false ;
if ( window . webkitNotifications . checkPermission ( ) !== 0 ) window . webkitNotifications . requestPermission ( ) ;
if ( window . webkitNotifications . checkPermission ( ) === 0 && title ) {
popup = window . webkitNotifications . createNotification ( "" , title , text ) ;
popup . show ( ) ;
}
} ,
start : {
local : function ( files ) {
var albumID = album . getID ( ) ,
error = false ,
process = function ( files , file ) {
var formData = new FormData ( ) ,
xhr = new XMLHttpRequest ( ) ,
pre _progress = 0 ,
progress ,
finish = function ( ) {
window . onbeforeunload = null ;
$ ( "#upload_files" ) . val ( "" ) ;
if ( error === false ) {
// Success
upload . close ( ) ;
upload . notify ( "Upload complete" ) ;
} else {
// Error
$ ( ".upload_message a.close" ) . show ( ) ;
upload . notify ( "Upload complete" , "Failed to upload one or more photos." ) ;
}
if ( album . getID ( ) === false ) lychee . goto ( "0" ) ;
else album . load ( albumID ) ;
} ;
// Check if file is supported
if ( file . supported === false ) {
// Skip file
if ( file . next !== null ) process ( files , file . next ) ;
else {
// Look for supported files
// If zero files are supported, hide the upload after a delay
var hasSupportedFiles = false ;
for ( var i = 0 ; i < files . length ; i ++ ) {
if ( files [ i ] . supported === true ) {
hasSupportedFiles = true ;
break ;
}
}
if ( hasSupportedFiles === false ) setTimeout ( finish , 2000 ) ;
}
return false ;
}
formData . append ( "function" , "upload" ) ;
formData . append ( "albumID" , albumID ) ;
formData . append ( 0 , file ) ;
xhr . open ( "POST" , lychee . api _path ) ;
xhr . onload = function ( ) {
var wait = false ;
file . ready = true ;
// Set status
if ( xhr . status === 200 && xhr . responseText === "1" ) {
// Success
$ ( ".upload_message .rows .row:nth-child(" + ( file . num + 1 ) + ") .status" )
. html ( "Finished" )
. addClass ( "success" ) ;
} else {
// Error
$ ( ".upload_message .rows .row:nth-child(" + ( file . num + 1 ) + ") .status" )
. html ( "Error" )
. addClass ( "error" ) ;
$ ( ".upload_message .rows .row:nth-child(" + ( file . num + 1 ) + ") p.notice" )
. html ( "Server returned an unknown response. Please take a look at the console of your browser for further details." )
. show ( ) ;
// Set global error
error = true ;
// Throw error
lychee . error ( "Upload failed. Server returned the status code " + xhr . status + "!" , xhr , xhr . responseText ) ;
}
// Check if there are file which are not finished
for ( var i = 0 ; i < files . length ; i ++ ) {
if ( files [ i ] . ready === false ) {
wait = true ;
break ;
}
}
// Finish upload when all files are finished
if ( wait === false ) finish ( ) ;
} ;
xhr . upload . onprogress = function ( e ) {
if ( e . lengthComputable ) {
// Calculate progress
progress = ( e . loaded / e . total * 100 | 0 ) ;
// Set progress when progress has changed
if ( progress > pre _progress ) {
$ ( ".upload_message .rows .row:nth-child(" + ( file . num + 1 ) + ") .status" ) . html ( progress + "%" ) ;
pre _progress = progress ;
}
if ( progress >= 100 ) {
// Scroll to the uploading file
var scrollPos = 0 ;
if ( ( file . num + 1 ) > 4 ) scrollPos = ( file . num + 1 - 4 ) * 40
$ ( ".upload_message .rows" ) . scrollTop ( scrollPos ) ;
// Set status to processing
$ ( ".upload_message .rows .row:nth-child(" + ( file . num + 1 ) + ") .status" ) . html ( "Processing" ) ;
// Upload next file
if ( file . next !== null ) process ( files , file . next ) ;
}
}
} ;
xhr . send ( formData ) ;
} ;
if ( files . length <= 0 ) return false ;
if ( albumID === false || visible . albums ( ) === true ) albumID = 0 ;
for ( var i = 0 ; i < files . length ; i ++ ) {
files [ i ] . num = i ;
files [ i ] . ready = false ;
files [ i ] . supported = true ;
if ( i < files . length - 1 ) files [ i ] . next = files [ i + 1 ] ;
else files [ i ] . next = null ;
// Check if file is supported
if ( files [ i ] . type !== "image/jpeg" && files [ i ] . type !== "image/jpg" && files [ i ] . type !== "image/png" && files [ i ] . type !== "image/gif" ) {
files [ i ] . ready = true ;
files [ i ] . supported = false ;
}
}
window . onbeforeunload = function ( ) { return "Lychee is currently uploading!" ; } ;
upload . show ( "Uploading" , files ) ;
// Upload first file
process ( files , files [ 0 ] ) ;
} ,
url : function ( ) {
var albumID = album . getID ( ) ,
params ,
extension ,
buttons ,
link ,
files = [ ] ;
if ( albumID === false ) albumID = 0 ;
buttons = [
[ "Import" , function ( ) {
link = $ ( ".message input.text" ) . val ( ) ;
if ( link && link . length > 3 ) {
extension = link . split ( '.' ) . pop ( ) ;
if ( extension !== "jpeg" && extension !== "jpg" && extension !== "png" && extension !== "gif" && extension !== "webp" ) {
loadingBar . show ( "error" , "The file format of this link is not supported." ) ;
return false ;
}
files [ 0 ] = {
name : link ,
supported : true
}
upload . show ( "Importing URL" , files , function ( ) {
$ ( ".upload_message .rows .row .status" ) . html ( "Importing" ) ;
} ) ;
params = "importUrl&url=" + escape ( encodeURI ( link ) ) + "&albumID=" + albumID ;
lychee . api ( params , function ( data ) {
upload . close ( ) ;
upload . notify ( "Import complete" ) ;
if ( album . getID ( ) === false ) lychee . goto ( "0" ) ;
else album . load ( albumID ) ;
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} else loadingBar . show ( "error" , "Link to short or too long. Please try another one!" ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Import from Link" , "Please enter the direct link to a photo to import it: <input class='text' type='text' placeholder='http://' value='http://'>" , buttons ) ;
} ,
server : function ( ) {
var albumID = album . getID ( ) ,
params ,
buttons ,
files = [ ] ,
path ;
if ( albumID === false ) albumID = 0 ;
buttons = [
[ "Import" , function ( ) {
path = $ ( ".message input.text" ) . val ( ) ;
files [ 0 ] = {
name : path ,
supported : true
} ;
upload . show ( "Importing from server" , files , function ( ) {
$ ( ".upload_message .rows .row .status" ) . html ( "Importing" ) ;
} ) ;
params = "importServer&albumID=" + albumID + "&path=" + escape ( encodeURI ( path ) ) ;
lychee . api ( params , function ( data ) {
upload . close ( ) ;
upload . notify ( "Import complete" ) ;
if ( data === "Notice: Import only contains albums!" ) {
if ( visible . albums ( ) ) lychee . load ( ) ;
else lychee . goto ( "" ) ;
}
else if ( album . getID ( ) === false ) lychee . goto ( "0" ) ;
else album . load ( albumID ) ;
if ( data === "Notice: Import only contains albums!" ) return true ;
else if ( data === "Warning: Folder empty!" ) lychee . error ( "Folder empty. No photos imported!" , params , data ) ;
else if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
} ] ,
[ "Cancel" , function ( ) { } ]
] ;
modal . show ( "Import from Server" , "This action will import all photos, folders and sub-folders which are located in the following directory. The <b>original files will be deleted</b> after the import when possible. <input class='text' type='text' maxlength='100' placeholder='Absolute path to directory' value='" + lychee . location + "uploads/import/'>" , buttons ) ;
} ,
dropbox : function ( ) {
var albumID = album . getID ( ) ,
params ,
links = "" ;
if ( albumID === false ) albumID = 0 ;
lychee . loadDropbox ( function ( ) {
Dropbox . choose ( {
linkType : "direct" ,
multiselect : true ,
success : function ( files ) {
for ( var i = 0 ; i < files . length ; i ++ ) {
links += files [ i ] . link + "," ;
files [ i ] = {
name : files [ i ] . link ,
supported : true
} ;
}
// Remove last comma
links = links . substr ( 0 , links . length - 1 ) ;
upload . show ( "Importing from Dropbox" , files , function ( ) {
$ ( ".upload_message .rows .row .status" ) . html ( "Importing" ) ;
} ) ;
params = "importUrl&url=" + escape ( links ) + "&albumID=" + albumID ;
lychee . api ( params , function ( data ) {
upload . close ( ) ;
upload . notify ( "Import complete" ) ;
if ( album . getID ( ) === false ) lychee . goto ( "0" ) ;
else album . load ( albumID ) ;
if ( data !== true ) lychee . error ( null , params , data ) ;
} ) ;
}
} ) ;
} ) ;
}
} ,
close : function ( force ) {
if ( force === true ) {
$ ( ".upload_overlay" ) . remove ( ) ;
} else {
$ ( ".upload_overlay" ) . removeClass ( "fadeIn" ) . css ( "opacity" , 0 ) ;
setTimeout ( function ( ) { $ ( ".upload_overlay" ) . remove ( ) } , 300 ) ;
}
}
} ;
/ * *
* @ name UI View
* @ description Responsible to reflect data changes to the UI .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
view = {
header : {
show : function ( ) {
var newMargin = - 1 * ( $ ( "#imageview #image" ) . height ( ) / 2 ) + 20 ;
clearTimeout ( $ ( window ) . data ( "timeout" ) ) ;
lychee . imageview . removeClass ( "full" ) ;
lychee . header . removeClass ( "hidden" ) ;
lychee . loadingBar . css ( "opacity" , 1 ) ;
if ( $ ( "#imageview #image.small" ) . length > 0 ) $ ( "#imageview #image" ) . css ( 'margin-top' , newMargin ) ;
else $ ( "#imageview #image" ) . removeClass ( 'full' ) ;
} ,
hide : function ( e , delay ) {
var newMargin = - 1 * ( $ ( "#imageview #image" ) . height ( ) / 2 ) ;
if ( delay === undefined ) delay = 500 ;
if ( visible . photo ( ) && ! visible . infobox ( ) && ! visible . contextMenu ( ) && ! visible . message ( ) ) {
clearTimeout ( $ ( window ) . data ( "timeout" ) ) ;
$ ( window ) . data ( "timeout" , setTimeout ( function ( ) {
lychee . imageview . addClass ( "full" ) ;
lychee . header . addClass ( "hidden" ) ;
lychee . loadingBar . css ( "opacity" , 0 ) ;
if ( $ ( "#imageview #image.small" ) . length > 0 ) $ ( "#imageview #image" ) . css ( 'margin-top' , newMargin ) ;
else $ ( "#imageview #image" ) . addClass ( 'full' ) ;
} , delay ) ) ;
}
} ,
mode : function ( mode ) {
var albumID = album . getID ( ) ;
switch ( mode ) {
case "albums" :
lychee . header . removeClass ( "view" ) ;
$ ( "#tools_album, #tools_photo" ) . hide ( ) ;
$ ( "#tools_albums" ) . show ( ) ;
break ;
case "album" :
lychee . header . removeClass ( "view" ) ;
$ ( "#tools_albums, #tools_photo" ) . hide ( ) ;
$ ( "#tools_album" ) . show ( ) ;
album . json . content === false ? $ ( "#button_archive" ) . hide ( ) : $ ( "#button_archive" ) . show ( ) ;
if ( lychee . publicMode && album . json . downloadable === "0" ) $ ( "#button_archive" ) . hide ( ) ;
if ( albumID === "s" || albumID === "f" || albumID === "r" ) {
$ ( "#button_info_album, #button_trash_album, #button_share_album" ) . hide ( ) ;
} else if ( albumID === "0" ) {
$ ( "#button_info_album, #button_share_album" ) . hide ( ) ;
$ ( "#button_trash_album" ) . show ( ) ;
} else {
$ ( "#button_info_album, #button_trash_album, #button_share_album" ) . show ( ) ;
}
break ;
case "photo" :
lychee . header . addClass ( "view" ) ;
$ ( "#tools_albums, #tools_album" ) . hide ( ) ;
$ ( "#tools_photo" ) . show ( ) ;
break ;
}
}
} ,
infobox : {
show : function ( ) {
if ( ! visible . infobox ( ) ) $ ( "body" ) . append ( "<div id='infobox_overlay' class='fadeIn'></div>" ) ;
lychee . infobox . addClass ( "active" ) ;
} ,
hide : function ( ) {
lychee . animate ( "#infobox_overlay" , "fadeOut" ) ;
setTimeout ( function ( ) { $ ( "#infobox_overlay" ) . remove ( ) } , 300 ) ;
lychee . infobox . removeClass ( "active" ) ;
}
} ,
albums : {
init : function ( ) {
view . albums . title ( ) ;
view . albums . content . init ( ) ;
} ,
title : function ( ) {
lychee . setTitle ( "Albums" , false ) ;
} ,
content : {
init : function ( ) {
var smartData = "" ,
albumsData = "" ;
/* Smart Albums */
albums . parse ( albums . json . unsortedAlbum ) ;
albums . parse ( albums . json . publicAlbum ) ;
albums . parse ( albums . json . starredAlbum ) ;
albums . parse ( albums . json . recentAlbum ) ;
if ( ! lychee . publicMode ) smartData = build . divider ( "Smart Albums" ) + build . album ( albums . json . unsortedAlbum ) + build . album ( albums . json . starredAlbum ) + build . album ( albums . json . publicAlbum ) + build . album ( albums . json . recentAlbum ) ;
/* Albums */
if ( albums . json . content ) {
$ . each ( albums . json . content , function ( ) {
albums . parse ( this ) ;
//display albums in reverse order
albumsData = build . album ( this ) + albumsData ;
} ) ;
if ( ! lychee . publicMode ) albumsData = build . divider ( "Albums" ) + albumsData ;
}
if ( smartData === "" && albumsData === "" ) {
lychee . content . html ( '' ) ;
$ ( "body" ) . append ( build . no _content ( "share" ) ) ;
} else {
lychee . content . html ( smartData + albumsData ) ;
}
$ ( "img[data-type!='nonretina']" ) . retina ( ) ;
} ,
title : function ( albumID ) {
var prefix = "" ,
longTitle = "" ,
title = albums . json . content [ albumID ] . title ;
if ( albums . json . content [ albumID ] . password ) prefix = "<span class='icon-lock'></span> " ;
if ( title != null && title . length > 18 ) {
longTitle = title ;
title = title . substr ( 0 , 18 ) + "..." ;
}
$ ( ".album[data-id='" + albumID + "'] .overlay h1" )
. html ( prefix + title )
. attr ( "title" , longTitle ) ;
} ,
delete : function ( albumID ) {
$ ( ".album[data-id='" + albumID + "']" ) . css ( "opacity" , 0 ) . animate ( {
width : 0 ,
marginLeft : 0
} , 300 , function ( ) {
$ ( this ) . remove ( ) ;
if ( albums . json . num <= 0 ) lychee . animate ( ".divider:last-of-type" , "fadeOut" ) ;
} ) ;
}
}
} ,
album : {
init : function ( ) {
album . parse ( ) ;
view . album . infobox ( ) ;
view . album . title ( ) ;
view . album . public ( ) ;
view . album . content . init ( ) ;
album . json . init = 1 ;
} ,
hide : function ( ) {
view . infobox . hide ( ) ;
} ,
title : function ( ) {
if ( ( visible . album ( ) || ! album . json . init ) && ! visible . photo ( ) ) {
switch ( album . getID ( ) ) {
case "f" :
lychee . setTitle ( "Starred" , false ) ;
break ;
case "s" :
lychee . setTitle ( "Public" , false ) ;
break ;
case "r" :
lychee . setTitle ( "Recent" , false ) ;
break ;
case "0" :
lychee . setTitle ( "Unsorted" , false ) ;
break ;
default :
if ( album . json . init ) $ ( "#infobox .attr_title" ) . html ( album . json . title + " " + build . editIcon ( "edit_title_album" ) ) ;
lychee . setTitle ( album . json . title , true ) ;
break ;
}
}
} ,
content : {
init : function ( ) {
var photosData = "" ;
$ . each ( album . json . content , function ( ) {
photosData += build . photo ( this ) ;
} ) ;
lychee . content . html ( photosData ) ;
$ ( "img[data-type!='svg']" ) . retina ( ) ;
//scroll to top
$ ( "html, body" ) . animate ( { scrollTop : 0 } , "slow" ) ;
} ,
title : function ( photoID ) {
var longTitle = "" ,
title = album . json . content [ photoID ] . title ;
if ( title != null && title . length > 18 ) {
longTitle = title ;
title = title . substr ( 0 , 18 ) + "..." ;
}
$ ( ".photo[data-id='" + photoID + "'] .overlay h1" )
. html ( title )
. attr ( "title" , longTitle ) ;
} ,
star : function ( photoID ) {
$ ( ".photo[data-id='" + photoID + "'] .icon-star" ) . remove ( ) ;
if ( album . json . content [ photoID ] . star == 1 ) $ ( ".photo[data-id='" + photoID + "']" ) . append ( "<a class='badge red icon-star'></a>" ) ;
} ,
public : function ( photoID ) {
$ ( ".photo[data-id='" + photoID + "'] .icon-share" ) . remove ( ) ;
if ( album . json . content [ photoID ] . public == 1 ) $ ( ".photo[data-id='" + photoID + "']" ) . append ( "<a class='badge red icon-share'></a>" ) ;
} ,
delete : function ( photoID ) {
$ ( ".photo[data-id='" + photoID + "']" ) . css ( "opacity" , 0 ) . animate ( {
width : 0 ,
marginLeft : 0
} , 300 , function ( ) {
$ ( this ) . remove ( ) ;
// Only when search is not active
if ( ! visible . albums ( ) ) {
album . json . num -- ;
view . album . num ( ) ;
view . album . title ( ) ;
}
} ) ;
}
} ,
description : function ( ) {
$ ( "#infobox .attr_description" ) . html ( album . json . description + " " + build . editIcon ( "edit_description_album" ) ) ;
} ,
num : function ( ) {
$ ( "#infobox .attr_images" ) . html ( album . json . num ) ;
} ,
public : function ( ) {
if ( album . json . public == 1 ) {
$ ( "#button_share_album a" ) . addClass ( "active" ) ;
$ ( "#button_share_album" ) . attr ( "title" , "Share Album" ) ;
$ ( ".photo .icon-share" ) . remove ( ) ;
if ( album . json . init ) $ ( "#infobox .attr_visibility" ) . html ( "Public" ) ;
} else {
$ ( "#button_share_album a" ) . removeClass ( "active" ) ;
$ ( "#button_share_album" ) . attr ( "title" , "Make Public" ) ;
if ( album . json . init ) $ ( "#infobox .attr_visibility" ) . html ( "Private" ) ;
}
} ,
password : function ( ) {
if ( album . json . password == 1 ) $ ( "#infobox .attr_password" ) . html ( "Yes" ) ;
else $ ( "#infobox .attr_password" ) . html ( "No" ) ;
} ,
infobox : function ( ) {
if ( ( visible . album ( ) || ! album . json . init ) && ! visible . photo ( ) ) lychee . infobox . html ( build . infoboxAlbum ( album . json ) ) . show ( ) ;
}
} ,
photo : {
init : function ( ) {
photo . parse ( ) ;
view . photo . infobox ( ) ;
view . photo . title ( ) ;
view . photo . star ( ) ;
view . photo . public ( ) ;
view . photo . photo ( ) ;
photo . json . init = 1 ;
} ,
show : function ( ) {
// Change header
lychee . content . addClass ( "view" ) ;
view . header . mode ( "photo" ) ;
// Make body not scrollable
$ ( "body" ) . css ( "overflow" , "hidden" ) ;
// Fullscreen
$ ( document )
. bind ( "mouseenter" , view . header . show )
. bind ( "mouseleave" , view . header . hide ) ;
lychee . animate ( lychee . imageview , "fadeIn" ) ;
} ,
hide : function ( ) {
view . header . show ( ) ;
if ( visible . infobox ) view . infobox . hide ( ) ;
lychee . content . removeClass ( "view" ) ;
view . header . mode ( "album" ) ;
// Make body scrollable
$ ( "body" ) . css ( "overflow" , "auto" ) ;
// Disable Fullscreen
$ ( document )
. unbind ( "mouseenter" )
. unbind ( "mouseleave" ) ;
// Hide Photo
lychee . animate ( lychee . imageview , "fadeOut" ) ;
setTimeout ( function ( ) {
lychee . imageview . hide ( ) ;
view . album . infobox ( ) ;
} , 300 ) ;
} ,
title : function ( ) {
if ( photo . json . init ) $ ( "#infobox .attr_title" ) . html ( photo . json . title + " " + build . editIcon ( "edit_title" ) ) ;
lychee . setTitle ( photo . json . title , true ) ;
} ,
description : function ( ) {
if ( photo . json . init ) $ ( "#infobox .attr_description" ) . html ( photo . json . description + " " + build . editIcon ( "edit_description" ) ) ;
} ,
star : function ( ) {
$ ( "#button_star a" ) . removeClass ( "icon-star-empty icon-star" ) ;
if ( photo . json . star == 1 ) {
// Starred
$ ( "#button_star a" ) . addClass ( "icon-star" ) ;
$ ( "#button_star" ) . attr ( "title" , "Unstar Photo" ) ;
} else {
// Unstarred
$ ( "#button_star a" ) . addClass ( "icon-star-empty" ) ;
$ ( "#button_star" ) . attr ( "title" , "Star Photo" ) ;
}
} ,
public : function ( ) {
if ( photo . json . public == 1 || photo . json . public == 2 ) {
// Photo public
$ ( "#button_share a" ) . addClass ( "active" ) ;
$ ( "#button_share" ) . attr ( "title" , "Share Photo" ) ;
if ( photo . json . init ) $ ( "#infobox .attr_visibility" ) . html ( "Public" ) ;
} else {
// Photo private
$ ( "#button_share a" ) . removeClass ( "active" ) ;
$ ( "#button_share" ) . attr ( "title" , "Make Public" ) ;
if ( photo . json . init ) $ ( "#infobox .attr_visibility" ) . html ( "Private" ) ;
}
} ,
tags : function ( ) {
$ ( "#infobox #tags" ) . html ( build . tags ( photo . json . tags ) ) ;
} ,
photo : function ( ) {
lychee . imageview . html ( build . imageview ( photo . json , photo . isSmall ( ) , visible . controls ( ) ) ) ;
if ( ( album . json && album . json . content && album . json . content [ photo . getID ( ) ] && album . json . content [ photo . getID ( ) ] . nextPhoto === "" ) || lychee . viewMode ) $ ( "a#next" ) . hide ( ) ;
if ( ( album . json && album . json . content && album . json . content [ photo . getID ( ) ] && album . json . content [ photo . getID ( ) ] . previousPhoto === "" ) || lychee . viewMode ) $ ( "a#previous" ) . hide ( ) ;
} ,
infobox : function ( ) {
lychee . infobox . html ( build . infoboxPhoto ( photo . json ) ) . show ( ) ;
}
}
} ;
/ * *
* @ name Visible Module
* @ description This module is used to check if elements are visible or not .
* @ author Tobias Reich
* @ copyright 2014 by Tobias Reich
* /
visible = {
albums : function ( ) {
if ( $ ( '#tools_albums' ) . css ( 'display' ) === 'block' ) return true ;
else return false ;
} ,
album : function ( ) {
if ( $ ( '#tools_album' ) . css ( 'display' ) === 'block' ) return true ;
else return false ;
} ,
photo : function ( ) {
if ( $ ( '#imageview.fadeIn' ) . length > 0 ) return true ;
else return false ;
} ,
search : function ( ) {
if ( search . code !== null && search . code !== '' ) return true ;
else return false ;
} ,
infobox : function ( ) {
if ( $ ( '#infobox.active' ) . length > 0 ) return true ;
else return false ;
} ,
infoboxbutton : function ( ) {
if ( visible . albums ( ) ) return false ;
if ( visible . photo ( ) ) return true ;
if ( visible . album ( ) && $ ( '#button_info_album:visible' ) . length > 0 ) return true ;
else return false ;
} ,
controls : function ( ) {
if ( lychee . loadingBar . css ( 'opacity' ) < 1 ) return false ;
else return true ;
} ,
message : function ( ) {
if ( $ ( '.message' ) . length > 0 ) return true ;
else return false ;
} ,
signin : function ( ) {
if ( $ ( '.message .sign_in' ) . length > 0 ) return true ;
else return false ;
} ,
contextMenu : function ( ) {
if ( $ ( '.contextmenu' ) . length > 0 ) return true ;
else return false ;
} ,
multiselect : function ( ) {
if ( $ ( '#multiselect' ) . length > 0 ) return true ;
else return false ;
}
} ;