2015-10-09 22:41:21 +02:00
/ *
* * NOTE : This file is generated by Gulp and should not be edited directly !
* * Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp .
* /
2015-06-09 14:09:03 +03:00
angular . module ( "LayoutEditor" , [ "ngSanitize" , "ngResource" , "ui.sortable" ] ) ;
2015-02-19 22:14:55 +01:00
var LayoutEditor ;
( function ( LayoutEditor ) {
var Clipboard = function ( ) {
var self = this ;
2015-06-20 19:35:11 +03:00
this . _clipboardData = { } ;
this . _isDisabled = false ;
this . _wasInvoked = false ;
this . setData = function ( contentType , data ) {
self . _clipboardData [ contentType ] = data ;
self . _wasInvoked = true ;
2015-02-19 22:14:55 +01:00
} ;
2015-06-20 19:35:11 +03:00
this . getData = function ( contentType ) {
self . _wasInvoked = true ;
2016-04-02 02:15:38 +02:00
return self . _clipboardData [ contentType ] ;
2015-02-19 22:14:55 +01:00
} ;
this . disable = function ( ) {
2015-06-20 19:35:11 +03:00
self . _isDisabled = true ;
self . _wasInvoked = false ;
self . _clipboardData = { } ;
2015-02-19 22:14:55 +01:00
} ;
2015-06-20 19:35:11 +03:00
this . isDisabled = function ( ) {
return self . _isDisabled ;
}
this . wasInvoked = function ( ) {
return self . _wasInvoked ;
}
2015-02-19 22:14:55 +01:00
}
LayoutEditor . Clipboard = new Clipboard ( ) ;
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. factory ( "clipboard" , [
function ( ) {
return {
setData : LayoutEditor . Clipboard . setData ,
getData : LayoutEditor . Clipboard . getData ,
2015-06-20 19:35:11 +03:00
disable : LayoutEditor . Clipboard . disable ,
isDisabled : LayoutEditor . Clipboard . isDisabled ,
wasInvoked : LayoutEditor . Clipboard . wasInvoked
2015-03-06 09:03:04 +01:00
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
} ) ( LayoutEditor || ( LayoutEditor = { } ) ) ;
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. factory ( "scopeConfigurator" , [ "$timeout" , "clipboard" ,
function ( $timeout , clipboard ) {
return {
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
configureForElement : function ( $scope , $element ) {
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$element . find ( ".layout-panel" ) . click ( function ( e ) {
e . stopPropagation ( ) ;
} ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$element . parent ( ) . keydown ( function ( e ) {
var handled = false ;
var resetFocus = false ;
var element = $scope . element ;
2015-02-19 22:14:55 +01:00
2015-09-21 12:07:44 -07:00
if ( element . editor . isDragging )
2015-03-06 09:03:04 +01:00
return ;
2015-06-20 19:35:11 +03:00
// If native clipboard support exists, the pseudo-clipboard will have been disabled.
if ( ! clipboard . isDisabled ( ) ) {
2015-03-06 09:03:04 +01:00
var focusedElement = element . editor . focusedElement ;
if ( ! ! focusedElement ) {
// Pseudo clipboard handling for browsers not allowing real clipboard operations.
if ( e . ctrlKey ) {
switch ( e . which ) {
case 67 : // C
focusedElement . copy ( clipboard ) ;
break ;
case 88 : // X
focusedElement . cut ( clipboard ) ;
break ;
case 86 : // V
focusedElement . paste ( clipboard ) ;
break ;
}
2015-02-19 22:14:55 +01:00
}
}
}
2015-03-06 09:03:04 +01:00
if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 46 ) { // Del
$scope . delete ( element ) ;
2015-02-19 22:14:55 +01:00
handled = true ;
2015-03-06 09:03:04 +01:00
} else if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && ( e . which == 32 || e . which == 27 ) ) { // Space or Esc
$element . find ( ".layout-panel-action-properties" ) . first ( ) . click ( ) ;
2015-02-19 22:14:55 +01:00
handled = true ;
}
2016-06-05 18:58:17 +02:00
if ( ! ! element . hasEditor ) { // This element has an editor dialog.
2015-03-06 09:03:04 +01:00
if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 13 ) { // Enter
$element . find ( ".layout-panel-action-edit" ) . first ( ) . click ( ) ;
2015-02-19 22:14:55 +01:00
handled = true ;
}
}
2015-03-06 09:03:04 +01:00
if ( ! ! element . children ) { // This is a container.
if ( ! e . ctrlKey && ! e . shiftKey && e . altKey && e . which == 40 ) { // Alt+Down
if ( element . children . length > 0 )
element . children [ 0 ] . setIsFocused ( ) ;
2015-02-19 22:14:55 +01:00
handled = true ;
}
2015-03-06 09:03:04 +01:00
if ( element . type == "Column" ) { // This is a column.
var connectAdjacent = ! e . ctrlKey ;
if ( e . which == 37 ) { // Left
if ( e . altKey )
element . expandLeft ( connectAdjacent ) ;
if ( e . shiftKey )
element . contractRight ( connectAdjacent ) ;
handled = true ;
} else if ( e . which == 39 ) { // Right
if ( e . altKey )
element . contractLeft ( connectAdjacent ) ;
if ( e . shiftKey )
element . expandRight ( connectAdjacent ) ;
handled = true ;
}
2015-02-19 22:14:55 +01:00
}
}
2015-03-06 09:03:04 +01:00
if ( ! ! element . parent ) { // This is a child.
if ( e . altKey && e . which == 38 ) { // Alt+Up
element . parent . setIsFocused ( ) ;
2015-02-19 22:14:55 +01:00
handled = true ;
}
2015-03-06 09:03:04 +01:00
if ( element . parent . type == "Row" ) { // Parent is a horizontal container.
if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 37 ) { // Left
element . parent . moveFocusPrevChild ( element ) ;
handled = true ;
}
else if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 39 ) { // Right
element . parent . moveFocusNextChild ( element ) ;
handled = true ;
}
else if ( e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 37 ) { // Ctrl+Left
element . moveUp ( ) ;
resetFocus = true ;
handled = true ;
}
else if ( e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 39 ) { // Ctrl+Right
element . moveDown ( ) ;
handled = true ;
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
else { // Parent is a vertical container.
if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 38 ) { // Up
element . parent . moveFocusPrevChild ( element ) ;
handled = true ;
}
else if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 40 ) { // Down
element . parent . moveFocusNextChild ( element ) ;
handled = true ;
}
else if ( e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 38 ) { // Ctrl+Up
element . moveUp ( ) ;
resetFocus = true ;
handled = true ;
}
else if ( e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 40 ) { // Ctrl+Down
element . moveDown ( ) ;
handled = true ;
}
2015-02-19 22:14:55 +01:00
}
}
2015-03-06 09:03:04 +01:00
if ( handled ) {
e . preventDefault ( ) ;
}
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
e . stopPropagation ( ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . $apply ( ) ; // Event is not triggered by Angular directive but raw event handler on element.
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
// HACK: Workaround because of how Angular treats the DOM when elements are shifted around - input focus is sometimes lost.
if ( resetFocus ) {
window . setTimeout ( function ( ) {
$scope . $apply ( function ( ) {
element . editor . focusedElement . setIsFocused ( ) ;
} ) ;
} , 100 ) ;
}
} ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . element . setIsFocusedEventHandlers . push ( function ( ) {
$element . parent ( ) . focus ( ) ;
} ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . delete = function ( element ) {
element . delete ( ) ;
}
2015-12-21 19:48:59 +01:00
if ( $scope . element . hasEditor ) {
$scope . edit = function ( ) {
$scope . $root . editElement ( $scope . element ) . then ( function ( args ) {
$scope . $apply ( function ( ) {
if ( args . cancel )
return ;
$scope . element . data = args . element . data ;
2015-12-21 20:59:47 +01:00
$scope . element . applyElementEditorModel ( args . elementEditorModel ) ;
if ( ! ! $scope . element . setHtml )
$scope . element . setHtml ( args . element . html ) ;
2015-12-21 19:48:59 +01:00
} ) ;
} ) ;
} ;
}
2015-03-06 09:03:04 +01:00
} ,
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
configureForContainer : function ( $scope , $element ) {
var element = $scope . element ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
//$scope.isReceiving = false; // True when container is receiving an external element via drag/drop.
$scope . getShowChildrenPlaceholder = function ( ) {
return $scope . element . children . length === 0 && ! $scope . element . getIsDropTarget ( ) ;
} ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . sortableOptions = {
cursor : "move" ,
delay : 150 ,
disabled : element . getIsSealed ( ) ,
distance : 5 ,
//handle: element.children.length < 2 ? ".imaginary-class" : false, // For some reason doesn't get re-evaluated after adding more children.
start : function ( e , ui ) {
$scope . $apply ( function ( ) {
element . setIsDropTarget ( true ) ;
element . editor . isDragging = true ;
} ) ;
// Make the drop target placeholder as high as the item being dragged.
ui . placeholder . height ( ui . item . height ( ) - 4 ) ;
ui . placeholder . css ( "min-height" , 0 ) ;
} ,
stop : function ( e , ui ) {
$scope . $apply ( function ( ) {
element . editor . isDragging = false ;
2015-02-19 22:14:55 +01:00
element . setIsDropTarget ( false ) ;
} ) ;
2015-03-06 09:03:04 +01:00
} ,
over : function ( e , ui ) {
if ( ! ! ui . sender && ! ! ui . sender [ 0 ] . isToolbox ) {
if ( ! ! ui . sender [ 0 ] . dropTargetTimeout ) {
$timeout . cancel ( ui . sender [ 0 ] . dropTargetTimeout ) ;
ui . sender [ 0 ] . dropTargetTimeout = null ;
}
$timeout ( function ( ) {
if ( element . type == "Row" ) {
// If there was a previous drop target and it was a row, roll back any pending column adds to it.
var previousDropTarget = element . editor . dropTargetElement ;
if ( ! ! previousDropTarget && previousDropTarget . type == "Row" )
previousDropTarget . rollbackAddColumn ( ) ;
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
element . setIsDropTarget ( false ) ;
} ) ;
ui . sender [ 0 ] . dropTargetTimeout = $timeout ( function ( ) {
if ( element . type == "Row" ) {
var receivedColumn = ui . item . sortable . model ;
var receivedColumnWidth = Math . floor ( 12 / ( element . children . length + 1 ) ) ;
receivedColumn . width = receivedColumnWidth ;
receivedColumn . offset = 0 ;
element . beginAddColumn ( receivedColumnWidth ) ;
// Make the drop target placeholder the correct width and as high as the highest existing column in the row.
var maxHeight = _ . max ( _ ( $element . find ( "> .layout-children > .layout-column:not(.ui-sortable-placeholder)" ) ) . map ( function ( e ) {
return $ ( e ) . height ( ) ;
} ) ) ;
for ( i = 1 ; i <= 12 ; i ++ )
ui . placeholder . removeClass ( "col-xs-" + i ) ;
ui . placeholder . addClass ( "col-xs-" + receivedColumn . width ) ;
if ( maxHeight > 0 ) {
ui . placeholder . height ( maxHeight ) ;
ui . placeholder . css ( "min-height" , 0 ) ;
}
else {
ui . placeholder . height ( 0 ) ;
ui . placeholder . css ( "min-height" , "" ) ;
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
element . setIsDropTarget ( true ) ;
} , 150 ) ;
}
} ,
receive : function ( e , ui ) {
if ( ! ! ui . sender && ! ! ui . sender [ 0 ] . isToolbox ) {
$scope . $apply ( function ( ) {
var receivedElement = ui . item . sortable . model ;
if ( ! ! receivedElement ) {
if ( element . type == "Row" )
element . commitAddColumn ( ) ;
// Should ideally call LayoutEditor.Container.addChild() instead, but since this handler
// is run *before* the ui-sortable directive's handler, if we try to add the child to the
// array that handler will get an exception when trying to do the same.
// Because of this, we need to invoke "setParent" so that specific container types can perform element speficic initialization.
receivedElement . setEditor ( element . editor ) ;
receivedElement . setParent ( element ) ;
2015-07-04 14:27:04 +02:00
2015-04-03 17:42:11 +02:00
if ( ! ! receivedElement . hasEditor ) {
2015-03-06 09:03:04 +01:00
$scope . $root . editElement ( receivedElement ) . then ( function ( args ) {
if ( ! args . cancel ) {
2015-03-12 00:23:05 +01:00
receivedElement . data = args . element . data ;
2015-07-04 14:27:04 +02:00
receivedElement . applyElementEditorModel ( args . elementEditorModel ) ;
2015-04-17 14:34:00 +02:00
2015-07-04 14:27:04 +02:00
if ( ! ! receivedElement . setHtml )
2015-04-17 14:34:00 +02:00
receivedElement . setHtml ( args . element . html ) ;
2015-03-06 09:03:04 +01:00
}
$timeout ( function ( ) {
if ( ! ! args . cancel )
receivedElement . delete ( ) ;
else
receivedElement . setIsFocused ( ) ;
//$scope.isReceiving = false;
element . setIsDropTarget ( false ) ;
} ) ;
return ;
2015-02-19 22:14:55 +01:00
} ) ;
2015-03-06 09:03:04 +01:00
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
$timeout ( function ( ) {
//$scope.isReceiving = false;
element . setIsDropTarget ( false ) ;
if ( ! ! receivedElement )
receivedElement . setIsFocused ( ) ;
} ) ;
2015-02-19 22:14:55 +01:00
} ) ;
2015-03-06 09:03:04 +01:00
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
} ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . click = function ( child , e ) {
if ( ! child . editor . isDragging )
child . setIsFocused ( ) ;
e . stopPropagation ( ) ;
} ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
$scope . getClasses = function ( child ) {
var result = [ "layout-element" ] ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
if ( ! ! child . children ) {
result . push ( "layout-container" ) ;
if ( child . getIsSealed ( ) )
result . push ( "layout-container-sealed" ) ;
}
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
result . push ( "layout-" + child . type . toLowerCase ( ) ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
if ( ! ! child . dropTargetClass )
result . push ( child . dropTargetClass ) ;
2015-02-19 22:14:55 +01:00
2015-03-06 09:03:04 +01:00
// TODO: Move these to either the Column directive or the Column model class.
if ( child . type == "Row" ) {
result . push ( "row" ) ;
if ( ! child . canAddColumn ( ) )
result . push ( "layout-row-full" ) ;
}
if ( child . type == "Column" ) {
result . push ( "col-xs-" + child . width ) ;
result . push ( "col-xs-offset-" + child . offset ) ;
}
if ( child . type == "Content" )
result . push ( "layout-content-" + child . contentTypeClass ) ;
if ( child . getIsActive ( ) )
result . push ( "layout-element-active" ) ;
if ( child . getIsFocused ( ) )
result . push ( "layout-element-focused" ) ;
if ( child . getIsSelected ( ) )
result . push ( "layout-element-selected" ) ;
if ( child . getIsDropTarget ( ) )
result . push ( "layout-element-droptarget" ) ;
if ( child . isTemplated )
result . push ( "layout-element-templated" ) ;
return result ;
} ;
}
} ;
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutEditor" , [ "environment" ,
function ( environment ) {
return {
restrict : "E" ,
scope : { } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" , "$attrs" , "$compile" , "clipboard" ,
function ( $scope , $element , $attrs , $compile , clipboard ) {
if ( ! ! $attrs . model )
$scope . element = eval ( $attrs . model ) ;
else
throw new Error ( "The 'model' attribute must evaluate to a LayoutEditor.Editor object." ) ;
$scope . click = function ( canvas , e ) {
if ( ! canvas . editor . isDragging )
canvas . setIsFocused ( ) ;
e . stopPropagation ( ) ;
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
$scope . getClasses = function ( canvas ) {
var result = [ "layout-element" , "layout-container" , "layout-canvas" ] ;
if ( canvas . getIsActive ( ) )
result . push ( "layout-element-active" ) ;
if ( canvas . getIsFocused ( ) )
result . push ( "layout-element-focused" ) ;
if ( canvas . getIsSelected ( ) )
result . push ( "layout-element-selected" ) ;
if ( canvas . getIsDropTarget ( ) )
result . push ( "layout-element-droptarget" ) ;
if ( canvas . isTemplated )
result . push ( "layout-element-templated" ) ;
return result ;
2015-03-06 09:03:04 +01:00
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
// An unfortunate side-effect of the next hack on line 54 is that the created elements aren't added to the DOM yet, so we can't use it to get to the parent ".layout-desiger" element.
// Work around: access that element directly (which efectively turns multiple layout editors on a single page impossible).
// //var layoutDesignerHost = $element.closest(".layout-designer").data("layout-designer-host");
var layoutDesignerHost = $ ( ".layout-designer" ) . data ( "layout-designer-host" ) ;
$scope . $root . layoutDesignerHost = layoutDesignerHost ;
layoutDesignerHost . element . on ( "replacecanvas" , function ( e , args ) {
var editor = $scope . element ;
var canvasData = {
data : args . canvas . data ,
htmlId : args . canvas . htmlId ,
htmlClass : args . canvas . htmlClass ,
htmlStyle : args . canvas . htmlStyle ,
isTemplated : args . canvas . isTemplated ,
children : args . canvas . children
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
// HACK: Instead of simply updating the $scope.element with a new instance, we need to replace the entire orc-layout-editor markup
// in order for angular to rebind starting with the Canvas element. Otherwise, for some reason, it will rebind starting with the first child of Canvas.
// You can see this happening when setting a breakpoint in ScopeConfigurator where containers are initialized with drag & drop: on page load, the first element
// is a Canvas (good), but after having selected another template, the first element is (typically) a Grid (bad).
// Simply recompiling the orc-layout-editor directive will cause the entire thing to be generated, which works just fine as well (even though not is nice as simply leveraging model binding).
layoutDesignerHost . editor = window . layoutEditor = new LayoutEditor . Editor ( editor . config , canvasData ) ;
var template = "<orc-layout-editor" + " model='window.layoutEditor' />" ;
var html = $compile ( template ) ( $scope ) ;
$ ( ".layout-editor-holder" ) . html ( html ) ;
} ) ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
$scope . $root . editElement = function ( element ) {
var host = $scope . $root . layoutDesignerHost ;
return host . editElement ( element ) ;
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
$scope . $root . addElement = function ( contentType ) {
var host = $scope . $root . layoutDesignerHost ;
return host . addElement ( contentType ) ;
} ;
2015-03-06 09:03:04 +01:00
2015-03-10 22:06:48 +01:00
$ ( document ) . on ( "cut copy paste" , function ( e ) {
2015-06-20 19:35:11 +03:00
// If the pseudo clipboard was already invoked (which happens on the first clipboard
// operation after page load even if native clipboard support exists) then sit this
// one operation out, but make sure whatever is on the pseudo clipboard gets migrated
// to the native clipboard for subsequent operations.
if ( clipboard . wasInvoked ( ) ) {
e . originalEvent . clipboardData . setData ( "text/plain" , clipboard . getData ( "text/plain" ) ) ;
e . originalEvent . clipboardData . setData ( "text/json" , clipboard . getData ( "text/json" ) ) ;
e . preventDefault ( ) ;
}
else {
var focusedElement = $scope . element . focusedElement ;
if ( ! ! focusedElement ) {
2015-03-10 22:06:48 +01:00
$scope . $apply ( function ( ) {
2015-06-20 19:35:11 +03:00
switch ( e . type ) {
case "copy" :
focusedElement . copy ( e . originalEvent . clipboardData ) ;
break ;
case "cut" :
focusedElement . cut ( e . originalEvent . clipboardData ) ;
break ;
case "paste" :
focusedElement . paste ( e . originalEvent . clipboardData ) ;
break ;
}
2015-03-10 22:06:48 +01:00
} ) ;
2015-06-20 19:35:11 +03:00
// HACK: Workaround because of how Angular treats the DOM when elements are shifted around - input focus is sometimes lost.
window . setTimeout ( function ( ) {
$scope . $apply ( function ( ) {
if ( ! ! $scope . element . focusedElement )
$scope . element . focusedElement . setIsFocused ( ) ;
} ) ;
} , 100 ) ;
e . preventDefault ( ) ;
}
2015-03-10 22:06:48 +01:00
}
2015-06-20 19:35:11 +03:00
// Native clipboard support obviously exists, so disable the peudo clipboard from now on.
clipboard . disable ( ) ;
2015-03-10 22:06:48 +01:00
} ) ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Editor" ) ,
replace : true ,
link : function ( scope , element ) {
// No clicks should propagate from the TinyMCE toolbars.
element . find ( ".layout-toolbar-container" ) . click ( function ( e ) {
2015-02-19 22:14:55 +01:00
e . stopPropagation ( ) ;
2015-03-06 09:03:04 +01:00
} ) ;
// Unfocus and unselect everything on click outside of canvas.
$ ( window ) . click ( function ( e ) {
2015-09-21 12:07:44 -07:00
scope . $apply ( function ( ) {
scope . element . activeElement = null ;
scope . element . focusedElement = null ;
} ) ;
2015-03-06 09:03:04 +01:00
} ) ;
}
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutCanvas" , [ "scopeConfigurator" , "environment" ,
function ( scopeConfigurator , environment ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" , "$attrs" ,
function ( $scope , $element , $attrs ) {
scopeConfigurator . configureForElement ( $scope , $element ) ;
scopeConfigurator . configureForContainer ( $scope , $element ) ;
$scope . sortableOptions [ "axis" ] = "y" ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Canvas" ) ,
replace : true
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutChild" , [ "$compile" ,
function ( $compile ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
link : function ( scope , element ) {
var template = "<orc-layout-" + scope . element . type . toLowerCase ( ) + " element='element' />" ;
var html = $compile ( template ) ( scope ) ;
$ ( element ) . replaceWith ( html ) ;
}
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutColumn" , [ "$compile" , "scopeConfigurator" , "environment" ,
function ( $compile , scopeConfigurator , environment ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
scopeConfigurator . configureForElement ( $scope , $element ) ;
scopeConfigurator . configureForContainer ( $scope , $element ) ;
$scope . sortableOptions [ "axis" ] = "y" ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Column" ) ,
replace : true ,
link : function ( scope , element , attrs ) {
element . find ( ".layout-column-resize-bar" ) . draggable ( {
axis : "x" ,
helper : "clone" ,
revert : true ,
start : function ( e , ui ) {
scope . $apply ( function ( ) {
scope . element . editor . isResizing = true ;
} ) ;
} ,
drag : function ( e , ui ) {
var columnElement = element . parent ( ) ;
var columnSize = columnElement . width ( ) / scope . element . width ;
var connectAdjacent = ! e . ctrlKey ;
if ( $ ( e . target ) . hasClass ( "layout-column-resize-bar-left" ) ) {
var delta = ui . offset . left - columnElement . offset ( ) . left ;
if ( delta < - columnSize && scope . element . canExpandLeft ( connectAdjacent ) ) {
scope . $apply ( function ( ) {
scope . element . expandLeft ( connectAdjacent ) ;
} ) ;
}
else if ( delta > columnSize && scope . element . canContractLeft ( connectAdjacent ) ) {
scope . $apply ( function ( ) {
scope . element . contractLeft ( connectAdjacent ) ;
} ) ;
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
else if ( $ ( e . target ) . hasClass ( "layout-column-resize-bar-right" ) ) {
var delta = ui . offset . left - columnElement . width ( ) - columnElement . offset ( ) . left ;
if ( delta > columnSize && scope . element . canExpandRight ( connectAdjacent ) ) {
scope . $apply ( function ( ) {
scope . element . expandRight ( connectAdjacent ) ;
} ) ;
}
else if ( delta < - columnSize && scope . element . canContractRight ( connectAdjacent ) ) {
scope . $apply ( function ( ) {
scope . element . contractRight ( connectAdjacent ) ;
} ) ;
}
2015-02-19 22:14:55 +01:00
}
2015-03-06 09:03:04 +01:00
} ,
stop : function ( e , ui ) {
scope . $apply ( function ( ) {
scope . element . editor . isResizing = false ;
} ) ;
}
} ) ;
}
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutContent" , [ "$sce" , "scopeConfigurator" , "environment" ,
2015-04-03 01:46:57 +02:00
function ( $sce , scopeConfigurator , environment ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
scopeConfigurator . configureForElement ( $scope , $element ) ;
2015-12-21 20:59:47 +01:00
2015-04-03 01:46:57 +02:00
// Overwrite the setHtml function so that we can use the $sce service to trust the html (and not have the html binding strip certain tags).
$scope . element . setHtml = function ( html ) {
$scope . element . html = html ;
$scope . element . htmlUnsafe = $sce . trustAsHtml ( html ) ;
} ;
$scope . element . setHtml ( $scope . element . html ) ;
}
] ,
templateUrl : environment . templateUrl ( "Content" ) ,
replace : true
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutGrid" , [ "$compile" , "scopeConfigurator" , "environment" ,
function ( $compile , scopeConfigurator , environment ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
scopeConfigurator . configureForElement ( $scope , $element ) ;
scopeConfigurator . configureForContainer ( $scope , $element ) ;
$scope . sortableOptions [ "axis" ] = "y" ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Grid" ) ,
replace : true
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutRow" , [ "$compile" , "scopeConfigurator" , "environment" ,
function ( $compile , scopeConfigurator , environment ) {
return {
restrict : "E" ,
scope : { element : "=" } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
scopeConfigurator . configureForElement ( $scope , $element ) ;
scopeConfigurator . configureForContainer ( $scope , $element ) ;
$scope . sortableOptions [ "axis" ] = "x" ;
$scope . sortableOptions [ "ui-floating" ] = true ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Row" ) ,
replace : true
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutPopup" , [
function ( ) {
return {
restrict : "A" ,
link : function ( scope , element , attrs ) {
var popup = $ ( element ) ;
var trigger = popup . closest ( ".layout-popup-trigger" ) ;
var parentElement = popup . closest ( ".layout-element" ) ;
trigger . click ( function ( ) {
popup . toggle ( ) ;
if ( popup . is ( ":visible" ) ) {
popup . position ( {
my : attrs . orcLayoutPopupMy || "left top" ,
at : attrs . orcLayoutPopupAt || "left bottom+4px" ,
of : trigger
} ) ;
popup . find ( "input" ) . first ( ) . focus ( ) ;
}
} ) ;
popup . click ( function ( e ) {
e . stopPropagation ( ) ;
} ) ;
parentElement . click ( function ( e ) {
2015-02-24 12:47:57 +01:00
popup . hide ( ) ;
2015-03-06 09:03:04 +01:00
} ) ;
popup . keydown ( function ( e ) {
if ( ! e . ctrlKey && ! e . shiftKey && ! e . altKey && e . which == 27 ) // Esc
popup . hide ( ) ;
e . stopPropagation ( ) ;
} ) ;
2015-06-09 02:08:58 +03:00
popup . on ( "cut copy paste" , function ( e ) {
// Allow clipboard operations in popup without invoking clipboard event handlers on parent element.
e . stopPropagation ( ) ;
} ) ;
2015-03-06 09:03:04 +01:00
}
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutToolbox" , [ "$compile" , "environment" ,
function ( $compile , environment ) {
return {
restrict : "E" ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
$scope . resetElements = function ( ) {
$scope . gridElements = [
LayoutEditor . Grid . from ( {
toolboxIcon : "\uf00a" ,
toolboxLabel : "Grid" ,
toolboxDescription : "Empty grid." ,
children : [ ]
2015-03-06 09:03:04 +01:00
} )
2015-03-10 22:06:48 +01:00
] ;
$scope . rowElements = [
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (1 column)" ,
toolboxDescription : "Row with 1 column." ,
children : LayoutEditor . Column . times ( 1 )
} ) ,
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (2 columns)" ,
toolboxDescription : "Row with 2 columns." ,
children : LayoutEditor . Column . times ( 2 )
} ) ,
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (3 columns)" ,
toolboxDescription : "Row with 3 columns." ,
children : LayoutEditor . Column . times ( 3 )
} ) ,
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (4 columns)" ,
toolboxDescription : "Row with 4 columns." ,
children : LayoutEditor . Column . times ( 4 )
} ) ,
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (6 columns)" ,
toolboxDescription : "Row with 6 columns." ,
children : LayoutEditor . Column . times ( 6 )
} ) ,
LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (12 columns)" ,
toolboxDescription : "Row with 12 columns." ,
children : LayoutEditor . Column . times ( 12 )
} ) , LayoutEditor . Row . from ( {
toolboxIcon : "\uf0c9" ,
toolboxLabel : "Row (empty)" ,
toolboxDescription : "Empty row." ,
children : [ ]
} )
] ;
$scope . columnElements = [
LayoutEditor . Column . from ( {
toolboxIcon : "\uf0db" ,
toolboxLabel : "Column" ,
toolboxDescription : "Empty column." ,
width : 1 ,
offset : 0 ,
children : [ ]
} )
] ;
2015-04-19 22:13:09 +02:00
$scope . canvasElements = [
LayoutEditor . Canvas . from ( {
toolboxIcon : "\uf044" ,
toolboxLabel : "Canvas" ,
toolboxDescription : "Empty canvas." ,
children : [ ]
} )
] ;
2015-03-10 22:06:48 +01:00
$scope . contentElementCategories = _ ( $scope . element . config . categories ) . map ( function ( category ) {
return {
name : category . name ,
elements : _ ( category . contentTypes ) . map ( function ( contentType ) {
var type = contentType . type ;
var factory = LayoutEditor . factories [ type ] || LayoutEditor . factories [ "Content" ] ;
var item = {
isTemplated : false ,
contentType : contentType . id ,
contentTypeLabel : contentType . label ,
contentTypeClass : contentType . typeClass ,
data : null ,
hasEditor : contentType . hasEditor ,
html : contentType . html
} ;
var element = factory ( item ) ;
element . toolboxIcon = contentType . icon || "\uf1c9" ;
element . toolboxLabel = contentType . label ;
element . toolboxDescription = contentType . description ;
return element ;
} )
} ;
} ) ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
$scope . resetElements ( ) ;
$scope . getSortableOptions = function ( type ) {
var editorId = $element . closest ( ".layout-editor" ) . attr ( "id" ) ;
var parentClasses ;
var placeholderClasses ;
var floating = false ;
switch ( type ) {
case "Grid" :
parentClasses = [ ".layout-canvas" , ".layout-column" , ".layout-common-holder" ] ;
placeholderClasses = "layout-element layout-container layout-grid ui-sortable-placeholder" ;
break ;
case "Row" :
parentClasses = [ ".layout-grid" ] ;
placeholderClasses = "layout-element layout-container layout-row row ui-sortable-placeholder" ;
break ;
case "Column" :
parentClasses = [ ".layout-row:not(.layout-row-full)" ] ;
placeholderClasses = "layout-element layout-container layout-column ui-sortable-placeholder" ;
floating = true ; // To ensure a smooth horizontal-list reordering. https://github.com/angular-ui/ui-sortable#floating
break ;
case "Content" :
parentClasses = [ ".layout-canvas" , ".layout-column" , ".layout-common-holder" ] ;
placeholderClasses = "layout-element layout-content ui-sortable-placeholder" ;
break ;
2015-04-19 22:13:09 +02:00
case "Canvas" :
parentClasses = [ ".layout-canvas" , ".layout-column" , ".layout-common-holder" ] ;
placeholderClasses = "layout-element layout-container layout-grid ui-sortable-placeholder" ;
break ;
2015-03-10 22:06:48 +01:00
}
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
return {
cursor : "move" ,
connectWith : _ ( parentClasses ) . map ( function ( e ) { return "#" + editorId + " " + e + ":not(.layout-container-sealed) > .layout-element-wrapper > .layout-children" ; } ) . join ( ", " ) ,
placeholder : placeholderClasses ,
"ui-floating" : floating ,
create : function ( e , ui ) {
e . target . isToolbox = true ; // Will indicate to connected sortables that dropped items were sent from toolbox.
} ,
start : function ( e , ui ) {
$scope . $apply ( function ( ) {
$scope . element . isDragging = true ;
} ) ;
} ,
stop : function ( e , ui ) {
$scope . $apply ( function ( ) {
$scope . element . isDragging = false ;
$scope . resetElements ( ) ;
} ) ;
} ,
over : function ( e , ui ) {
$scope . $apply ( function ( ) {
$scope . element . canvas . setIsDropTarget ( false ) ;
} ) ;
} ,
}
} ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
var layoutIsCollapsedCookieName = "layoutToolboxCategory_Layout_IsCollapsed" ;
$scope . layoutIsCollapsed = $ . cookie ( layoutIsCollapsedCookieName ) === "true" ;
2015-02-19 22:14:55 +01:00
2015-03-10 22:06:48 +01:00
$scope . toggleLayoutIsCollapsed = function ( e ) {
$scope . layoutIsCollapsed = ! $scope . layoutIsCollapsed ;
$ . cookie ( layoutIsCollapsedCookieName , $scope . layoutIsCollapsed , { expires : 365 } ) ; // Remember collapsed state for a year.
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "Toolbox" ) ,
2015-06-09 19:38:44 +03:00
replace : true ,
link : function ( scope , element ) {
var toolbox = element . find ( ".layout-toolbox" ) ;
$ ( window ) . on ( "resize scroll" , function ( e ) {
var canvas = element . parent ( ) . find ( ".layout-canvas" ) ;
// If the canvas is taller than the toolbox, make the toolbox sticky-positioned within the editor
// to help the user avoid excessive vertical scrolling.
var canvasIsTaller = ! ! canvas && canvas . height ( ) > toolbox . height ( ) ;
var windowPos = $ ( window ) . scrollTop ( ) ;
if ( canvasIsTaller && windowPos > element . offset ( ) . top + element . height ( ) - toolbox . height ( ) ) {
toolbox . addClass ( "sticky-bottom" ) ;
toolbox . removeClass ( "sticky-top" ) ;
}
else if ( canvasIsTaller && windowPos > element . offset ( ) . top ) {
toolbox . addClass ( "sticky-top" ) ;
toolbox . removeClass ( "sticky-bottom" ) ;
}
else {
toolbox . removeClass ( "sticky-top" ) ;
toolbox . removeClass ( "sticky-bottom" ) ;
}
} ) ;
}
2015-03-06 09:03:04 +01:00
} ;
}
] ) ;
2015-02-19 22:14:55 +01:00
angular
. module ( "LayoutEditor" )
2015-03-06 09:03:04 +01:00
. directive ( "orcLayoutToolboxGroup" , [ "$compile" , "environment" ,
function ( $compile , environment ) {
return {
restrict : "E" ,
scope : { category : "=" } ,
2015-03-10 22:06:48 +01:00
controller : [ "$scope" , "$element" ,
function ( $scope , $element ) {
var isCollapsedCookieName = "layoutToolboxCategory_" + $scope . category . name + "_IsCollapsed" ;
$scope . isCollapsed = $ . cookie ( isCollapsedCookieName ) === "true" ;
$scope . toggleIsCollapsed = function ( e ) {
$scope . isCollapsed = ! $scope . isCollapsed ;
$ . cookie ( isCollapsedCookieName , $scope . isCollapsed , { expires : 365 } ) ; // Remember collapsed state for a year.
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
} ;
}
] ,
2015-03-06 09:03:04 +01:00
templateUrl : environment . templateUrl ( "ToolboxGroup" ) ,
replace : true
} ;
}
] ) ;
2016-06-05 18:58:17 +02:00
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkxheW91dEVkaXRvci5qcyIsIk1vZHVsZS5qcyIsIkNsaXBib2FyZC5qcyIsIlNjb3BlQ29uZmlndXJhdG9yLmpzIiwiRWRpdG9yLmpzIiwiQ2FudmFzLmpzIiwiQ2hpbGQuanMiLCJDb2x1bW4uanMiLCJDb250ZW50LmpzIiwiR3JpZC5qcyIsIlJvdy5qcyIsIlBvcHVwLmpzIiwiVG9vbGJveC5qcyIsIlRvb2xib3hHcm91cC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsQUNMQTtBQ0FBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FDN0NBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQ2xWQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUN6SUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUNsQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ