2012-09-21 04:46:45 +00:00
/ *
* View framework for JamKazam .
*
* Processes proprietary attributes in markup to convert a set of HTML elements
* into the JamKazam screen layout . This module is only responsible for size
* and position . All other visual aspects should be elsewhere .
*
* See the layout - example . html file for a simple working example .
* /
2014-02-21 22:02:51 +00:00
( function ( context , $ ) {
"use strict" ;
context . JK = context . JK || { } ;
// Static function to hide the 'curtain' which hides the underlying
// stuff until we can get it laid out. Called from both the main
// client as well as the landing page.
context . JK . hideCurtain = function ( duration ) {
context . setTimeout ( function ( ) {
$ ( '.curtain' ) . fadeOut ( 2 * duration ) ;
} , duration ) ;
} ;
context . JK . Layout = function ( ) {
// privates
var logger = context . JK . logger ;
2014-04-09 17:25:52 +00:00
var NOT _HANDLED = "not handled" ;
2014-02-21 22:02:51 +00:00
var me = null ; // Reference to this instance for context sanity.
var opts = {
headerHeight : 75 ,
sidebarWidth : 300 ,
notifyHeight : 150 ,
notifyGutter : 10 ,
collapsedSidebar : 30 ,
panelHeaderHeight : 36 ,
2014-04-08 11:43:26 +00:00
alwaysOpenPanelHeaderHeight : 78 , // for the search bar
2014-02-21 22:02:51 +00:00
gutter : 60 , // Margin around the whole UI
screenMargin : 0 , // Margin around screens (not headers/sidebar)
gridOuterMargin : 6 , // Outer margin on Grids (added to screenMargin if screen)
gridPadding : 8 , // Padding around grid cells. Added to outer margin.
animationDuration : 400 ,
allowBodyOverflow : false , // Allow tests to disable the body-no-scroll policy
sizeOverlayToContent : false // if true, use the size of <body> tag to decide overlay size everytime overlay is shown. should be used in non-client settings
2012-11-18 17:27:20 +00:00
} ;
2014-02-21 22:02:51 +00:00
var width = $ ( context ) . width ( ) ;
var height = $ ( context ) . height ( ) ;
var resizing = null ;
var sidebarVisible = true ;
var expandedPanel = null ;
var previousScreen = null ;
var currentScreen = null ;
2014-02-23 04:41:42 +00:00
var currentHash = null ;
2014-02-21 22:02:51 +00:00
var screenBindings = { } ;
var dialogBindings = { } ;
var wizardShowFunctions = { } ;
var openDialogs = [ ] ; // FIFO stack
2014-02-23 04:41:42 +00:00
var resettingHash = false ;
2014-02-21 22:02:51 +00:00
function setup ( ) {
requiredStyles ( ) ;
hideAll ( ) ;
setInitialExpandedSidebarPanel ( ) ;
sizeScreens ( width , height , '[layout="screen"]' , true ) ;
positionOffscreens ( width , height ) ;
$ ( '[layout="sidebar"]' ) . show ( ) ;
$ ( '[layout="panel"]' ) . show ( ) ;
layout ( ) ;
}
function setInitialExpandedSidebarPanel ( ) {
2014-04-08 11:43:26 +00:00
expandedPanel = 'panelFriends' ;
2014-02-21 22:02:51 +00:00
}
function layout ( ) {
width = $ ( context ) . width ( ) ;
height = $ ( context ) . height ( ) ;
// TODO
// Work on naming. File is layout, class is Layout, this method
// is layout and every other method starts with 'layoutX'. Perhaps
// a little redundant?
layoutCurtain ( width , height ) ;
layoutDialogOverlay ( width , height ) ;
layoutScreens ( width , height ) ;
layoutSidebar ( width , height ) ;
layoutHeader ( width , height ) ;
layoutNotify ( width , height ) ;
layoutFooter ( width , height ) ;
}
function layoutCurtain ( screenWidth , screenHeight ) {
var curtainStyle = {
position : 'absolute' ,
width : screenWidth + 'px' ,
height : screenHeight + 'px'
} ;
$ ( '.curtain' ) . css ( curtainStyle ) ;
}
function layoutDialogOverlay ( screenWidth , screenHeight ) {
var style = {
position : 'absolute' ,
width : screenWidth + 'px' ,
height : screenHeight + 'px'
} ;
$ ( '.dialog-overlay' ) . css ( style ) ;
}
function layoutScreens ( screenWidth , screenHeight ) {
var previousScreenSelector = '[layout-id="' + previousScreen + '"]' ;
var currentScreenSelector = '[layout-id="' + currentScreen + '"]' ;
$ ( currentScreenSelector ) . show ( ) ;
var width = screenWidth - ( 2 * opts . gutter + 2 * opts . screenMargin ) ;
var left = - 1 * width - 100 ;
if ( currentScreenSelector === previousScreenSelector ) {
left = $ ( currentScreenSelector ) . css ( "left" ) ;
if ( left ) {
left = left . split ( "px" ) [ 0 ] ;
}
}
$ ( previousScreenSelector ) . animate ( { left : left } , { duration : opts . animationDuration , queue : false } ) ;
sizeScreens ( screenWidth , screenHeight , '[layout="screen"]' ) ;
positionOffscreens ( screenWidth , screenHeight ) ;
positionOnscreen ( screenWidth , screenHeight ) ;
}
function sizeScreens ( screenWidth , screenHeight , selector , immediate ) {
var duration = opts . animationDuration ;
if ( immediate ) {
duration = 0 ;
}
var width = screenWidth - ( 2 * opts . gutter + 2 * opts . screenMargin ) ;
if ( sidebarVisible ) {
width -= ( opts . sidebarWidth + 2 * opts . gridPadding ) ;
} else {
width -= opts . collapsedSidebar + 2 * opts . gridPadding ;
width += opts . gutter ; // Add back in the right gutter width.
}
var height = screenHeight - opts . headerHeight - ( 2 * opts . gutter + 2 * opts . screenMargin ) ;
var css = {
width : width ,
height : height
} ;
var $screens = $ ( selector ) ;
$screens . animate ( css , { duration : duration , queue : false } ) ;
layoutHomeScreen ( width , height ) ;
}
/ * *
* Postition all screens that are not the current screen .
* /
function positionOffscreens ( screenWidth , screenHeight ) {
var top = opts . headerHeight + opts . gutter + opts . screenMargin ;
var left = - 1 * ( screenWidth + 2 * opts . gutter ) ;
var $screens = $ ( '[layout="screen"]' ) . not ( '[layout-id="' + currentScreen + '"]' ) ;
$screens . css ( {
top : top ,
left : left
} ) ;
}
/ * *
* Position the current screen
* /
function positionOnscreen ( screenWidth , screenHeight , immediate ) {
var duration = opts . animationDuration ;
if ( immediate ) {
duration = 0 ;
}
var top = opts . headerHeight + opts . gutter + opts . screenMargin ;
var left = opts . gutter + opts . screenMargin ;
var $screen = $ ( '[layout-id="' + currentScreen + '"]' ) ;
$screen . animate ( {
top : top ,
left : left ,
overflow : 'auto'
} , duration ) ;
}
function layoutHomeScreen ( homeScreenWidth , homeScreenHeight ) {
var $grid = $ ( '[layout-grid]' ) ;
var gridWidth = homeScreenWidth ;
var gridHeight = homeScreenHeight ;
$grid . css ( { width : gridWidth , height : gridHeight } ) ;
var layout = $grid . attr ( 'layout-grid' ) ;
if ( ! layout )
return ;
var gridRows = layout . split ( 'x' ) [ 0 ] ;
var gridCols = layout . split ( 'x' ) [ 1 ] ;
$grid . children ( ) . each ( function ( ) {
var childPosition = $ ( this ) . attr ( "layout-grid-position" ) ;
var childRow = childPosition . split ( ',' ) [ 1 ] ;
var childCol = childPosition . split ( ',' ) [ 0 ] ;
var childRowspan = $ ( this ) . attr ( "layout-grid-rows" ) ;
var childColspan = $ ( this ) . attr ( "layout-grid-columns" ) ;
var childLayout = me . getCardLayout ( gridWidth , gridHeight , gridRows , gridCols ,
childRow , childCol , childRowspan , childColspan ) ;
$ ( this ) . animate ( {
width : childLayout . width ,
height : childLayout . height ,
top : childLayout . top ,
left : childLayout . left
} , opts . animationDuration ) ;
} ) ;
}
function layoutSidebar ( screenWidth , screenHeight ) {
var width = opts . sidebarWidth ;
var expanderHeight = $ ( '[layout-sidebar-expander]' ) . height ( ) ;
var height = screenHeight - opts . headerHeight - 2 * opts . gutter + expanderHeight ;
var right = opts . gutter ;
if ( ! sidebarVisible ) {
// Negative right to hide most of sidebar
right = ( 0 - opts . sidebarWidth ) + opts . collapsedSidebar ;
}
var top = opts . headerHeight + opts . gutter - expanderHeight ;
var css = {
width : width ,
height : height ,
top : top ,
right : right
} ;
$ ( '[layout="sidebar"]' ) . animate ( css , opts . animationDuration ) ;
layoutPanels ( width , height ) ;
if ( sidebarVisible ) {
$ ( '[layout-panel="collapsed"]' ) . hide ( ) ;
$ ( '[layout-panel="expanded"]' ) . show ( ) ;
$ ( '[layout-sidebar-expander="hidden"]' ) . hide ( ) ;
$ ( '[layout-sidebar-expander="visible"]' ) . show ( ) ;
} else {
$ ( '[layout-panel="collapsed"]' ) . show ( ) ;
$ ( '[layout-panel="expanded"]' ) . hide ( ) ;
$ ( '[layout-sidebar-expander="hidden"]' ) . show ( ) ;
$ ( '[layout-sidebar-expander="visible"]' ) . hide ( ) ;
}
}
function layoutPanels ( sidebarWidth , sidebarHeight ) {
// TODO - don't like the accordian - poor usability. Requires longest mouse
// reach when switching panels. Probably better to do tabs.
if ( ! sidebarVisible ) {
return ;
}
2014-03-27 18:43:15 +00:00
var $expandedPanel = $ ( '[layout-id="' + expandedPanel + '"]' ) ;
var $expandedPanelContents = $expandedPanel . find ( '[layout-panel="contents"]' ) ;
2014-04-08 11:43:26 +00:00
var combinedHeaderHeight = ( $ ( '[layout-panel="contents"]' ) . length - 1 ) * opts . panelHeaderHeight + opts . alwaysOpenPanelHeaderHeight ;
2014-02-21 22:02:51 +00:00
var expanderHeight = $ ( '[layout-sidebar-expander]' ) . height ( ) ;
2014-04-08 11:43:26 +00:00
var expandedPanelHeight = sidebarHeight - ( combinedHeaderHeight + expanderHeight ) ;
2014-02-21 22:02:51 +00:00
$ ( '[layout-panel="contents"]' ) . hide ( ) ;
$ ( '[layout-panel="contents"]' ) . css ( { "height" : "1px" } ) ;
$expandedPanelContents . show ( ) ;
2014-03-27 18:43:15 +00:00
$expandedPanel . triggerHandler ( 'open' )
2014-02-21 22:02:51 +00:00
$expandedPanelContents . animate ( { "height" : expandedPanelHeight + "px" } , opts . animationDuration ) ;
}
function layoutHeader ( screenWidth , screenHeight ) {
var width = screenWidth - 2 * opts . gutter ;
var height = opts . headerHeight - opts . gutter ;
var top = opts . gutter ;
var left = opts . gutter ;
var css = {
width : width + "px" ,
height : height + "px" ,
top : top + "px" ,
left : left + "px"
} ;
$ ( '[layout="header"]' ) . css ( css ) ;
}
function layoutNotify ( screenWidth , screenHeight ) {
var $notify = $ ( '[layout="notify"]' ) ;
var nHeight = $notify . height ( ) ;
var notifyStyle = {
bottom : '0px' ,
position : 'fixed'
} ;
$notify . css ( notifyStyle ) ;
}
function layoutFooter ( screenWidth , screenHeight ) {
if ( ! opts . layoutFooter ) {
return ;
}
var $footer = $ ( '#footer' ) ;
$footer . show ( ) ;
var nHeight = $footer . height ( ) ;
var footerStyle = {
top : ( screenHeight - 80 ) + 'px'
} ;
var width = screenWidth - ( 2 * opts . gutter + 2 * opts . screenMargin ) ;
var left = - 1 * width - 100 ;
$footer . animate ( { "left" : opts . gutter , "width" : width , "top" : ( screenHeight - 78 ) + "px" } , opts . animationDuration ) ;
}
function requiredLayoutStyles ( ) {
var layoutStyle = {
position : 'absolute' ,
margin : '0px' ,
padding : '0px'
} ;
$ ( '[layout]' ) . css ( layoutStyle ) ;
// JW: Setting z-index of notify to 1001, so it will appear above the dialog overlay.
// This allows dialogs to use the notification.
$ ( '[layout="notify"]' ) . css ( { "z-index" : "1001" , "padding" : "20px" } ) ;
$ ( '[layout="panel"]' ) . css ( { position : 'relative' } ) ;
2014-04-08 11:43:26 +00:00
var $headers = $ ( '[layout-panel="expanded"] [layout-panel="header"]' ) ;
context . _ . each ( $headers , function ( $header ) {
$header = $ ( $header ) ;
var isAlwaysOpenHeader = $header . is ( '.always-open' ) ;
$header . css ( {
margin : "0px" ,
padding : "0px" ,
height : ( isAlwaysOpenHeader ? opts . alwaysOpenPanelHeaderHeight : opts . panelHeaderHeight ) + "px"
} ) ;
} )
2014-02-21 22:02:51 +00:00
$ ( '[layout-grid]' ) . css ( {
position : "relative"
} ) ;
$ ( '[layout-grid]' ) . children ( ) . css ( {
position : "absolute"
} ) ;
}
function requiredStyles ( ) {
var bodyStyle = {
margin : '0px' ,
padding : '0px' ,
overflow : 'hidden'
} ;
if ( opts . allowBodyOverflow ) {
delete bodyStyle . overflow ;
}
$ ( 'body' ) . css ( bodyStyle ) ;
requiredLayoutStyles ( ) ;
var curtainStyle = {
position : "absolute" ,
margin : '0px' ,
padding : '0px' ,
overflow : 'hidden' ,
zIndex : 100
} ;
$ ( '.curtain' ) . css ( curtainStyle ) ;
}
function hideAll ( ) {
$ ( '[layout]' ) . hide ( ) ;
$ ( '[layout="header"]' ) . show ( ) ;
}
function showSidebar ( ) {
sidebarVisible = true ;
layout ( ) ;
}
function hideSidebar ( ) {
sidebarVisible = false ;
layout ( ) ;
}
function toggleSidebar ( ) {
if ( sidebarVisible ) {
hideSidebar ( ) ;
} else {
showSidebar ( ) ;
}
}
function hideDialogs ( ) {
// TODO - may need dialogEvents here for specific dialogs.
$ ( '[layout="dialog"]' ) . hide ( ) ;
$ ( '.dialog-overlay' ) . hide ( ) ;
}
function tabClicked ( evt ) {
evt . preventDefault ( ) ;
var destination = $ ( evt . currentTarget ) . attr ( 'tab-target' ) ;
$ ( '[tab-target]' ) . removeClass ( 'selected' ) ;
$ ( evt . currentTarget ) . addClass ( 'selected' ) ;
$ ( '.tab' ) . hide ( ) ;
$ ( '[tab-id="' + destination + '"]' ) . show ( ) ;
}
function linkClicked ( evt ) {
evt . preventDefault ( ) ;
var $currentTarget = $ ( evt . currentTarget ) ;
// allow links to be disabled
if ( $currentTarget . hasClass ( "disabled" ) ) {
return ;
}
var destination = $ ( evt . currentTarget ) . attr ( 'layout-link' ) ;
var destinationType = $ ( '[layout-id="' + destination + '"]' ) . attr ( "layout" ) ;
if ( destinationType === "screen" ) {
context . location = '/client#/' + destination ;
} else if ( destinationType === "dialog" ) {
showDialog ( destination ) ;
}
}
function close ( evt ) {
var $target = $ ( evt . currentTarget ) . closest ( '[layout]' ) ;
var layoutId = $target . attr ( 'layout-id' ) ;
var isDialog = ( $target . attr ( 'layout' ) === 'dialog' ) ;
if ( isDialog ) {
closeDialog ( layoutId ) ;
} else {
$target . hide ( ) ;
}
return false ;
}
function closeDialog ( dialog ) {
2014-03-20 11:53:26 +00:00
logger . debug ( "closing dialog: " + dialog ) ;
2014-02-21 22:02:51 +00:00
var $dialog = $ ( '[layout-id="' + dialog + '"]' ) ;
dialogEvent ( dialog , 'beforeHide' ) ;
var $overlay = $ ( '.dialog-overlay' ) ;
unstackDialogs ( $overlay ) ;
$dialog . hide ( ) ;
dialogEvent ( dialog , 'afterHide' ) ;
2014-03-26 17:09:48 +00:00
$ ( me ) . triggerHandler ( 'dialog_closed' , { dialogCount : openDialogs . length } )
2014-02-21 22:02:51 +00:00
}
function screenEvent ( screen , evtName , data ) {
if ( screen && screen in screenBindings ) {
if ( evtName in screenBindings [ screen ] ) {
2014-02-23 04:41:42 +00:00
return screenBindings [ screen ] [ evtName ] . call ( me , data ) ;
2014-02-21 22:02:51 +00:00
}
}
2014-04-09 17:25:52 +00:00
return NOT _HANDLED ;
2014-02-21 22:02:51 +00:00
}
function dialogEvent ( dialog , evtName , data ) {
if ( dialog && dialog in dialogBindings ) {
if ( evtName in dialogBindings [ dialog ] ) {
2014-04-09 17:25:52 +00:00
return dialogBindings [ dialog ] [ evtName ] . call ( me , data ) ;
2014-02-21 22:02:51 +00:00
}
}
2014-04-09 17:25:52 +00:00
return NOT _HANDLED ;
}
function activeElementEvent ( evtName , data ) {
var result = { } ;
var currDialog = currentDialog ( ) ;
if ( currDialog ) {
result . dialog = dialogEvent ( currDialog . attr ( 'layout-id' ) , evtName , data ) ;
}
if ( currentScreen ) {
result . screen = screenEvent ( currentScreen , evtName , data ) ;
}
return result ;
2014-02-21 22:02:51 +00:00
}
2014-02-23 04:41:42 +00:00
function onHashChange ( e , postFunction ) {
2014-02-25 19:22:32 +00:00
if ( currentHash == context . location . hash ) { return }
2014-02-23 04:41:42 +00:00
if ( resettingHash ) {
resettingHash = false ;
e . preventDefault ( ) ;
return false ;
}
try {
var location = context . RouteMap . parse ( context . location . hash ) ;
}
catch ( e ) {
// this is nowhere in the rich client; just let it go through
return postFunction ( e ) ;
}
2014-02-22 18:43:11 +00:00
2014-02-23 04:41:42 +00:00
var screen = location . page . substring ( 1 ) ; // remove leading slash
var accepted = screenEvent ( currentScreen , 'beforeLeave' , { screen : screen , hash : context . location . hash } ) ;
if ( accepted === false ) {
2014-03-20 11:53:26 +00:00
logger . debug ( "navigation to " + context . location . hash + " rejected by " + currentScreen ) ;
2014-02-25 19:22:32 +00:00
//resettingHash = true;
2014-02-23 04:41:42 +00:00
// reset the hash to where it just was
context . location . hash = currentHash ;
}
else {
// not rejected by the screen; let it go
return postFunction ( e ) ;
}
2014-02-22 18:43:11 +00:00
}
2014-02-21 22:02:51 +00:00
function changeToScreen ( screen , data ) {
2014-02-22 18:43:11 +00:00
if ( screen === currentScreen ) {
return ;
}
2014-02-23 04:41:42 +00:00
changeScreen ( screen , data ) ;
2014-02-22 18:43:11 +00:00
}
function changeScreen ( screen , data ) {
2014-02-21 22:02:51 +00:00
previousScreen = currentScreen ;
currentScreen = screen ;
2014-02-23 04:41:42 +00:00
currentHash = context . location . hash ;
var accepted = screenEvent ( previousScreen , 'beforeHide' , data ) ;
if ( accepted === false ) return ;
2014-02-21 22:02:51 +00:00
2014-02-24 18:54:19 +00:00
logger . debug ( "Changing screen to " + currentScreen ) ;
2014-02-21 22:02:51 +00:00
screenEvent ( currentScreen , 'beforeShow' , data ) ;
// For now -- it seems we want it open always.
// TODO - support user preference here? Remember how they left it?
sidebarVisible = true ;
/ *
var openSidebarScreens = [
'home' , 'session' , 'createSession' ,
'findSession' , 'searchResults'
] ;
$ . each ( openSidebarScreens , function ( ) {
logger . debug ( "comparing " + this + " to " + currentScreen ) ;
if ( this === currentScreen ) {
sidebarVisible = true ;
return false ;
}
} ) ;
* /
layout ( ) ;
screenEvent ( previousScreen , 'afterHide' , data ) ;
screenEvent ( currentScreen , 'afterShow' , data ) ;
// Show any requested dialog
if ( "d" in data ) {
2014-03-20 11:53:26 +00:00
showDialog ( data . d , data ) ;
2014-02-21 22:02:51 +00:00
}
}
2014-03-26 17:09:48 +00:00
function isDialogShowing ( ) {
return openDialogs . length > 0 ;
}
2014-03-27 18:43:15 +00:00
function currentDialog ( ) {
if ( openDialogs . length == 0 ) return null ;
return openDialogs [ openDialogs . length - 1 ] ;
}
// payload is a notification event from websocket gateway
function dialogObscuredNotification ( payload ) {
var openDialog = currentDialog ( ) ;
if ( ! openDialog ) return false ;
if ( typeof openDialog . handledNotification === 'function' ) {
return ! openDialog . handledNotification ( payload ) ;
}
else {
return true ;
}
}
2014-04-03 14:11:23 +00:00
// payload is a notification event from websocket gateway
function isNoisyNotification ( payload ) {
var openDialog = currentDialog ( ) ;
if ( ! openDialog ) return false ;
if ( typeof openDialog . isNoisyNotification === 'function' ) {
return ! openDialog . isNoisyNotification ( payload ) ;
}
else {
return true ;
}
}
2014-02-21 22:02:51 +00:00
/ * *
* Responsible for keeping N dialogs in correct stacked order ,
* also moves the . dialog - overlay such that it hides / obscures all dialogs except the highest one
* /
function stackDialogs ( $dialog , $overlay ) {
// don't push a dialog on the stack that is already on there; remove it from where ever it is currently
// and the rest of the code will make it end up at the top
var layoutId = $dialog . attr ( 'layout-id' ) ;
for ( var i = openDialogs . length - 1 ; i >= 0 ; i -- ) {
if ( openDialogs [ i ] . attr ( 'layout-id' ) === layoutId ) {
openDialogs . splice ( i , 1 ) ;
}
}
2014-02-22 07:20:53 +00:00
// pull out a topmost one, if present
var topMost = null ;
for ( var i = openDialogs . length - 1 ; i >= 0 ; i -- ) {
if ( openDialogs [ i ] . attr ( 'topmost' ) === 'true' ) {
topMost = openDialogs [ i ] ;
openDialogs . splice ( i , 1 ) ;
}
}
2014-02-21 22:02:51 +00:00
openDialogs . push ( $dialog ) ;
2014-02-22 07:20:53 +00:00
if ( topMost ) openDialogs . push ( topMost ) ;
2014-02-21 22:02:51 +00:00
var zIndex = 1000 ;
for ( var i in openDialogs ) {
var $openDialog = openDialogs [ i ] ;
$openDialog . css ( 'zIndex' , zIndex ) ;
zIndex ++ ;
}
$overlay . css ( 'zIndex' , zIndex - 1 ) ;
}
function unstackDialogs ( $overlay ) {
if ( openDialogs . length > 0 ) {
openDialogs . pop ( ) ;
}
var zIndex = 1000 + openDialogs . length ;
$overlay . css ( 'zIndex' , zIndex - 1 ) ;
if ( openDialogs . length == 0 ) {
$overlay . hide ( ) ;
}
}
2014-02-22 07:20:53 +00:00
function showDialog ( dialog , options ) {
2014-04-09 17:25:52 +00:00
if ( dialogEvent ( dialog , 'beforeShow' , options ) === false ) {
2014-02-21 22:02:51 +00:00
return ;
}
var $overlay = $ ( '.dialog-overlay' )
if ( opts . sizeOverlayToContent ) {
var $body = $ ( 'body' )
$ ( '.dialog-overlay' ) . css ( {
width : $body . width ( ) + 'px' ,
height : $body . height ( ) + 'px'
} ) ;
}
$overlay . show ( ) ;
centerDialog ( dialog ) ;
var $dialog = $ ( '[layout-id="' + dialog + '"]' ) ;
stackDialogs ( $dialog , $overlay ) ;
$dialog . show ( ) ;
2014-03-20 11:53:26 +00:00
dialogEvent ( dialog , 'afterShow' , options ) ;
2014-02-21 22:02:51 +00:00
}
function centerDialog ( dialog ) {
var $dialog = $ ( '[layout-id="' + dialog + '"]' ) ;
$dialog . css ( {
left : width / 2 - ( $dialog . width ( ) / 2 ) + "px" ,
top : height / 2 - ( $dialog . height ( ) / 2 ) + "px"
} ) ;
}
function panelHeaderClicked ( evt ) {
evt . preventDefault ( ) ;
expandedPanel = $ ( evt . currentTarget ) . closest ( '[layout="panel"]' ) . attr ( "layout-id" ) ;
layout ( ) ;
return false ;
}
function wizardLinkClicked ( evt ) {
evt . preventDefault ( ) ;
var targetStepId = $ ( evt . currentTarget ) . attr ( "layout-wizard-link" ) ;
setWizardStep ( targetStepId ) ;
return false ;
}
function startNewFtue ( ) {
var step = 0 ;
setWizardStep ( step ) ;
wizardShowFunctions [ step ] ( ) ;
showDialog ( 'ftue' ) ;
}
function setWizardStep ( targetStepId ) {
var selector = '[layout-wizard-step="' + targetStepId + '"]' ;
var $targetStep = $ ( selector ) ;
var stepDialogTitle = $targetStep . attr ( "dialog-title" ) ;
if ( stepDialogTitle ) {
var $myDialog = $targetStep . closest ( '[layout="dialog"]' ) ;
var $myTitle = $ ( '.content-head h1' , $myDialog ) ;
$myTitle . html ( stepDialogTitle ) ;
}
// Hide all steps:
// Invoke the 'show' function, if present prior to actually showing.
if ( context . _ . contains ( context . _ . keys ( wizardShowFunctions ) , targetStepId ) ) {
wizardShowFunctions [ targetStepId ] ( ) ;
}
$ ( '[layout-wizard-step]' ) . hide ( ) ;
$targetStep . show ( ) ;
var ftuePurpose = $targetStep . attr ( "dialog-purpose" ) ;
context . JK . GA . trackFTUECompletion ( ftuePurpose , context . JK . detectOS ( ) ) ;
}
function trackLocationChange ( e ) {
context . JK . GA . virtualPageView ( location . pathname + location . search + location . hash ) ;
}
function handleDialogState ( ) {
var rawDialogState = $ . cookie ( 'dialog_state' ) ;
try {
var dialogState = JSON . parse ( rawDialogState ) ;
if ( ! dialogState ) {
$ . removeCookie ( 'dialog_state' ) ;
return ;
}
}
catch ( e ) {
$ . removeCookie ( 'dialog_state' ) ;
return ;
}
var dialogName = dialogState [ 'name' ] ;
if ( dialogName ) {
setTimeout ( function ( ) {
// TODO: we need a 'everything is initialized' event
showDialog ( dialogName ) ;
} , 0 ) ;
}
$ . removeCookie ( 'dialog_state' ) ;
}
// on next page load, a dialog of this name will show
function queueDialog ( name ) {
$ . cookie ( 'dialog_state' , JSON . stringify ( { name : name } ) )
}
function events ( ) {
$ ( context ) . resize ( function ( ) {
if ( resizing ) {
context . clearTimeout ( resizing ) ;
}
resizing = context . setTimeout ( layout , 80 ) ;
} ) ;
$ ( 'body' ) . on ( 'click' , '[layout-link]' , linkClicked ) ;
$ ( '[layout-action="close"]' ) . on ( 'click' , close ) ;
$ ( '[layout-sidebar-expander]' ) . on ( 'click' , toggleSidebar ) ;
$ ( '[layout-panel="expanded"] [layout-panel="header"]' ) . on ( 'click' , panelHeaderClicked ) ;
$ ( '[layout-wizard-link]' ) . on ( 'click' , wizardLinkClicked ) ;
$ ( '[tab-target]' ) . on ( 'click' , tabClicked ) ;
$ ( context ) . on ( 'hashchange' , trackLocationChange ) ;
}
// public functions
this . getOpts = function ( ) {
return opts ;
} ;
2013-06-10 05:24:33 +00:00
2014-02-21 22:02:51 +00:00
// used for concurrent notifications
var notifyQueue = [ ] ;
var firstNotification = false ;
var notifyDetails ;
2012-10-06 16:36:05 +00:00
2014-02-21 22:02:51 +00:00
this . notify = function ( message , descriptor ) {
var $notify = $ ( '[layout="notify"]' ) ;
2013-05-20 01:54:24 +00:00
2014-02-21 22:02:51 +00:00
if ( notifyQueue . length === 0 ) {
firstNotification = true ;
2014-03-08 06:37:25 +00:00
setNotificationInfo ( message , descriptor , $notify ) ;
2014-02-21 22:02:51 +00:00
}
2012-09-21 04:46:45 +00:00
2014-02-21 22:02:51 +00:00
notifyQueue . push ( { message : message , descriptor : descriptor } ) ;
// JW - speeding up the in/out parts of notify. Extending non-moving time.
$notify . slideDown ( 250 )
. delay ( 4000 )
. slideUp ( {
duration : 400 ,
queue : true ,
complete : function ( ) {
notifyDetails = notifyQueue . shift ( ) ;
2013-11-16 04:35:40 +00:00
2014-02-21 22:02:51 +00:00
// shift 1 more time if this is first notification being displayed
if ( firstNotification ) {
notifyDetails = notifyQueue . shift ( ) ;
firstNotification = false ;
2013-10-21 22:13:53 +00:00
}
2014-02-21 22:02:51 +00:00
if ( notifyDetails !== undefined ) {
2014-03-13 09:24:24 +00:00
setNotificationInfo ( notifyDetails . message , notifyDetails . descriptor , $notify ) ;
2013-10-21 22:13:53 +00:00
}
2014-03-08 06:37:25 +00:00
notifyDetails = { } ;
2014-02-21 22:02:51 +00:00
}
} ) ;
} ;
2013-10-21 22:13:53 +00:00
2014-03-08 06:37:25 +00:00
function setNotificationInfo ( message , descriptor , notificationSelector ) {
2014-02-21 22:02:51 +00:00
var $notify = $ ( '[layout="notify"]' ) ;
$ ( 'h2' , $notify ) . text ( message . title ) ;
2014-03-06 17:56:15 +00:00
$ ( 'p' , $notify ) . empty ( ) ;
2014-02-21 22:02:51 +00:00
if ( message . text instanceof jQuery ) {
$ ( 'p' , $notify ) . append ( message . text )
}
else {
$ ( 'p' , $notify ) . html ( message . text ) ;
}
if ( message . icon _url ) {
$ ( '#avatar' , $notify ) . attr ( 'src' , message . icon _url ) ;
$ ( '#notify-avatar' , $notify ) . show ( ) ;
}
else {
$ ( '#notify-avatar' , $notify ) . hide ( ) ;
}
if ( message . detail ) {
$ ( 'div.detail' , $notify ) . html ( message . detail ) . show ( ) ;
}
else {
$ ( 'div.detail' , $notify ) . hide ( ) ;
}
2014-03-08 06:37:25 +00:00
$ ( '#ok-button' , $notify ) . unbind ( 'click' ) ;
$ ( '#cancel-button' , $notify ) . unbind ( 'click' ) ;
2014-02-21 22:02:51 +00:00
if ( descriptor ) {
if ( descriptor . ok _text ) {
$ ( '#ok-button' , $notify ) . html ( descriptor . ok _text ) ;
}
else {
$ ( '#ok-button' , $notify ) . html ( "OKAY" ) ;
2014-03-08 06:37:25 +00:00
}
$ ( '#ok-button' , $notify ) . click ( function ( ) {
if ( descriptor . ok _callback !== undefined ) {
2014-02-21 22:02:51 +00:00
if ( descriptor . ok _callback _args ) {
descriptor . ok _callback ( descriptor . ok _callback _args ) ;
return false ;
2013-10-21 22:13:53 +00:00
}
2014-02-21 22:02:51 +00:00
else {
descriptor . ok _callback ( ) ;
return false ;
2014-02-06 18:59:19 +00:00
}
2014-03-08 06:37:25 +00:00
}
else {
notificationSelector . hide ( ) ;
}
} ) ;
2013-05-26 00:55:30 +00:00
2014-02-21 22:02:51 +00:00
if ( descriptor . cancel _text ) {
$ ( '#cancel-button' , $notify ) . html ( descriptor . cancel _text ) ;
2013-09-27 21:39:51 +00:00
}
2014-02-21 22:02:51 +00:00
else {
if ( descriptor . no _cancel ) {
$ ( '#cancel-button' , $notify ) . hide ( ) ;
}
else {
$ ( '#cancel-button' , $notify ) . html ( "CANCEL" ) ;
}
2013-05-16 15:33:04 +00:00
}
2014-03-08 06:37:25 +00:00
$ ( '#cancel-button' , $notify ) . click ( function ( ) {
if ( descriptor . cancel _callback !== undefined ) {
2014-02-21 22:02:51 +00:00
if ( descriptor . cancel _callback _args ) {
descriptor . cancel _callback ( descriptor . cancel _callback _args ) ;
return false ;
2014-02-07 14:07:08 +00:00
}
2014-02-21 22:02:51 +00:00
else {
descriptor . cancel _callback ( ) ;
return false ;
2014-02-07 14:07:08 +00:00
}
2014-03-08 06:37:25 +00:00
}
else {
notificationSelector . hide ( ) ;
}
} ) ;
2014-02-21 22:02:51 +00:00
}
2014-03-08 06:37:25 +00:00
// by default OKAY and CANCEL should just hide the notification
2014-02-21 22:02:51 +00:00
else {
$ ( '#ok-button' , $notify ) . html ( "OKAY" ) ;
2014-03-08 06:37:25 +00:00
$ ( '#ok-button' , $notify ) . click ( function ( ) {
notificationSelector . hide ( ) ;
} ) ;
2014-02-21 22:02:51 +00:00
$ ( '#cancel-button' , $notify ) . html ( "CANCEL" ) ;
2014-03-08 06:37:25 +00:00
$ ( '#cancel-button' , $notify ) . click ( function ( ) {
notificationSelector . hide ( ) ;
} ) ;
2014-02-21 22:02:51 +00:00
}
}
2014-02-07 14:07:08 +00:00
2014-02-21 22:02:51 +00:00
this . setWizardStep = setWizardStep ;
this . startNewFtue = startNewFtue ;
2012-09-21 04:46:45 +00:00
2014-02-21 22:02:51 +00:00
this . changeToScreen = function ( screen , data ) {
changeToScreen ( screen , data ) ;
} ;
2012-09-21 04:46:45 +00:00
2014-02-23 04:41:42 +00:00
this . onHashChange = function ( e , postFunction ) {
return onHashChange ( e , postFunction ) ;
}
2014-03-20 11:53:26 +00:00
this . showDialog = function ( dialog , options ) {
showDialog ( dialog , options ) ;
2014-02-21 22:02:51 +00:00
} ;
2013-08-10 17:37:09 +00:00
2014-04-03 14:11:23 +00:00
this . dialogObscuredNotification = function ( payload ) {
return dialogObscuredNotification ( payload ) ;
}
this . isNoisyNotification = function ( payload ) {
return isNoisyNotification ( payload ) ;
2014-03-27 18:43:15 +00:00
}
2014-04-09 17:25:52 +00:00
this . shouldFreezeAppOnDisconnect = function ( ) {
return shouldFreezeAppOnDisconnect ( ) ;
}
2014-03-26 17:09:48 +00:00
this . isDialogShowing = function ( ) {
return isDialogShowing ( ) ;
}
2014-04-09 17:25:52 +00:00
this . activeElementEvent = function ( evtName , data ) {
return activeElementEvent ( evtName , data ) ;
}
2014-02-21 22:02:51 +00:00
this . close = function ( evt ) {
close ( evt ) ;
} ;
2013-10-21 22:13:53 +00:00
2014-04-09 17:25:52 +00:00
this . beforeDisconnect = function ( ) {
fireEvents ( ) ;
}
this . afterReconnect = function ( ) {
fireEvents ( ) ;
}
2014-02-21 22:02:51 +00:00
this . closeDialog = closeDialog ;
this . handleDialogState = handleDialogState ;
this . queueDialog = queueDialog ;
/ * *
* Given information on a grid , and a given card ' s grid settings , use the
* margin options and return a list of [ top , left , width , height ]
* for the cell .
* /
this . getCardLayout = function ( gridWidth , gridHeight , gridRows , gridCols , row , col , rowspan , colspan ) {
var _gridWidth = gridWidth + 3 * opts . gridPadding ;
var _gridHeight = gridHeight + 3 * opts . gridPadding ;
var cellWidth , cellHeight , top , left , width , height ;
cellWidth = Math . floor ( ( _gridWidth - 2 * opts . gridOuterMargin ) / gridCols ) ;
cellHeight = Math . floor ( ( _gridHeight - 2 * opts . gridOuterMargin ) / gridRows ) ;
width = colspan * cellWidth - 2 * opts . gridPadding ;
height = rowspan * cellHeight - 2 * opts . gridPadding ;
top = row * cellHeight ; // + opts.gridOuterMargin; // + opts.gridPadding;
left = col * cellWidth ; // + opts.gridOuterMargin; // + opts.gridPadding;
return {
top : top ,
left : left ,
width : width ,
height : height
} ;
} ;
2013-08-10 17:37:09 +00:00
2014-02-21 22:02:51 +00:00
this . bindScreen = function ( screen , handler ) {
screenBindings [ screen ] = handler ;
} ;
2014-02-13 22:57:48 +00:00
2014-02-21 22:02:51 +00:00
this . bindDialog = function ( dialog , handler ) {
dialogBindings [ dialog ] = handler ;
} ;
2013-03-31 13:54:00 +00:00
2014-02-21 22:02:51 +00:00
this . registerWizardStepFunction = function ( stepId , showFunction ) {
wizardShowFunctions [ stepId ] = showFunction ;
} ;
2013-03-31 13:54:00 +00:00
2014-02-21 22:02:51 +00:00
this . initialize = function ( inOpts ) {
me = this ;
opts = $ . extend ( opts , inOpts ) ;
setup ( ) ;
events ( ) ;
} ;
2012-11-03 00:29:58 +00:00
2014-02-21 22:02:51 +00:00
return this ;
2012-09-21 04:46:45 +00:00
2014-02-21 22:02:51 +00:00
} ;
2012-09-21 04:46:45 +00:00
} ( window , jQuery ) ) ;