Reputation: 5829
I am trying to use this plugin but for some reason whenever I include it on the page and try to initialise it with the following code:
$('#nestable_list_1').nestable({
maxDepth: 2
})
I get the following error in the console:
$(...).nestable is not a function
I've copied it from another project where it is working fine and I've even got rid of all scripts on the page apart from jQuery incase something is conflicting with it and it's still not working. The path is definitely correct and I can see that it is loading in the plugin script.
I have done some testing by including other plugin files and using the following code:
if(jQuery().pluginName) {
alert("loaded")
} else {
alert("not loaded");
}
With any other plugin if I include it, it says its loaded and then when I remove it again it says its not loaded. The contents of the exact plugin file I'm using are:
/*!
* Nestable jQuery Plugin - Copyright (c) 2012 David Bushell - http://dbushell.com/
* Dual-licensed under the BSD or MIT licenses
*/
;(function($, window, document, undefined)
{
var hasTouch = 'ontouchstart' in document;
/**
* Detect CSS pointer-events property
* events are normally disabled on the dragging element to avoid conflicts
* https://github.com/ausi/Feature-detection-technique-for-pointer-events/blob/master/modernizr-pointerevents.js
*/
var hasPointerEvents = (function()
{
var el = document.createElement('div'),
docEl = document.documentElement;
if (!('pointerEvents' in el.style)) {
return false;
}
el.style.pointerEvents = 'auto';
el.style.pointerEvents = 'x';
docEl.appendChild(el);
var supports = window.getComputedStyle && window.getComputedStyle(el, '').pointerEvents === 'auto';
docEl.removeChild(el);
return !!supports;
})();
var defaults = {
listNodeName : 'ol',
itemNodeName : 'li',
rootClass : 'dd',
listClass : 'dd-list',
itemClass : 'dd-item',
dragClass : 'dd-dragel',
handleClass : 'dd-handle',
collapsedClass : 'dd-collapsed',
placeClass : 'dd-placeholder',
noDragClass : 'dd-nodrag',
emptyClass : 'dd-empty',
expandBtnHTML : '<button data-action="expand" type="button">Expand</button>',
collapseBtnHTML : '<button data-action="collapse" type="button">Collapse</button>',
group : 0,
maxDepth : 5,
threshold : 20
};
function Plugin(element, options)
{
this.w = $(document);
this.el = $(element);
this.options = $.extend({}, defaults, options);
this.init();
}
Plugin.prototype = {
init: function()
{
var list = this;
list.reset();
list.el.data('nestable-group', this.options.group);
list.placeEl = $('<div class="' + list.options.placeClass + '"/>');
$.each(this.el.find(list.options.itemNodeName), function(k, el) {
list.setParent($(el));
});
list.el.on('click', 'button', function(e) {
if (list.dragEl) {
return;
}
var target = $(e.currentTarget),
action = target.data('action'),
item = target.parent(list.options.itemNodeName);
if (action === 'collapse') {
list.collapseItem(item);
}
if (action === 'expand') {
list.expandItem(item);
}
});
var onStartEvent = function(e)
{
var handle = $(e.target);
if (!handle.hasClass(list.options.handleClass)) {
if (handle.closest('.' + list.options.noDragClass).length) {
return;
}
handle = handle.closest('.' + list.options.handleClass);
}
if (!handle.length || list.dragEl) {
return;
}
list.isTouch = /^touch/.test(e.type);
if (list.isTouch && e.touches.length !== 1) {
return;
}
e.preventDefault();
list.dragStart(e.touches ? e.touches[0] : e);
};
var onMoveEvent = function(e)
{
if (list.dragEl) {
e.preventDefault();
list.dragMove(e.touches ? e.touches[0] : e);
}
};
var onEndEvent = function(e)
{
if (list.dragEl) {
e.preventDefault();
list.dragStop(e.touches ? e.touches[0] : e);
}
};
if (hasTouch) {
list.el[0].addEventListener('touchstart', onStartEvent, false);
window.addEventListener('touchmove', onMoveEvent, false);
window.addEventListener('touchend', onEndEvent, false);
window.addEventListener('touchcancel', onEndEvent, false);
}
list.el.on('mousedown', onStartEvent);
list.w.on('mousemove', onMoveEvent);
list.w.on('mouseup', onEndEvent);
},
serialize: function()
{
var data,
depth = 0,
list = this;
step = function(level, depth)
{
var array = [ ],
items = level.children(list.options.itemNodeName);
items.each(function()
{
var li = $(this),
item = $.extend({}, li.data()),
sub = li.children(list.options.listNodeName);
if (sub.length) {
item.children = step(sub, depth + 1);
}
array.push(item);
});
return array;
};
data = step(list.el.find(list.options.listNodeName).first(), depth);
return data;
},
serialise: function()
{
return this.serialize();
},
reset: function()
{
this.mouse = {
offsetX : 0,
offsetY : 0,
startX : 0,
startY : 0,
lastX : 0,
lastY : 0,
nowX : 0,
nowY : 0,
distX : 0,
distY : 0,
dirAx : 0,
dirX : 0,
dirY : 0,
lastDirX : 0,
lastDirY : 0,
distAxX : 0,
distAxY : 0
};
this.isTouch = false;
this.moving = false;
this.dragEl = null;
this.dragRootEl = null;
this.dragDepth = 0;
this.hasNewRoot = false;
this.pointEl = null;
},
expandItem: function(li)
{
li.removeClass(this.options.collapsedClass);
li.children('[data-action="expand"]').hide();
li.children('[data-action="collapse"]').show();
li.children(this.options.listNodeName).show();
},
collapseItem: function(li)
{
var lists = li.children(this.options.listNodeName);
if (lists.length) {
li.addClass(this.options.collapsedClass);
li.children('[data-action="collapse"]').hide();
li.children('[data-action="expand"]').show();
li.children(this.options.listNodeName).hide();
}
},
expandAll: function()
{
var list = this;
list.el.find(list.options.itemNodeName).each(function() {
list.expandItem($(this));
});
},
collapseAll: function()
{
var list = this;
list.el.find(list.options.itemNodeName).each(function() {
list.collapseItem($(this));
});
},
setParent: function(li)
{
if (li.children(this.options.listNodeName).length) {
li.prepend($(this.options.expandBtnHTML));
li.prepend($(this.options.collapseBtnHTML));
}
li.children('[data-action="expand"]').hide();
},
unsetParent: function(li)
{
li.removeClass(this.options.collapsedClass);
li.children('[data-action]').remove();
li.children(this.options.listNodeName).remove();
},
dragStart: function(e)
{
var mouse = this.mouse,
target = $(e.target),
dragItem = target.closest(this.options.itemNodeName);
this.placeEl.css('height', dragItem.height());
mouse.offsetX = e.offsetX !== undefined ? e.offsetX : e.pageX - target.offset().left;
mouse.offsetY = e.offsetY !== undefined ? e.offsetY : e.pageY - target.offset().top;
mouse.startX = mouse.lastX = e.pageX;
mouse.startY = mouse.lastY = e.pageY;
this.dragRootEl = this.el;
this.dragEl = $(document.createElement(this.options.listNodeName)).addClass(this.options.listClass + ' ' + this.options.dragClass);
this.dragEl.css('width', dragItem.width());
dragItem.after(this.placeEl);
dragItem[0].parentNode.removeChild(dragItem[0]);
dragItem.appendTo(this.dragEl);
$(document.body).append(this.dragEl);
this.dragEl.css({
'left' : e.pageX - mouse.offsetX,
'top' : e.pageY - mouse.offsetY
});
// total depth of dragging item
var i, depth,
items = this.dragEl.find(this.options.itemNodeName);
for (i = 0; i < items.length; i++) {
depth = $(items[i]).parents(this.options.listNodeName).length;
if (depth > this.dragDepth) {
this.dragDepth = depth;
}
}
},
dragStop: function(e)
{
var el = this.dragEl.children(this.options.itemNodeName).first();
el[0].parentNode.removeChild(el[0]);
this.placeEl.replaceWith(el);
this.dragEl.remove();
this.el.trigger('change');
if (this.hasNewRoot) {
this.dragRootEl.trigger('change');
}
this.reset();
},
dragMove: function(e)
{
var list, parent, prev, next, depth,
opt = this.options,
mouse = this.mouse;
this.dragEl.css({
'left' : e.pageX - mouse.offsetX,
'top' : e.pageY - mouse.offsetY
});
// mouse position last events
mouse.lastX = mouse.nowX;
mouse.lastY = mouse.nowY;
// mouse position this events
mouse.nowX = e.pageX;
mouse.nowY = e.pageY;
// distance mouse moved between events
mouse.distX = mouse.nowX - mouse.lastX;
mouse.distY = mouse.nowY - mouse.lastY;
// direction mouse was moving
mouse.lastDirX = mouse.dirX;
mouse.lastDirY = mouse.dirY;
// direction mouse is now moving (on both axis)
mouse.dirX = mouse.distX === 0 ? 0 : mouse.distX > 0 ? 1 : -1;
mouse.dirY = mouse.distY === 0 ? 0 : mouse.distY > 0 ? 1 : -1;
// axis mouse is now moving on
var newAx = Math.abs(mouse.distX) > Math.abs(mouse.distY) ? 1 : 0;
// do nothing on first move
if (!mouse.moving) {
mouse.dirAx = newAx;
mouse.moving = true;
return;
}
// calc distance moved on this axis (and direction)
if (mouse.dirAx !== newAx) {
mouse.distAxX = 0;
mouse.distAxY = 0;
} else {
mouse.distAxX += Math.abs(mouse.distX);
if (mouse.dirX !== 0 && mouse.dirX !== mouse.lastDirX) {
mouse.distAxX = 0;
}
mouse.distAxY += Math.abs(mouse.distY);
if (mouse.dirY !== 0 && mouse.dirY !== mouse.lastDirY) {
mouse.distAxY = 0;
}
}
mouse.dirAx = newAx;
/**
* move horizontal
*/
if (mouse.dirAx && mouse.distAxX >= opt.threshold) {
// reset move distance on x-axis for new phase
mouse.distAxX = 0;
prev = this.placeEl.prev(opt.itemNodeName);
// increase horizontal level if previous sibling exists and is not collapsed
if (mouse.distX > 0 && prev.length && !prev.hasClass(opt.collapsedClass)) {
// cannot increase level when item above is collapsed
list = prev.find(opt.listNodeName).last();
// check if depth limit has reached
depth = this.placeEl.parents(opt.listNodeName).length;
if (depth + this.dragDepth <= opt.maxDepth) {
// create new sub-level if one doesn't exist
if (!list.length) {
list = $('<' + opt.listNodeName + '/>').addClass(opt.listClass);
list.append(this.placeEl);
prev.append(list);
this.setParent(prev);
} else {
// else append to next level up
list = prev.children(opt.listNodeName).last();
list.append(this.placeEl);
}
}
}
// decrease horizontal level
if (mouse.distX < 0) {
// we can't decrease a level if an item preceeds the current one
next = this.placeEl.next(opt.itemNodeName);
if (!next.length) {
parent = this.placeEl.parent();
this.placeEl.closest(opt.itemNodeName).after(this.placeEl);
if (!parent.children().length) {
this.unsetParent(parent.parent());
}
}
}
}
var isEmpty = false;
// find list item under cursor
if (!hasPointerEvents) {
this.dragEl[0].style.visibility = 'hidden';
}
this.pointEl = $(document.elementFromPoint(e.pageX - document.body.scrollLeft, e.pageY - (window.pageYOffset || document.documentElement.scrollTop)));
if (!hasPointerEvents) {
this.dragEl[0].style.visibility = 'visible';
}
if (this.pointEl.hasClass(opt.handleClass)) {
this.pointEl = this.pointEl.parent(opt.itemNodeName);
}
if (this.pointEl.hasClass(opt.emptyClass)) {
isEmpty = true;
}
else if (!this.pointEl.length || !this.pointEl.hasClass(opt.itemClass)) {
return;
}
// find parent list of item under cursor
var pointElRoot = this.pointEl.closest('.' + opt.rootClass),
isNewRoot = this.dragRootEl.data('nestable-id') !== pointElRoot.data('nestable-id');
/**
* move vertical
*/
if (!mouse.dirAx || isNewRoot || isEmpty) {
// check if groups match if dragging over new root
if (isNewRoot && opt.group !== pointElRoot.data('nestable-group')) {
return;
}
// check depth limit
depth = this.dragDepth - 1 + this.pointEl.parents(opt.listNodeName).length;
if (depth > opt.maxDepth) {
return;
}
var before = e.pageY < (this.pointEl.offset().top + this.pointEl.height() / 2);
parent = this.placeEl.parent();
// if empty create new list to replace empty placeholder
if (isEmpty) {
list = $(document.createElement(opt.listNodeName)).addClass(opt.listClass);
list.append(this.placeEl);
this.pointEl.replaceWith(list);
}
else if (before) {
this.pointEl.before(this.placeEl);
}
else {
this.pointEl.after(this.placeEl);
}
if (!parent.children().length) {
this.unsetParent(parent.parent());
}
if (!this.dragRootEl.find(opt.itemNodeName).length) {
this.dragRootEl.append('<div class="' + opt.emptyClass + '"/>');
}
// parent root list has changed
if (isNewRoot) {
this.dragRootEl = pointElRoot;
this.hasNewRoot = this.el[0] !== this.dragRootEl[0];
}
}
}
};
$.fn.nestable = function(params)
{
var lists = this,
retval = this;
lists.each(function()
{
var plugin = $(this).data("nestable");
if (!plugin) {
$(this).data("nestable", new Plugin(this, params));
$(this).data("nestable-id", new Date().getTime());
} else {
if (typeof params === 'string' && typeof plugin[params] === 'function') {
retval = plugin[params]();
}
}
});
return retval || lists;
};
})(window.jQuery || window.Zepto, window, document);
Here is the source of my page:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Edit Menu | The Craft Channel</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<link href='//fonts.googleapis.com/css?family=Ubuntu:400,300,300italic,400italic,500,700italic,700,500italic&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="http://local.jump2it.com/metronic/global/plugins/jquery-nestable/jquery.nestable.css" />
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="//oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="row">
<div class="col-sm-4 col-md-4 col-lg-3">
<div class="portlet box blue-hoki">
<div class="portlet-title">
<div class="caption">Pages</div>
</div>
<div class="portlet-body">
<form>
<div class="form-group">
<select class="form-control" name="page_id"><option value="1">Some test page</option></select>
</div>
<div class="for-group">
<button type="button" class="btn btn-primary" id="add-page">Add to menu</button>
</div>
</form>
</div>
</div>
<div class="portlet box blue-hoki">
<div class="portlet-title">
<div class="caption">Custom</div>
</div>
<div class="portlet-body">
<form class="custom-menu-item">
<div class="form-group">
<input type="text" name="navigation_label" class="navigation_label form-control" placeholder="Navigation label">
</div>
<div class="form-group">
<input type="text" name="url" class="url form-control" placeholder="Target URL">
</div>
<button type="button" class="btn btn-primary" id="add-custom">Add to menu</button>
</form>
</div>
</div>
</div>
<div class="col-sm-8 col-md-8 col-lg-9">
<div class="portlet box blue-hoki">
<div class="portlet-title">
<div class="caption">Main Menu Items</div>
<div class="actions">
<form class="form-inline menu-info" role="form" method="POST" action="http://local.jump2it.com/admin/menus/update/1">
<input type="hidden" name="_token" value="sRF0SrjVoy6mq49rgNObx743EYlqtIZWWVSaAjwL">
<input type="hidden" name="menu_data" id="menu_data" value="">
<input class="btn btn-primary" type="submit" value="Save">
</form>
</div>
</div>
<div class="portlet-body">
<p class="xs-mbottom20 sm-mbottom20 md-mbottom20 lg-mbottom20">Drag each item into the order you prefer. Click the arrow on the right of the item to reveal additional configuration options.</p>
<div class="dd" id="nestable_list_1">
<ol class="dd-list"><li class="dd-item" data-entity_id="" data-type="Custom" data-link_text="Home" data-url="/"><div class="dd-handle">Home</div><div class="type"><span>Custom</span><div class="toggle"></div></div><div class="info"><div class="row"><div class="col-lg-6"><label>Navigation label</label><input type="text" name="navigation_label" class="navigation_label form-control" value="Home"></div><div class="col-lg-6"><label>URL</label><input type="text" name="url" class="url form-control" value="/"></div></div><a href="#" class="remove">Remove</a> | <a href="#" class="cancel">Cancel</a></div></li><li class="dd-item" data-entity_id="" data-type="Custom" data-link_text="News" data-url="/news"><div class="dd-handle">News</div><div class="type"><span>Custom</span><div class="toggle"></div></div><div class="info"><div class="row"><div class="col-lg-6"><label>Navigation label</label><input type="text" name="navigation_label" class="navigation_label form-control" value="News"></div><div class="col-lg-6"><label>URL</label><input type="text" name="url" class="url form-control" value="/news"></div></div><a href="#" class="remove">Remove</a> | <a href="#" class="cancel">Cancel</a></div></li></ol>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script type="text" src="http://local.jump2it.com/metronic/global/plugins/jquery-nestable/jquery.nestable.js"></script>
<script type="text/javascript">
var changes_made = false;
$(document).ready(function() {
$('#nestable_list_1').nestable({
maxDepth: 2
})
.on('change', function() {
changes_made = true;
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
});
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
window.onbeforeunload = leave_check;
// drop down options
$('#nestable_list_1').on('click', '.dd-list .dd-item .type .toggle', function() {
parent = $(this).closest('.dd-item');
if(parent.hasClass('open')) {
parent.removeClass('open');
$('> .info', parent).slideUp();
} else {
parent.addClass('open');
$('> .info', parent).slideDown();
}
});
// close dropdown options box with the cancel button
$('#nestable_list_1').on('click', '.dd-list .dd-item .info .cancel', function(e) {
e.preventDefault();
parent = $(this).closest('.dd-item');
parent.removeClass('open');
$('.info', parent).slideUp();
});
// when a page is added
$('#add-page').click(function() {
page_id = $('select[name="page_id"] option:selected').val();
page_name = $('select[name="page_id"] option:selected').text();
html = '<li class="dd-item" data-entity_id="'+page_id+'" data-type="Page" data-link_text="'+page_name+'" data-url="">\
<div class="dd-handle">'+page_name+'</div>\
<div class="type">\
<span>Page</span>\
<div class="toggle"></div>\
</div>\
<div class="info">\
<div class="row">\
<div class="col-lg-6">\
<label>Navigation label</label>\
<input type="text" name="navigation_label" class="navigation_label form-control" value="'+page_name+'">\
</div>\
</div>\
<a href="#" class="remove">Remove</a> | <a href="#" class="cancel">Cancel</a>\
</div>\
</li>';
$('#nestable_list_1 > ol.dd-list').append(html);
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
});
// when a custom link is added
$('#add-custom').click(function() {
link_text = $('.custom-menu-item .navigation_label').val();
url = $('.custom-menu-item .url').val();
html = '<li class="dd-item" data-entity_id="" data-type="Custom" data-link_text="'+link_text+'" data-url="'+url+'">\
<div class="dd-handle">'+link_text+'</div>\
<div class="type">\
<span>Custom</span>\
<div class="toggle"></div>\
</div>\
<div class="info">\
<div class="row">\
<div class="col-lg-6">\
<label>Navigation label</label>\
<input type="text" name="navigation_label" class="navigation_label form-control" value="'+link_text+'">\
</div>\
<div class="col-lg-6">\
<label>URL</label>\
<input type="text" name="url" class="url form-control" value="'+url+'">\
</div>\
</div>\
<a href="#" class="remove">Remove</a> | <a href="#" class="cancel">Cancel</a>\
</div>\
</li>';
$('#nestable_list_1 > ol.dd-list').append(html);
$('.custom-menu-item .navigation_label').val("");
$('.custom-menu-item .url').val("");
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
});
// when link text is updated
$('#nestable_list_1').on('keyup', '.navigation_label', function(event) {
val = $(this).val();
$(this).closest('.dd-item').data('link_text', val);
$(this).closest('.dd-item').attr('data-link_text', val);
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
});
// when url is updated
$('#nestable_list_1').on('keyup', '.url', function(event) {
val = $(this).val();
$(this).closest('.dd-item').data('url', val);
$(this).closest('.dd-item').attr('data-url', val);
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
});
// when a page is deleted
$('#nestable_list_1').on('click', '.dd-list .dd-item .info .remove', function(e) {
e.preventDefault();
if(confirm('Are you sure you want to remove this menu item?')) {
$(this).closest('.dd-item').remove();
$('#menu_data').val(JSON.stringify($('#nestable_list_1').nestable('serialize')));
}
});
$('.menu-info').submit(function() {
changes_made = false;
return true;
});
});
function leave_check()
{
if(changes_made) {
return "Are you sure you want to leave this page? Any unsaved changes will be lost.";
}
}
function apply_changes()
{
$.post('http://local.jump2it.com/admin/menus/ajax_update_category_tree', { 'categories' : JSON.stringify($('#nestable_list_1').nestable('serialize')) }, function(data) {
}, 'json');
}
// method to collapse all items on page load
function collapse(obj)
{
if($('ol.dd-list', obj).size() > 0) {
obj.addClass('dd-collapsed');
$('button[data-action="expand"]', obj).show();
$('button[data-action="collapse"]', obj).hide();
}
}
</script>
</body>
</html>
How can it be possible that the file works on another project, the path is correct, the file is definitely being loaded and the execution order is correct yet it still just errors?
Upvotes: 1
Views: 3060
Reputation: 5829
The reason it didn't work is because I had type="text"
instead of type="text/javascript"
on my script tag when I was including the plugin
Upvotes: 2