Skip to content

Commit

Permalink
Redid MutationObserver logic. We now listen for attributes changes, b…
Browse files Browse the repository at this point in the history
…ut disconnect and reconnect around the packery call to avoid infinite loops.

Tabs to spaces in panels.dashboard.js
  • Loading branch information
Matthew McNamara committed Feb 21, 2015
1 parent aecb99e commit e2408bc
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 105 deletions.
23 changes: 15 additions & 8 deletions lib/dashboard/public/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ window.onload = function () {
.on('reconnect_failed', function onReconnectFailed() {
socketStatus('danger', '<strong>Failed to reconnect</strong> to NodeCG after the max. number of attempts');
notify('Reconnection Failed', {
body:'Could not reconnect to NodeCG after the maximum number of attempts.',
body: 'Could not reconnect to NodeCG after the maximum number of attempts.',
icon: FAIL_URI,
tag: 'reconnect_failed'
});
Expand Down Expand Up @@ -143,17 +143,25 @@ window.onload = function () {
//Init Packery
initPackery($panelsContainer);

startObserving();
});

function startObserving() {
// Packery causes attributes changes, so before applying it
// we disconnect then reconnect our observer to avoid infinite loops
try {
//Create a MutationObserver which will watch for changes to the DOM and re-apply masonry
observer = new MutationObserver(function () {
observer.disconnect();
applyPackery();
startObserving();
});

// define what element should be observed by the observer
// and what types of mutations trigger the callback
observer.observe(document, {
subtree: true,
attributes: false, // causes infinite loop, masonry itself triggers the MutationObserver if true
attributes: true,
childList: true,
characterData: true,
attributeOldValue: false,
Expand All @@ -162,19 +170,18 @@ window.onload = function () {
} catch (e) {
console.warn('MutationObserver not supported, dashboard panels may be less responsive to DOM changes');
}

});
}

// re-apply masonry onClick, useful for checkboxes that toggle controls
$panelsContainer.click(function () {
applyPackery();
});

// Initialize all panel info popovers
$panels.find('.panel-info').popover();
// Initialize all panel info popovers
$panels.find('.panel-info').popover();

// Helper for .btn-file inputs, makes them fire the expected event and display expected filename
$('.btn-file :file').on('change', function() {
// Helper for .btn-file inputs, makes them fire the expected event and display expected filename
$('.btn-file :file').on('change', function () {
var input = $(this),
numFiles = input.get(0).files ? input.get(0).files.length : 1,
label = input.val().replace(/\\/g, '/').replace(/.*\//, ''),
Expand Down
196 changes: 99 additions & 97 deletions lib/dashboard/public/panels.dashboard.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,108 @@
'use strict';

var $packeryContainer,
isPackeryInit = false;
isPackeryInit = false;

function arrayUnique(array) {
var a = array.concat();
for (var i = 0; i < a.length; ++i) {
for (var j = i + 1; j < a.length; ++j) {
if (a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
var a = array.concat();
for (var i = 0; i < a.length; ++i) {
for (var j = i + 1; j < a.length; ++j) {
if (a[i] === a[j])
a.splice(j--, 1);
}
}
return a;
}

function initPackery($panelsContainer) {
var itemSelector = '.panel';

$packeryContainer = $panelsContainer.packery({
columnWidth: 128, //.panel-span-1 width = 128
gutter: 16, // gutter = 8*2
// disable initial layout
isInitLayout: false
});

var pckry = $packeryContainer.data('packery');

// Initial sort
var sortOrder = []; // global variable for saving order, used later
var storedSortOrder = localStorage.getItem('panelSortingOrder');
if (storedSortOrder) {
storedSortOrder = JSON.parse(storedSortOrder);

//create a hash of items
var itemsByPanelName = {},
allPanels = [];
var panelName;
for (var i = 0, len = pckry.items.length; i < len; i++) {
var item = pckry.items[i];
panelName = $(item.element).attr('data-panel');
allPanels[i] = panelName;
itemsByPanelName[panelName] = item;
}

// Merge the saved panel array with our currently loaded panels, remove dupes
var allPanelsOrdered = arrayUnique(storedSortOrder.concat(allPanels));

// Remove panels that no longer exist
var removededOld = allPanelsOrdered.filter(function (val) {
return allPanels.indexOf(val) !== -1;
});

// overwrite packery item order
i = 0;
len = removededOld.length;
for (; i < len; i++) {
panelName = removededOld[i];
pckry.items[i] = itemsByPanelName[panelName];
}
}

// Manually trigger initial layout
$packeryContainer.packery();
isPackeryInit = true;

$packeryContainer.find(itemSelector).each(function (i, itemElem) {
// make element draggable with Draggabilly
var draggie = new Draggabilly(itemElem, {
"handle": '.panel-heading'
});

// bind Draggabilly events to Packery
$packeryContainer.packery('bindDraggabillyEvents', draggie);
});

// Daggabilly shtuff

// Currently, there is no built-in option to force dragged elements to gravitate to their
// nearest neighbour in a specific direction. This will reset their locations 100ms after a drag
// causing them to gravitate.
$packeryContainer.packery('on', 'dragItemPositioned', function (pckryInstance, draggedItem) {
setTimeout(function () {
$packeryContainer.packery();
}, 100);
});

function orderItems() {
var itemElems = pckry.getItemElements();

// reset / empty order array
sortOrder.length = 0;

for (var i = 0; i < itemElems.length; i++) {
sortOrder[i] = itemElems[i].getAttribute('data-panel');
}

// save ordering
localStorage.setItem('panelSortingOrder', JSON.stringify(sortOrder));
}

$packeryContainer.packery('on', 'layoutComplete', orderItems);
$packeryContainer.packery('on', 'dragItemPositioned', orderItems);
var itemSelector = '.panel';

$packeryContainer = $panelsContainer.packery({
columnWidth: 128, //.panel-span-1 width = 128
gutter: 16, // gutter = 8*2
// disable initial layout
isInitLayout: false
});

var pckry = $packeryContainer.data('packery');

// Initial sort
var sortOrder = []; // global variable for saving order, used later
var storedSortOrder = localStorage.getItem('panelSortingOrder');
if (storedSortOrder) {
storedSortOrder = JSON.parse(storedSortOrder);

//create a hash of items
var itemsByPanelName = {},
allPanels = [];
var panelName;
for (var i = 0, len = pckry.items.length; i < len; i++) {
var item = pckry.items[i];
panelName = $(item.element).attr('data-panel');
allPanels[i] = panelName;
itemsByPanelName[panelName] = item;
}

// Merge the saved panel array with our currently loaded panels, remove dupes
var allPanelsOrdered = arrayUnique(storedSortOrder.concat(allPanels));

// Remove panels that no longer exist
var removededOld = allPanelsOrdered.filter(function (val) {
return allPanels.indexOf(val) !== -1;
});

// overwrite packery item order
i = 0;
len = removededOld.length;
for (; i < len; i++) {
panelName = removededOld[i];
pckry.items[i] = itemsByPanelName[panelName];
}
}

// Manually trigger initial layout
$packeryContainer.packery();
isPackeryInit = true;

$packeryContainer.find(itemSelector).each(function (i, itemElem) {
// make element draggable with Draggabilly
var draggie = new Draggabilly(itemElem, {
"handle": '.panel-heading'
});

// bind Draggabilly events to Packery
$packeryContainer.packery('bindDraggabillyEvents', draggie);
});

// Daggabilly shtuff

// Currently, there is no built-in option to force dragged elements to gravitate to their
// nearest neighbour in a specific direction. This will reset their locations 100ms after a drag
// causing them to gravitate.
$packeryContainer.packery('on', 'dragItemPositioned', function (pckryInstance, draggedItem) {
setTimeout(function () {
$packeryContainer.packery();
}, 100);
});

function orderItems() {
var itemElems = pckry.getItemElements();

// reset / empty order array
sortOrder.length = 0;

for (var i = 0; i < itemElems.length; i++) {
sortOrder[i] = itemElems[i].getAttribute('data-panel');
}

// save ordering
localStorage.setItem('panelSortingOrder', JSON.stringify(sortOrder));
}

$packeryContainer.packery('on', 'layoutComplete', orderItems);
$packeryContainer.packery('on', 'dragItemPositioned', orderItems);
}

function applyPackery() {
if (isPackeryInit) $packeryContainer.packery();
}
if (isPackeryInit) $packeryContainer.packery();
}

0 comments on commit e2408bc

Please sign in to comment.