487 lines
15 KiB
JavaScript
487 lines
15 KiB
JavaScript
|
/**
|
||
|
* ProcessWire Admin Theme jQuery/Javascript for AdminThemeReno
|
||
|
*
|
||
|
* By Tom Reno and Ryan Cramer
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
var ProcessWireAdminTheme = {
|
||
|
|
||
|
/**
|
||
|
* Initialize the default ProcessWire admin theme
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
init: function() {
|
||
|
this.setupCloneButton();
|
||
|
ProcessWireAdmin.init();
|
||
|
// this.setupFieldFocus();
|
||
|
this.setupSearch();
|
||
|
this.setupDropdowns();
|
||
|
this.setupSidebarNav();
|
||
|
this.setupSideBarState();
|
||
|
this.setupSideBarToggle();
|
||
|
var $body = $("body");
|
||
|
var $html = $("html");
|
||
|
if($body.hasClass('hasWireTabs') && $("ul.WireTabs").length == 0) $body.removeClass('hasWireTabs');
|
||
|
$('#content').removeClass('pw-fouc-fix'); // FOUC fix, deprecated
|
||
|
$body.removeClass('pw-init').addClass('pw-ready');
|
||
|
$html.removeClass('pw-init').addClass('pw-ready');
|
||
|
// this.browserCheck();
|
||
|
$('a.notice-remove', '#notices').on('click', function() {
|
||
|
$('#notices').slideUp('fast', function() {
|
||
|
$(this).remove();
|
||
|
return false;
|
||
|
});
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Sidebar Navigation State
|
||
|
*
|
||
|
*/
|
||
|
setupSidebarNav: function() {
|
||
|
|
||
|
var url = window.location.toString();
|
||
|
|
||
|
$(document).on('mouseup', function (e){
|
||
|
var quicklinks = $("ul.quicklinks");
|
||
|
if (!quicklinks.is(e.target) && quicklinks.has(e.target).length === 0) {
|
||
|
quicklinks.hide();
|
||
|
$('.quicklink-open').removeClass('active');
|
||
|
$('#main-nav .current').removeClass('no-arrow');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
$(document).on('keydown', function(e) {
|
||
|
var type = e.target.tagName.toLowerCase();
|
||
|
var firstClass = e.target.className.split(" ")[0];
|
||
|
var state;
|
||
|
|
||
|
// input, textarea, CKEditor (Inline mode) focused, so do nothing.
|
||
|
if (type == 'input' || type == 'textarea' || firstClass == 'InputfieldCKEditorInline') return;
|
||
|
|
||
|
switch(e.which) {
|
||
|
case 37:
|
||
|
state = 'open';
|
||
|
break;
|
||
|
|
||
|
case 39:
|
||
|
state = 'closed';
|
||
|
break;
|
||
|
|
||
|
default: return;
|
||
|
}
|
||
|
ProcessWireAdminTheme.setupSideBarState(true, state);
|
||
|
|
||
|
e.preventDefault();
|
||
|
});
|
||
|
|
||
|
///////////////////////////////////////////////////////////////////
|
||
|
|
||
|
function closeOpenQuicklinks() {
|
||
|
$("#main-nav > li > a.open:not(.hover-temp):not(.just-clicked)").each(function() {
|
||
|
// close sections that are currently open
|
||
|
var $t = $(this);
|
||
|
var $u = $t.next('ul:visible');
|
||
|
if($u.length > 0) {
|
||
|
if($u.find('.quicklinks-open').length > 0) $u.find('.quicklink-close').trigger('click');
|
||
|
//$u.slideUp('fast');
|
||
|
}
|
||
|
//$(this).removeClass('open').removeClass('current');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// this bit of code below monitors single click vs. double click
|
||
|
// on double click it goes to the page linked by the nav item
|
||
|
// on single click it opens or closes the nav
|
||
|
|
||
|
var clickTimer = null, numClicks = 0;
|
||
|
$("#main-nav a.parent").on('dblclick', function(e) {
|
||
|
e.preventDefault();
|
||
|
|
||
|
}).on('click', function() {
|
||
|
var $a = $(this);
|
||
|
$a.addClass('just-clicked');
|
||
|
numClicks++;
|
||
|
if(numClicks === 1) {
|
||
|
clickTimer = setTimeout(function() {
|
||
|
// single click occurred
|
||
|
closeOpenQuicklinks();
|
||
|
$a.toggleClass('open').next('ul').slideToggle(200, function() {
|
||
|
$a.removeClass('just-clicked');
|
||
|
});
|
||
|
numClicks = 0;
|
||
|
}, 200);
|
||
|
} else {
|
||
|
// double click occurred
|
||
|
clearTimeout(clickTimer);
|
||
|
numClicks = 0;
|
||
|
window.location.href = $a.attr('href');
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
});
|
||
|
|
||
|
var quicklinkTimer = null;
|
||
|
|
||
|
$(".quicklink-open").on('click', function(event){
|
||
|
closeOpenQuicklinks();
|
||
|
|
||
|
var $this = $(this);
|
||
|
$this.parent().addClass('quicklinks-open');
|
||
|
$this.toggleClass('active').parent().next('ul.quicklinks').toggle();
|
||
|
$this.parent().parent().siblings().find('ul.quicklinks').hide();
|
||
|
$this.parent().parent().siblings().find('.quicklink-open').removeClass('active').parent('a').removeClass('quicklinks-open');
|
||
|
$this.effect('pulsate', 100);
|
||
|
event.stopPropagation();
|
||
|
//psuedo elements are not part of the DOM, need to remove current arrows by adding a class to the current item.
|
||
|
$('#main-nav .current:not(.open)').addClass('no-arrow');
|
||
|
|
||
|
// below is used to populate quicklinks via ajax json services on Process modules that provide it
|
||
|
var $ul = $(this).parent().next('ul.quicklinks');
|
||
|
var jsonURL = $ul.attr('data-json');
|
||
|
if(jsonURL.length > 0 && !$ul.hasClass('json-loaded')) {
|
||
|
$ul.addClass('json-loaded');
|
||
|
var $spinner = $ul.find('.quicklinks-spinner');
|
||
|
var spinnerSavedClass = $spinner.attr('class');
|
||
|
$spinner.removeClass(spinnerSavedClass).addClass('fa fa-fw fa-spin fa-spinner');
|
||
|
$.getJSON(jsonURL, function(data) {
|
||
|
if(data.add) {
|
||
|
var $li = $("<li class='add'><a href='" + data.url + data.add.url + "'><i class='fa fa-fw fa-" + data.add.icon + "'></i>" + data.add.label + "</a></li>");
|
||
|
$ul.append($li);
|
||
|
}
|
||
|
// populate the retrieved items
|
||
|
$.each(data.list, function(n) {
|
||
|
var icon = '';
|
||
|
// if(this.icon) icon = "<i class='fa fa-fw fa-" + this.icon + "'></i>";
|
||
|
var url = this.url.indexOf('/') === 0 ? this.url : data.url + this.url;
|
||
|
var $li = $("<li><a style='white-space:nowrap' href='" + url + "'>" + icon + this.label + "</a></li>");
|
||
|
if(typeof this.className != "undefined" && this.className && this.className.length) $li.addClass(this.className);
|
||
|
$ul.append($li);
|
||
|
});
|
||
|
$spinner.removeClass('fa-spin fa-spinner').addClass(spinnerSavedClass);
|
||
|
if(data.icon.length > 0) $spinner.removeClass('fa-bolt').addClass('fa-' + data.icon);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}).on('mouseover', function() {
|
||
|
var $this = $(this);
|
||
|
if($this.parent().hasClass('quicklinks-open')) return;
|
||
|
$this.addClass('hover-temp');
|
||
|
clearTimeout(quicklinkTimer);
|
||
|
quicklinkTimer = setTimeout(function() {
|
||
|
if($this.parent().hasClass('quicklinks-open')) return;
|
||
|
if($this.hasClass('hover-temp')) $this.trigger('click');
|
||
|
}, 500);
|
||
|
|
||
|
}).on('mouseout', function() {
|
||
|
$(this).removeClass('hover-temp');
|
||
|
});
|
||
|
|
||
|
$(".quicklink-close").on('click', function(){
|
||
|
$(this).parent().removeClass('quicklinks-open');
|
||
|
$(this).closest('ul.quicklinks').hide().prev('a').removeClass('quicklinks-open');
|
||
|
$('.quicklink-open').removeClass('active');
|
||
|
$('#main-nav .current').removeClass('no-arrow');
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
$('#main-nav .parent').each(function(){
|
||
|
var myHref= $(this).attr('href');
|
||
|
if(url.match(myHref)) {
|
||
|
$(this).next('ul').show();
|
||
|
$(this).addClass('open');
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Clone a button at the bottom to the top
|
||
|
*
|
||
|
*/
|
||
|
setupCloneButton: function() {
|
||
|
// no head_button in modal view
|
||
|
if($("body").is(".modal")) return;
|
||
|
|
||
|
// if there are buttons in the format "a button" without ID attributes, copy them into the masthead
|
||
|
// or buttons in the format button.head_button_clone with an ID attribute.
|
||
|
// var $buttons = $("#content a[id=''] button[id=''], #content button.head_button_clone[id!='']");
|
||
|
var $buttons = $("button.pw-head-button, button.head_button_clone");
|
||
|
|
||
|
// don't continue if no buttons here or if we're in IE
|
||
|
if($buttons.length == 0) return; // || $.browser.msie) return;
|
||
|
|
||
|
var $head = $("<div id='head_button'></div>").prependTo("#headline").show();
|
||
|
$buttons.each(function() {
|
||
|
var $t = $(this);
|
||
|
var $a = $t.parent('a');
|
||
|
var $button;
|
||
|
if($a.length) {
|
||
|
$button = $t.parent('a').clone();
|
||
|
//$head.prepend($button);
|
||
|
$head.append($button);
|
||
|
// } else if($t.is('.head_button_clone')) {
|
||
|
} else if($t.hasClass('head_button_clone') || $t.hasClass('pw-head-button')) {
|
||
|
$button = $t.clone();
|
||
|
$button.attr('data-from_id', $t.attr('id')).attr('id', $t.attr('id') + '_copy');
|
||
|
//$a = $("<a></a>").attr('href', '#');
|
||
|
$button.on('click', function() {
|
||
|
$("#" + $(this).attr('data-from_id')).trigger('click');
|
||
|
return false;
|
||
|
});
|
||
|
// $head.append($a.append($button));
|
||
|
//$head.prepend($a.append($button));
|
||
|
$head.prepend($button);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Make the site search use autocomplete
|
||
|
*
|
||
|
*/
|
||
|
setupSearch: function() {
|
||
|
|
||
|
$.widget( "custom.adminsearchautocomplete", $.ui.autocomplete, {
|
||
|
_renderMenu: function(ul, items) {
|
||
|
var that = this;
|
||
|
var currentType = "";// add an id to the menu for css styling
|
||
|
ul.attr('id', 'ProcessPageSearchAutocomplete');
|
||
|
// Loop over each menu item and customize the list item's html.
|
||
|
$.each(items, function(index, item) {
|
||
|
// Menu categories don't get linked so that they don't receive
|
||
|
// keyboard focus.
|
||
|
if (item.type != currentType) {
|
||
|
// ul.append("<li class='ui-widget-header'><a>" + item.type + "</a></li>" );
|
||
|
$("<li>" + item.type + "</li>").addClass("ui-widget-header").appendTo(ul);
|
||
|
currentType = item.type;
|
||
|
}
|
||
|
that._renderItemData(ul, item);
|
||
|
});
|
||
|
},
|
||
|
_renderItem: function(ul, item) {
|
||
|
if(item.label == item.template) item.template = '';
|
||
|
var $label = $("<span></span>").text(item.label).css('margin-right', '3px');
|
||
|
if(item.unpublished) $label.css('text-decoration', 'line-through');
|
||
|
if(item.hidden) $label.css('opacity', 0.7);
|
||
|
if(item.icon.length) {
|
||
|
var $icon = $('<i></i>').addClass('fa fa-fw fa-' + item.icon).css('margin-right', '2px');
|
||
|
$label.prepend($icon);
|
||
|
}
|
||
|
var $a = $("<a></a>")
|
||
|
.attr('href', item.edit_url)
|
||
|
.attr('title', item.tip)
|
||
|
.append($label)
|
||
|
.append($("<small class='uk-text-muted'></small>").text(item.template));
|
||
|
if(item.edit_url == '#' || !item.edit_url.length) $a.removeAttr('href');
|
||
|
return $("<li></li>").append($a).appendTo(ul);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var $input = $("#ProcessPageSearchQuery");
|
||
|
var $status = $("#ProcessPageSearchStatus");
|
||
|
|
||
|
$input.adminsearchautocomplete({
|
||
|
minLength: 2,
|
||
|
position: { my : "right top", at: "right bottom" },
|
||
|
search: function(event, ui) {
|
||
|
$status.html("<i class='fa fa-spinner fa-spin'></i>");
|
||
|
},
|
||
|
source: function(request, response) {
|
||
|
var url = $input.parents('form').attr('action') + '?q=' + request.term;
|
||
|
$.getJSON(url, function(data) {
|
||
|
var len = data.matches.length;
|
||
|
if(len < data.total) $status.text(data.matches.length + '/' + data.total);
|
||
|
else $status.text(len);
|
||
|
response($.map(data.matches, function(item) {
|
||
|
return {
|
||
|
label: item.title,
|
||
|
value: item.title,
|
||
|
page_id: item.id,
|
||
|
template: item.template_label ? item.template_label : '',
|
||
|
edit_url: item.editUrl,
|
||
|
type: item.type,
|
||
|
tip: item.tip,
|
||
|
unpublished: (typeof item.unpublished != "undefined" ? item.unpublished : false),
|
||
|
hidden: (typeof item.hidden != "undefined" ? item.hidden : false),
|
||
|
locked: (typeof item.locked != "undefined" ? item.locked : false),
|
||
|
icon: (typeof item.icon != "undefined" ? item.icon : '')
|
||
|
}
|
||
|
}));
|
||
|
});
|
||
|
},
|
||
|
select: function(event, ui) {
|
||
|
// follow the link if the Enter/Return key is tapped
|
||
|
if(typeof event.key != 'undefined') {
|
||
|
event.preventDefault();
|
||
|
if(ui.item.edit_url == '#' || !ui.item.edit_url.length) return false;
|
||
|
window.location = ui.item.edit_url;
|
||
|
}
|
||
|
}
|
||
|
}).on('blur', function() {
|
||
|
$status.text('');
|
||
|
});
|
||
|
|
||
|
// Search toggle
|
||
|
var $search = $("#search");
|
||
|
|
||
|
$input.on('keypress keydown keyup', function(event){
|
||
|
if(event.keyCode == 13) {
|
||
|
event.preventDefault(); // Don't submit to the search page on Enter Key
|
||
|
}
|
||
|
|
||
|
if(event.keyCode == 40) {
|
||
|
// down arrow
|
||
|
$input.data('no-close', true);
|
||
|
}
|
||
|
|
||
|
if(event.keyCode == 38 && !$input.data('no-close')) {
|
||
|
// up arrow
|
||
|
$search.removeClass("open");
|
||
|
$(this).val(); // close search on arrow up
|
||
|
$input.trigger('blur');
|
||
|
$('#ProcessPageSearchAutocomplete').hide().html('');
|
||
|
}
|
||
|
|
||
|
if(event.keyCode == 8) {
|
||
|
if (ProcessWire.trim($(this).val()) == ''){
|
||
|
$status.text(''); // remove status if nothing in the input.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
});
|
||
|
|
||
|
$(".search-toggle").on("click", function() {
|
||
|
if (!$search.hasClass('open')){
|
||
|
$('#masthead').find('ul.open').removeClass('open');
|
||
|
$input.trigger('focus');
|
||
|
} else {
|
||
|
$input.trigger('blur');
|
||
|
}
|
||
|
$search.toggleClass("open");
|
||
|
$input.val("");
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
$(".search-close").on("click", function() {
|
||
|
$search.removeClass("open");
|
||
|
$input.val("");
|
||
|
$input.trigger('blur');
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
|
||
|
$('body').on('click', function(event){
|
||
|
|
||
|
if (!$search.hasClass('open')) return; // not open, so do nothing
|
||
|
|
||
|
var hide = true;
|
||
|
var exclude = ['ProcessPageSearchAutocomplete', 'ProcessPageSearchForm', 'search'];
|
||
|
if ($.inArray(event.target.id, exclude) != -1) return; // stay open for these targets
|
||
|
|
||
|
// check if the event.target was a child element of any of any items in the exclude array.
|
||
|
$.each(exclude, function(key, val){
|
||
|
if ($(event.target).closest('#' + val).length) hide = false;
|
||
|
});
|
||
|
|
||
|
if (hide){
|
||
|
$search.removeClass("open");
|
||
|
$input.val("");
|
||
|
$input.trigger('blur');
|
||
|
}
|
||
|
});
|
||
|
|
||
|
},
|
||
|
|
||
|
setupDropdowns: function() {
|
||
|
|
||
|
$('#masthead li.pw-dropdown > a').on('click', function(e){
|
||
|
$(this).next("ul").toggleClass('open');
|
||
|
$(this).parent().siblings().find('ul.open').removeClass('open');
|
||
|
return false;
|
||
|
});
|
||
|
|
||
|
$('#masthead li.pw-dropdown > ul li a').on('click', function(e){
|
||
|
e.stopPropagation();
|
||
|
});
|
||
|
|
||
|
$(document).on('click', function(){
|
||
|
$('#masthead li.pw-dropdown ul').removeClass('open');
|
||
|
});
|
||
|
|
||
|
},
|
||
|
|
||
|
setupSideBarToggle: function() {
|
||
|
$(".main-nav-toggle").on("click", function(){
|
||
|
ProcessWireAdminTheme.setupSideBarState(true);
|
||
|
return false;
|
||
|
});
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @todo: Refactor this and the corresponding CSS. (Renobird)
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
setupSideBarState: function(click, state) {
|
||
|
if($("body").hasClass("id-23") || $("body").hasClass("modal")) return false;
|
||
|
var name = 'pw_sidebar_state';
|
||
|
var state = state || localStorage.getItem(name);
|
||
|
var toggle = $(".main-nav-toggle");
|
||
|
var sidebar = $("#sidebar");
|
||
|
var main = $("#main");
|
||
|
var masthead = $("#masthead");
|
||
|
var branding = $("#branding");
|
||
|
var bug = $("#NotificationBug");
|
||
|
var elems = toggle.add(sidebar).add(main).add(masthead).add(branding).add(bug);
|
||
|
|
||
|
if(state === null && $(window).width() >= 690) localStorage.setItem(name,"open");
|
||
|
|
||
|
if(!click && $(window).width() < 690){
|
||
|
localStorage.setItem(name,"closed");
|
||
|
state = 'closed';
|
||
|
}
|
||
|
|
||
|
if(click == true){
|
||
|
if(state == 'open'){
|
||
|
localStorage.setItem(name, "closed");
|
||
|
elems.addClass('closed');
|
||
|
} else if(state == 'closed'){
|
||
|
localStorage.setItem(name, "open");
|
||
|
elems.removeClass('closed');
|
||
|
}
|
||
|
|
||
|
sidebar.removeClass('hide');
|
||
|
branding.removeClass('hide');
|
||
|
main.removeClass('full');
|
||
|
masthead.removeClass('full');
|
||
|
toggle.removeClass('full');
|
||
|
|
||
|
} else if(state == 'closed'){
|
||
|
elems.addClass('closed');
|
||
|
sidebar.addClass('hide');
|
||
|
branding.addClass('hide');
|
||
|
main.addClass('full');
|
||
|
masthead.addClass('full');
|
||
|
toggle.addClass('full');
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Give a notice to IE versions we don't support
|
||
|
*
|
||
|
*/
|
||
|
browserCheck: function() {
|
||
|
// no longer used
|
||
|
}
|
||
|
};
|
||
|
|
||
|
$(document).ready(function() {
|
||
|
ProcessWireAdminTheme.init();
|
||
|
});
|