123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- SC.loadPackage({ 'Selection': {
- comment: 'I represent the selection of the editing tool, holding one or more Elements. I selection can move its Elements, it shows the Widgets, that apply to all of the Elements in the current selection, and it can start the content editing mode of one of its Elements.',
- sharedProperties: {
- selectionTools: { initValue: '<div class="sg-editing-selection"><div class="sg-editing-selection-widget-menu-right"></div><div class="sg-editing-selection-widget-menu-bottom"></div></div>' },
- },
- properties: {
- node: { comment: 'My DOM node.' },
- elements: { comment: 'I hold an array of the currently selected elements.' },
- active: { comment: 'Wether a selection is active or not.' },
- widgetsRight: { comment: 'I hold an array of the standard widgets, which are applicable to all elements, and which are shown on the right side of the selection.'},
- widgetsBottom: { comment: 'I hold an array of the widgets, which are applicable to the elements in the current selection, and which are shown on the bottom of the selection.'},
- menuNodeRight: { comment: 'I hold the DOM node containing the right menu node.'},
- menuNodeBottom: { comment: 'I hold the DOM node containing the bottom menu node.'},
- activeWidget: { comment: 'I store either null or the active (selected) widget.'},
- lockWidget: { comment: 'My lockWidget plays a special role, since it appears only on multiple selections and must update its state.'}
- },
- methods: {
- init: {
- comment: 'I init myself.',
- code: function(){
- var node = (new DOMParser()).parseFromString(this.class.get('selectionTools'), 'text/html').body.firstChild;
- this.set({
- node: node,
- menuNodeRight: node.firstChild,
- menuNodeBottom: node.lastChild,
- widgetsRight: [],
- widgetsBottom: [],
- active: false,
- elements: [],
- activeWidget: null
- });
- var generalWidgets = ['WidgetLayerTop', 'WidgetLayerBottom', 'WidgetCopy', 'WidgetDelete', 'WidgetEditHTML'],
- generalWidgetsContainer = this.get('menuNodeRight'),
- widget = null;
- for(var i = 0, l = generalWidgets.length; i < l; i++){
- widget = SC.init(generalWidgets[i], this);
- this.get('widgetsRight').push(widget);
- generalWidgetsContainer.appendChild(widget.get('widgetMenu'));
- }
- this.set({ lockWidget: SC.init('WidgetLock', this) });
-
- }
- },
- updateWidgetMenu: {
- comment: 'I update the menu of widgets according to the current selection.',
- code: function(){
- // Update widget menu to the right
- for(var widgetsRight = this.get('widgetsRight'), i = 0, l = widgetsRight.length;
- i < l; i++){
- widgetsRight[i].set({ isWidgetActive: false });
- }
- // Update widget menu to the bottom
- var elements = this.get('elements'),
- elementsWidgetSets = [],
- currentWidgets = [],
- currentWidgetsClasses = null,
- currentWidgetsContainer = this.get('menuNodeBottom'),
- widget = null;
-
- for(var widgetsBottom = this.get('widgetsBottom'), i = 0, l = widgetsBottom.length;
- i < l; i++){
- widgetsBottom[i].set({ isWidgetActive: false });
- currentWidgetsContainer.removeChild(widgetsBottom[i].get('widgetMenu'));
- }
- if(elements.length > 0){
-
- // find intersection of applicable widgets
- for(var i = 0, l = elements.length; i < l; i++){
- elementsWidgetSets.push( elements[i].class.get('applicableWidgets') );
- }
-
- if(elementsWidgetSets.length > 1){
- currentWidgetsClasses = elementsWidgetSets.shift().filter(function(v) {
- return elementsWidgetSets.every(function(a) {
- return a.indexOf(v) !== -1;
- });
- });
- }else{
- currentWidgetsClasses = elementsWidgetSets[0];
- }
- for(var i = 0, l = currentWidgetsClasses.length; i < l; i++){
- widget = SC.init(currentWidgetsClasses[i], this);
- currentWidgets.push(widget);
- currentWidgetsContainer.appendChild(widget.get('widgetMenu'));
- }
- }
- this.set({
- widgetsBottom: currentWidgets,
- activeWidget: null
- });
- // Update widget to the right
- this.do('updateLockGroup');
- // Finally draw red outline when multiple elements are selected
- if(elements.length === 1){
- this.get('node').classList.remove('sg-editing-selection-outline');
- }else{
- this.get('node').classList.add('sg-editing-selection-outline');
- }
- }
- },
- updateLockGroup: {
- comment: 'After adding and removing elements, I check wether I have multiple elements, and if so, if they form a locked group or not.',
- code: function(){
- var myElements = this.get('elements'),
- lockWidget = this.get('lockWidget'),
- menuNodeRight = this.get('menuNodeRight'),
- isGroup = true;
- if(myElements.length > 1){
- // check and add lockWidget
- if(lockWidget.get('widgetMenu').parentNode !== menuNodeRight){
- menuNodeRight.insertBefore(lockWidget.get('widgetMenu'), menuNodeRight.childNodes[0]);
- }
- // check group status of myElements
- for(var i = 0, l = myElements.length; i < l; i++){
- isGroup = isGroup && (myElements[i].get('group') !== null);
- }
- if(isGroup){
- for(var i = 1, l = myElements.length; i < l; i++){
- isGroup = isGroup && (myElements[i].get('group') === myElements[i-1].get('group'))
- }
- }
- lockWidget.set({ locked: isGroup })
- }else if(myElements.length > 0){
- // remove lockWidget
- if(lockWidget.get('widgetMenu').parentNode === menuNodeRight){
- menuNodeRight.removeChild(lockWidget.get('widgetMenu'));
- }
- }
- }
- },
- callWidgetAction: {
- comment: 'I call a widget object\'s action',
- code: function(config){
- if(this.do('isEmpty')){
- return;
- }
- var widget = (function(){
- var widgetsRight = this.get('widgetsRight'),
- widgetsBottom = this.get('widgetsBottom');
- for(var i = 0, l = widgetsRight.length; i < l; i++){
- if(widgetsRight[i].class() === config.widget){
- return widgetsRight[i];
- }
- }
- for(var i = 0, l = widgetsBottom.length; i < l; i++){
- if(widgetsBottom[i].class() === config.widget){
- return widgetsBottom[i];
- }
- }
- if('WidgetLock' === config.widget){
- return this.get('lockWidget');
- }
- }).call(this);
- if(!widget){
- return;
- }
- widget.do('action', config.modifier)
-
- }
- },
- addElement: {
- comment: 'I add anElement to myself.',
- code: function(anElement){
- var elementsToAdd = [ anElement ],
- myElements = this.get('elements'),
- group = anElement.get('group');
- if(group !== null){
- for(var elements = SuperGlue.get('document').get('children'),
- i = 0, l = elements.length; i < l; i++){
- if(elements[i].get('group') === group){
- elementsToAdd.push(elements[i]);
- }
- }
- }
- for(var i = 0, l = elementsToAdd.length; i < l; i++){
- if(myElements.indexOf(elementsToAdd[i]) < 0){
- myElements.push(elementsToAdd[i]);
- }
- }
-
- if(myElements.length === 1){
- myElements[0].get('resizeHandles').set({ selected: true });
- myElements[0].get('resizeHandles').do('showResizeHandles');
- }else{
- for(var i = 0, l = myElements.length; i < l; i++){
- myElements[i].get('resizeHandles').set({ selected: false });
- }
- }
- if(!this.get('active')){
- SuperGlue.get('document').get('editingContainer').appendChild(this.get('node'));
- }
- this.do('updateDimensions');
- this.do('updateWidgetMenu');
-
- }
- },
- removeElement: {
- comment: 'I remove anElement from myself.',
- code: function(anElement){
- var myElements = this.get('elements'),
- elementsToRemove = [ anElement ],
- group = anElement.get('group');
- if(group !== null){
- for(var i = 0, l = myElements.length; i < l; i++){
- if(myElements[i].get('group') === group){
- elementsToRemove.push(myElements[i]);
- }
- }
- }
- for(var i = 0, l = elementsToRemove.length; i < l; i++){
- var elementIndex = myElements.indexOf(elementsToRemove[i]);
- if(elementIndex >= 0){
- myElements.splice(elementIndex, 1);
- elementsToRemove[i].get('resizeHandles').set({ selected: false, mouseOnElement: false });
- elementsToRemove[i].get('resizeHandles').do('hideResizeHandles', true);
- }
- }
-
-
- if(myElements.length === 0){
- SuperGlue.get('document').get('editingContainer').removeChild(this.get('node'));
- this.set({ active: false });
- }else{
- if(myElements.length === 1){
- myElements[0].get('resizeHandles').set({ selected: true });
- myElements[0].get('resizeHandles').do('showResizeHandles');
- }else{
- for(var i = 0, l = myElements.length; i < l; i++){
- myElements[i].get('resizeHandles').set({ selected: false });
- }
- }
- this.do('updateDimensions');
- this.do('updateWidgetMenu');
- }
- }
- },
- toggleSelectionFor: {
- comment: 'I check wether anElement belongs to me already, and then add or remove it.',
- code: function(anElement){
- if(this.get('elements').indexOf(anElement) > -1){
- this.do('removeElement', anElement);
- }else{
- this.do('addElement', anElement);
- }
- }
- },
- clearAll: {
- comment: 'I remove all my Elements.',
- code: function(){
- var elements = this.get('elements');
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].get('resizeHandles').set({ selected: false, mouseOnElement: false });
- elements[i].get('resizeHandles').do('hideResizeHandles', true)
- }
- var widgetsBottom = this.get('widgetsBottom');
- for(var i = 0, l = widgetsBottom.length; i < l; i++){
- widgetsBottom[i].set({ isWidgetActive: false });
- }
- var editingContainer = SuperGlue.get('document').get('editingContainer');
- if(this.get('node').parentNode === editingContainer){
- editingContainer.removeChild(this.get('node'));
- }
- this.set({
- elements: [],
- active: false
- });
-
- }
- },
- isEmpty: {
- comment: 'Am I empty?',
- code: function(){
- return this.get('elements').length === 0
- }
- },
- calculateDimensions: {
- comment: 'I return the overall dimension to fit in all my Elements in the form [top, left, width, height].',
- code: function(){
- if(this.do('isEmpty')){ return []; }
-
- var myElements = this.get('elements'),
- top = myElements[0].get('top'),
- left = myElements[0].get('left'),
- width = 0,
- height = 0,
- l = myElements.length;
- for(var i = 0; i < l; i++){
- top = myElements[i].get('top') < top ? myElements[i].get('top') : top;
- left = myElements[i].get('left') < left ? myElements[i].get('left') : left;
- width = (myElements[i].get('width') + myElements[i].get('left')) < width
- ? width
- : (myElements[i].get('width') + myElements[i].get('left'));
- height = (myElements[i].get('height') + myElements[i].get('top')) < height
- ? height
- : (myElements[i].get('height') + myElements[i].get('top'));
- }
- width -= left;
- height -= top;
-
- return [top, left, width, height];
- }
- },
- updateDimensions: {
- comment: 'I update the overall dimension of my DOM node to fit in all my Elements.',
- code: function(){
- if(this.do('isEmpty')){ return; }
-
- var nodeStyle = this.get('node').style,
- dimensions = this.do('calculateDimensions'),
- top = dimensions[0],
- left = dimensions[1],
- width = dimensions[2],
- height = dimensions[3];
-
- nodeStyle.top = top + 'px';
- nodeStyle.left = left + 'px';
- nodeStyle.width = width + 'px';
- nodeStyle.height = height + 'px';
- }
- },
- registerForSelection: {
- comment: 'I prepare an Element to be selectable. Used by Element>>init.',
- code: function(anElement){
- var self = this,
- myNode = this.get('node'),
- thisElement = anElement,
- elementNode = anElement.get('node'),
- elements = [],
-
- infiniteSpace = true,
- pageWidth = 0,
- virtualTop = 0,
- virtualLeft = 0,
- virtualWidth = 0,
- virtualHeight = 0,
- elementsOffsetsX = null,
- elementsOffsetsY = null,
- targetNotInSelection,
- isGroup,
- group,
- startX = 0,
- startY = 0,
- clickPrecisionXleft = 0,
- clickPrecisionXright = 0,
- clickPrecisionYtop = 0,
- clickPrecisionYbottom = 0,
- withinClickPrecision = true,
- widthMarkersVisible,
- gridVisible,
- onMouseDown = function(evt){
- if(evt.button !== 0) return;
- if(evt.shiftKey || evt.ctrlKey){
- document.addEventListener('mouseup', onMouseUpWithModifier, true);
- SuperGlue.get('document').set({ interactionInProgress: true });
- // UNDO
- }else{
- startX = evt.pageX;
- startY = evt.pageY;
-
- clickPrecisionXleft = startX - 6;
- clickPrecisionXright = startX + 6;
- clickPrecisionYtop = startY - 6;
- clickPrecisionYbottom = startY + 6;
- withinClickPrecision = true;
- isGroup = false;
- infiniteSpace = ! SuperGlue.get('document').get('layout').centered;
- pageWidth = SuperGlue.get('document').get('layout').width;
- if(SuperGlue.get('document').get('grid').get('active')){
- var gridSize = SuperGlue.get('document').get('grid').get('gridSize');
- pageWidth = Math.floor(pageWidth / gridSize) * gridSize;
- }
- elements = self.get('elements');
-
- if(elements.length !== 0){
- targetNotInSelection = true;
- for(var i = 0, l = elements.length; i < l; i++){
- if(elements[i].get('node') === thisElement.get('node')){
- targetNotInSelection = false;
- break;
- }
- }
- if(targetNotInSelection){
- self.do('clearAll');
- elements = self.get('elements');
- }
- }else{
- targetNotInSelection = elements.indexOf(thisElement) < 0;
- }
- if(elements.length === 0){
- group = thisElement.get('group');
- if(group !== null){
- isGroup = true;
- for(var allElements = SuperGlue.get('document').get('children'),
- i = 0, l = allElements.length; i < l; i++){
- if(allElements[i].get('group') === group){
- elements.push(allElements[i]);
- }
- }
-
- }
- }
- if(elements.length === 0){
- virtualTop = anElement.get('top');
- virtualLeft = anElement.get('left');
- virtualWidth = anElement.get('width');
- virtualHeight = anElement.get('height');
- }else{
- var dimensions = self.do('calculateDimensions');
- virtualTop = dimensions[0];
- virtualLeft = dimensions[1];
- virtualWidth = dimensions[2];
- virtualHeight = dimensions[3];
- elementsOffsetsX = [];
- elementsOffsetsY = [];
- for(var i = 0, l = elements.length; i < l; i++){
- elementsOffsetsX.push( elements[i].get('left') - virtualLeft );
- elementsOffsetsY.push( elements[i].get('top') - virtualTop );
- }
- }
-
- document.addEventListener('mousemove', onMouseMove, true);
- document.addEventListener('mouseup', onMouseUp, true);
- SuperGlue.get('document').set({ interactionInProgress: true });
- // UNDO
- }
- evt.stopPropagation();
- evt.preventDefault();
- },
- onMouseUpWithModifier = function(evt){
- self.do('toggleSelectionFor', thisElement);
- document.removeEventListener('mouseup', onMouseUpWithModifier, true);
- SuperGlue.get('document').set({ interactionInProgress: false });
-
- // UNDO
-
- evt.stopPropagation();
- evt.preventDefault();
- },
- onMouseUp = function(evt){
- document.removeEventListener('mousemove', onMouseMove, true);
- document.removeEventListener('mouseup', onMouseUp, true);
-
- var myDocument = SuperGlue.get('document');
- myDocument.set({ interactionInProgress: false });
- myDocument.do('afterLayoutHasChanged');
- if(withinClickPrecision){
-
- if( !targetNotInSelection
- && thisElement.class() === 'TextElement'
- ){
- thisElement.do('activateTextEditor');
- }else{
-
- self.do('clearAll');
- self.do('addElement', thisElement);
- }
-
- }else{
- (function(elements, thisElement){
- var savedDimensions = []
- if(elements.length === 0){
- savedDimensions.push({
- top: thisElement.get('top'),
- left: thisElement.get('left'),
- width: thisElement.get('width'),
- height: thisElement.get('height')
- })
- }else{
- for(var i = 0, l = elements.length; i < l; i++){
- savedDimensions.push({
- top: elements[i].get('top'),
- left: elements[i].get('left'),
- width: elements[i].get('width'),
- height: elements[i].get('height')
- })
- }
- }
- SuperGlue.get('history').do('actionHasSucceeded', function(){
- if(elements.length === 0){
- thisElement.set({
- top: savedDimensions[0].top,
- left: savedDimensions[0].left,
- width: savedDimensions[0].width,
- height: savedDimensions[0].height
- })
- }else{
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- top: savedDimensions[i].top,
- left: savedDimensions[i].left,
- width: savedDimensions[i].width,
- height: savedDimensions[i].height
- })
- }
- }
- })
- }).call(this, elements, thisElement)
- SuperGlue.get('document').get('widthMarkers').set({ visible: widthMarkersVisible });
- SuperGlue.get('document').get('grid').set({ visible: gridVisible });
- if(isGroup){
- self.do('clearAll');
- }
- }
- // UNDO
-
- evt.stopPropagation();
- evt.preventDefault();
- },
- onMouseMove = function(evt){
- var diffX = evt.pageX - startX,
- diffY = evt.pageY - startY;
-
- if( withinClickPrecision &&
- ( evt.pageX < clickPrecisionXleft
- || evt.pageX > clickPrecisionXright
- || evt.pageY < clickPrecisionYtop
- || evt.pageY > clickPrecisionYbottom )
- ){
- var widthMarkers = SuperGlue.get('document').get('widthMarkers'),
- grid = SuperGlue.get('document').get('grid');
- widthMarkersVisible = widthMarkers.get('visible');
- gridVisible = grid.get('visible');
- widthMarkers.set({ visible: true });
- grid.set({ visible: true });
- (function(elements, thisElement){
- var savedDimensions = []
- if(elements.length === 0){
- savedDimensions.push({
- top: thisElement.get('top'),
- left: thisElement.get('left'),
- width: thisElement.get('width'),
- height: thisElement.get('height')
- })
- }else{
- for(var i = 0, l = elements.length; i < l; i++){
- savedDimensions.push({
- top: elements[i].get('top'),
- left: elements[i].get('left'),
- width: elements[i].get('width'),
- height: elements[i].get('height')
- })
- }
- }
- SuperGlue.get('history').do('actionHasStarted', function(){
- if(elements.length === 0){
- thisElement.set({
- top: savedDimensions[0].top,
- left: savedDimensions[0].left,
- width: savedDimensions[0].width,
- height: savedDimensions[0].height
- })
- }else{
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- top: savedDimensions[i].top,
- left: savedDimensions[i].left,
- width: savedDimensions[i].width,
- height: savedDimensions[i].height
- })
- }
- }
- })
- }).call(this, elements, thisElement)
- withinClickPrecision = false;
- }
- if(!withinClickPrecision){
- virtualTop += diffY;
- virtualLeft += diffX;
- if(elements.length === 0){
- if(virtualTop > 0){
- thisElement.set({ top: virtualTop });
- }else{
- thisElement.set({ top: 0 });
- }
- if(virtualLeft > 0){
- if(infiniteSpace || (virtualLeft + virtualWidth < pageWidth) ){
- thisElement.set({ left: virtualLeft });
- }else{
- thisElement.set({ left: (pageWidth - virtualWidth) });
- }
- }else{
- thisElement.set({ left: 0 });
- }
-
- }else{
- if(virtualTop > 0){
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- top: elementsOffsetsY[i] + virtualTop
- });
- }
- self.do('updateDimensions');
- }else{
- myNode.style.top = '0px';
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- top: elementsOffsetsY[i]
- });
- }
- }
- if(virtualLeft > 0){
- if(infiniteSpace || (virtualLeft + virtualWidth < pageWidth) ){
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- left: elementsOffsetsX[i] + virtualLeft
- });
- }
- self.do('updateDimensions');
- }else{
- myNode.style.left = (pageWidth - virtualWidth) + 'px';
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- left: elementsOffsetsX[i] + (pageWidth - virtualWidth)
- });
- }
- }
- }else{
- myNode.style.left = '0px';
- for(var i = 0, l = elements.length; i < l; i++){
- elements[i].set({
- left: elementsOffsetsX[i]
- });
- }
- }
-
- }
- startX = evt.pageX;
- startY = evt.pageY;
-
- }
-
- evt.stopPropagation();
- evt.preventDefault();
- };
- elementNode.addEventListener('mousedown', onMouseDown, false);
- }
- }
- }
- }});
|