[10/07/2025] Les coupons créés dynamiquement seront automatiquement valables sur les produits en promo, suite à pb rencontré par Alain
@@ -1 +1 @@
|
||||
9717a11a780f61e8175c3f93766112d8
|
||||
8703ff7296e8df1b0df573080378a0c6
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 953 B After Width: | Height: | Size: 953 B |
|
Before Width: | Height: | Size: 953 B |
|
Before Width: | Height: | Size: 353 B After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 303 B |
|
Before Width: | Height: | Size: 100 B After Width: | Height: | Size: 100 B |
|
After Width: | Height: | Size: 221 B |
|
Before Width: | Height: | Size: 100 B |
|
Before Width: | Height: | Size: 353 B |
@@ -1,617 +0,0 @@
|
||||
// Avoid `console` errors in browsers that lack a console.
|
||||
(function() {
|
||||
var method;
|
||||
var noop = function () {};
|
||||
var methods = [
|
||||
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
|
||||
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
|
||||
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
|
||||
'timeStamp', 'trace', 'warn'
|
||||
];
|
||||
var length = methods.length;
|
||||
var console = (window.console = window.console || {});
|
||||
|
||||
while (length--) {
|
||||
method = methods[length];
|
||||
|
||||
// Only stub undefined methods.
|
||||
if (!console[method]) {
|
||||
console[method] = noop;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
|
||||
var pseManager = (function($){
|
||||
|
||||
// cache dom elements
|
||||
var manager = {};
|
||||
var $pse = {};
|
||||
|
||||
function init(){
|
||||
$pse = {
|
||||
"id": $("#pse-id"),
|
||||
"product": $("#product"),
|
||||
"name": $("#pse-name"),
|
||||
"ref": $("#pse-ref"),
|
||||
"ean": $("#pse-ean"),
|
||||
"availability": $("#pse-availability"),
|
||||
"validity": $("#pse-validity"),
|
||||
"quantity": $("#quantity"),
|
||||
"promo": $("#pse-promo"),
|
||||
"new": $("#pse-new"),
|
||||
"weight": $("#pse-weight"),
|
||||
"price": $("#pse-price"),
|
||||
"priceOld": $("#pse-price-old"),
|
||||
"submit": $("#pse-submit"),
|
||||
"options": {},
|
||||
"pseId": null,
|
||||
"useFallback": false,
|
||||
"fallback": $("#pse-options .pse-fallback")
|
||||
};
|
||||
}
|
||||
|
||||
function buildProductForm() {
|
||||
var pse = null,
|
||||
combinationId = null,
|
||||
combinationValue = null,
|
||||
combinationValueId = null,
|
||||
combinations = null,
|
||||
combinationName = [],
|
||||
i;
|
||||
|
||||
// initialization for the first default pse
|
||||
$pse.pseId = $pse.id.val();
|
||||
|
||||
if (PSE_COUNT > 1) {
|
||||
// Use fallback method ?
|
||||
$pse.useFallback = useFallback();
|
||||
|
||||
if ($pse.useFallback) {
|
||||
$("#pse-options .option-option").remove();
|
||||
|
||||
for (pse in PSE){
|
||||
combinations = PSE[pse].combinations;
|
||||
combinationName = [];
|
||||
for (i = 0; i < combinations.length; i++){
|
||||
combinationName.push(PSE_COMBINATIONS_VALUE[combinations[i]][0]);
|
||||
}
|
||||
$pse.fallback
|
||||
.append("<option value='" + pse + "'>"
|
||||
+ combinationName.join(', ') + "</option>");
|
||||
}
|
||||
|
||||
$("#pse-options .pse-fallback").on("change",function(){
|
||||
updateProductForm();
|
||||
});
|
||||
|
||||
} else {
|
||||
$("#pse-options .option-fallback").remove();
|
||||
|
||||
// get the select for options
|
||||
$("#pse-options .pse-option").each(function(){
|
||||
var $option = $(this);
|
||||
if ( $option.data("attribute") in PSE_COMBINATIONS){
|
||||
$pse['options'][$option.data("attribute")] = $option;
|
||||
$option.on("change", updateProductForm);
|
||||
} else {
|
||||
// not affected to this product -> remove
|
||||
$option.closest(".option").remove();
|
||||
}
|
||||
});
|
||||
|
||||
// build select
|
||||
for (combinationValueId in PSE_COMBINATIONS_VALUE) {
|
||||
combinationValue = PSE_COMBINATIONS_VALUE[combinationValueId];
|
||||
$pse.options[combinationValue[1]]
|
||||
.append("<option value='" + combinationValueId + "'>"
|
||||
+ combinationValue[0] + "</option>");
|
||||
}
|
||||
|
||||
setPseForm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setPseForm(id) {
|
||||
var i = 0,
|
||||
pse = null,
|
||||
combinationValueId;
|
||||
pse = PSE[id || $pse.pseId];
|
||||
if ($pse.useFallback) {
|
||||
$pse.fallbak.val(pse.id);
|
||||
} else {
|
||||
for (var i=0; i<pse.combinations.length; i++){
|
||||
combinationValueId = pse.combinations[i];
|
||||
$pse['options'][PSE_COMBINATIONS_VALUE[combinationValueId][1]].val(pse.combinations[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateProductForm() {
|
||||
var pseId = null,
|
||||
selection;
|
||||
|
||||
if (PSE_COUNT > 1) {
|
||||
|
||||
if ($pse.useFallback) {
|
||||
pseId = $pse.fallback.val();
|
||||
} else {
|
||||
// get form data
|
||||
selection = getFormSelection();
|
||||
// get the pse
|
||||
pseId = pseExist(selection);
|
||||
|
||||
if ( ! pseId ) {
|
||||
// not exists, revert
|
||||
displayNotice();
|
||||
setPseForm();
|
||||
} else {
|
||||
$pse.validity.hide();
|
||||
}
|
||||
}
|
||||
|
||||
$pse.id.val(pseId);
|
||||
$pse.pseId = pseId;
|
||||
}
|
||||
|
||||
// Update UI
|
||||
updateProductUI();
|
||||
}
|
||||
|
||||
function displayNotice() {
|
||||
var $validity = $pse.validity;
|
||||
$validity.show('fast', function(){
|
||||
setTimeout(function(){
|
||||
$validity.hide('fast');
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
|
||||
function updateProductUI() {
|
||||
var pse = PSE[$pse.pseId],
|
||||
name = [],
|
||||
pseValueId,
|
||||
i
|
||||
;
|
||||
|
||||
$pse.ref.html(pse.ref);
|
||||
// $pse.ean.html(pse.ean);
|
||||
// name
|
||||
if (PSE_COUNT > 1) {
|
||||
|
||||
for (i = 0; i < pse.combinations.length; i++){
|
||||
pseValueId = pse.combinations[i]
|
||||
name.push(
|
||||
//PSE_COMBINATIONS[PSE_COMBINATIONS_VALUE[pseValueId][1]].name +
|
||||
//":" +
|
||||
PSE_COMBINATIONS_VALUE[pseValueId][0]
|
||||
)
|
||||
}
|
||||
|
||||
$pse.name.html(" - " + name.join(", ") + "");
|
||||
}
|
||||
|
||||
// promo
|
||||
if (pse.isPromo) {
|
||||
$pse.product.addClass("product--is-promo");
|
||||
} else {
|
||||
$pse.product.removeClass("product--is-promo");
|
||||
}
|
||||
|
||||
// new
|
||||
if (pse.isNew) {
|
||||
$pse.product.addClass("product--is-new");
|
||||
} else {
|
||||
$pse.product.removeClass("product--is-new");
|
||||
}
|
||||
|
||||
// availability
|
||||
if (pse.quantity > 0 || ! PSE_CHECK_AVAILABILITY) {
|
||||
$pse.availability
|
||||
.removeClass("out-of-stock")
|
||||
.addClass("in-stock")
|
||||
.attr("href", "http://schema.org/InStock");
|
||||
|
||||
if (parseInt($pse.quantity.val()) > pse.quantity){
|
||||
$pse.quantity.val(pse.quantity);
|
||||
}
|
||||
|
||||
if (PSE_CHECK_AVAILABILITY) {
|
||||
$pse.quantity.attr("max", pse.quantity);
|
||||
} else {
|
||||
$pse.quantity.attr("max", PSE_DEFAULT_AVAILABLE_STOCK);
|
||||
}
|
||||
$pse.submit.prop("disabled", false);
|
||||
|
||||
} else {
|
||||
$pse.availability.removeClass("in-stock")
|
||||
.addClass("out-of-stock")
|
||||
.attr("href", "http://schema.org/OutOfStock");
|
||||
|
||||
$pse.submit.prop("disabled", true);
|
||||
}
|
||||
|
||||
// price
|
||||
if (pse.isPromo){
|
||||
$pse.priceOld.html(pse.price);
|
||||
$pse.price.html(pse.promo);
|
||||
} else {
|
||||
$pse.priceOld.html("");
|
||||
$pse.price.html(pse.price);
|
||||
}
|
||||
}
|
||||
|
||||
function pseExist(selection) {
|
||||
var pseId,
|
||||
pse = null,
|
||||
combinations,
|
||||
i,
|
||||
j,
|
||||
existCombination;
|
||||
|
||||
for (pse in PSE){
|
||||
pseId = pse;
|
||||
combinations = PSE[pse].combinations;
|
||||
for (i = 0; i < selection.length; i++){
|
||||
existCombination = false;
|
||||
for (j = 0; j < combinations.length; j++){
|
||||
if (selection[i] == combinations[j]){
|
||||
existCombination = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existCombination === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existCombination) {
|
||||
return pseId;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function useFallback() {
|
||||
var pse = null,
|
||||
count = -1,
|
||||
pseCount = 0,
|
||||
combinations,
|
||||
i;
|
||||
|
||||
for (pse in PSE){
|
||||
combinations = PSE[pse].combinations;
|
||||
pseCount = 0;
|
||||
for (i = 0; i < combinations.length; i++) {
|
||||
pseCount += PSE_COMBINATIONS_VALUE[combinations[i]][1];
|
||||
}
|
||||
if (count == -1){
|
||||
count = pseCount;
|
||||
} else if (count != pseCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return (count <= 0);
|
||||
}
|
||||
|
||||
function getFormSelection() {
|
||||
var selection = [],
|
||||
combinationId;
|
||||
|
||||
for (combinationId in $pse.options){
|
||||
selection.push($pse.options[combinationId].val());
|
||||
}
|
||||
|
||||
return selection;
|
||||
}
|
||||
|
||||
manager.load = function(){
|
||||
init();
|
||||
buildProductForm();
|
||||
updateProductForm();
|
||||
}
|
||||
|
||||
return manager;
|
||||
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/* JQUERY PREVENT CONFLICT */
|
||||
(function ($) {
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
callback Function ------------------------------------------------ */
|
||||
var confirmCallback = {
|
||||
'address.delete': function ($elm) {
|
||||
$.post($elm.attr('href'), function (data) {
|
||||
if (data.success) {
|
||||
$elm.closest('tr').remove();
|
||||
} else {
|
||||
bootbox.alert(data.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
onLoad Function ------------------------------------------------- */
|
||||
$(document).ready(function () {
|
||||
|
||||
// Loader
|
||||
var $loader = $('<div class="loader"></div>');
|
||||
$('body').append($loader);
|
||||
|
||||
// Display loader if we do ajax call
|
||||
$(document)
|
||||
.ajaxStart(function () { $loader.show(); })
|
||||
.ajaxStop(function () { $loader.hide(); })
|
||||
.ajaxError(function () { $loader.hide(); });
|
||||
|
||||
// Check if the size of the window is appropriate for ajax
|
||||
var doAjax = ($(window).width() > 768) ? true : false;
|
||||
|
||||
// Main Navigation Hover
|
||||
$('.nav-main')
|
||||
.on('click.subnav', '[data-toggle=dropdown]', function (event) {
|
||||
if ($(this).parent().hasClass('open') && $(this).is(event.target)) { return false; }
|
||||
})
|
||||
.on('mouseenter.subnav', '.dropdown', function () {
|
||||
if ($(this).hasClass('open')) { return; }
|
||||
|
||||
$(this).addClass('open');
|
||||
})
|
||||
.on('mouseleave.subnav', '.dropdown', function () {
|
||||
var $this = $(this);
|
||||
|
||||
if (!$this.hasClass('open')) { return; }
|
||||
|
||||
//This will check if an input child has focus. If no then remove class open
|
||||
if ($this.find(":input:focus").length === 0) {
|
||||
$this.removeClass('open');
|
||||
} else {
|
||||
$this.find(":input:focus").one('blur', function () {
|
||||
$this.trigger('mouseleave.subnav');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Tooltip
|
||||
$('body').tooltip({
|
||||
selector: '[data-toggle=tooltip]'
|
||||
});
|
||||
|
||||
// Confirm Dialog
|
||||
$(document).on('click.confirm', '[data-confirm]', function () {
|
||||
var $this = $(this),
|
||||
href = $this.attr('href'),
|
||||
callback = $this.attr('data-confirm-callback'),
|
||||
title = $this.attr('data-confirm') !== '' ? $this.attr('data-confirm') : 'Are you sure?';
|
||||
|
||||
bootbox.confirm(title, function (confirm) {
|
||||
if (confirm) {
|
||||
//Check if callback and if it's a function
|
||||
if (callback && $.isFunction(confirmCallback[callback])) {
|
||||
confirmCallback[callback]($this);
|
||||
} else {
|
||||
if (href) {
|
||||
window.location.href = href;
|
||||
} else {
|
||||
// If forms
|
||||
var $form = $this.closest("form");
|
||||
if ($form.size() > 0) {
|
||||
$form.submit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Product Quick view Dialog
|
||||
$(document).on('click.product-quickview', '.product-quickview', function () {
|
||||
if (doAjax) {
|
||||
$.get(this.href,
|
||||
function (data) {
|
||||
// Hide all currently active bootbox dialogs
|
||||
bootbox.hideAll();
|
||||
// Show dialog
|
||||
bootbox.dialog({
|
||||
message : $("#product",data),
|
||||
onEscape: function() {
|
||||
bootbox.hideAll();
|
||||
}
|
||||
});
|
||||
window.pseManager.load();
|
||||
}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
// Product AddtoCard - OnSubmit
|
||||
if (typeof window.PSE_FORM !== "undefined"){
|
||||
window.pseManager.load();
|
||||
}
|
||||
|
||||
$(document).on('submit.form-product', '.form-product', function () {
|
||||
if (doAjax) {
|
||||
var url_action = $(this).attr("action"),
|
||||
product_id = $("input[name$='product_id']",this).val(),
|
||||
pse_id = $("input#pse-id",this).val();
|
||||
|
||||
$.ajax({type: "POST", data: $(this).serialize(), url: url_action,
|
||||
success: function(data){
|
||||
$(".cart-container").html($(data).html());
|
||||
$.ajax({url:"ajax/addCartMessage", data:{ product_id: product_id, pse_id: pse_id },
|
||||
success: function (data) {
|
||||
// Hide all currently active bootbox dialogs
|
||||
bootbox.hideAll();
|
||||
// Show dialog
|
||||
bootbox.dialog({
|
||||
message : data,
|
||||
onEscape: function() {
|
||||
bootbox.hideAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function (e) {
|
||||
console.log('Error.', e);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return;
|
||||
});
|
||||
|
||||
|
||||
// Toolbar
|
||||
var $category_products = $ ('#category-products');
|
||||
if ($category_products.size() > 0) {
|
||||
var $parent = $category_products.parent();
|
||||
|
||||
$parent.on('click.view-mode', '[data-toggle=view]', function () {
|
||||
if (($(this).hasClass('btn-grid') && $parent.hasClass('grid')) || ($(this).hasClass('btn-list') && $parent.hasClass('list'))) { return; }
|
||||
|
||||
// Add loader effect
|
||||
$loader.show();
|
||||
setTimeout(function () { $parent.toggleClass('grid').toggleClass('list'); $loader.hide(); }, 400);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Login
|
||||
var $form_login = $('#form-login');
|
||||
if ($form_login.size() > 0) {
|
||||
$form_login.on('change.account', ':radio', function () {
|
||||
if ($(this).val() === '0') {
|
||||
$('#password', $form_login).val('').prop('disabled', true); // Disabled (new customer)
|
||||
}
|
||||
else {
|
||||
$('#password', $form_login).prop('disabled', false); // Enabled
|
||||
}
|
||||
}).find(':radio:checked').trigger('change.account');
|
||||
}
|
||||
|
||||
// Mini Newsletter Subscription
|
||||
var $form_newsletter = $('#form-newsletter-mini');
|
||||
if ($form_newsletter.size() > 0) {
|
||||
$form_newsletter.on('submit.newsletter', function () {
|
||||
|
||||
$.ajax({
|
||||
url: $(this).attr('action'),
|
||||
type: $(this).attr('method'),
|
||||
data: $(this).serialize(),
|
||||
dataType: 'json',
|
||||
success: function (json) {
|
||||
var $msg = '';
|
||||
if (json.success) {
|
||||
$msg = json.message;
|
||||
} else {
|
||||
$msg = json.message;
|
||||
}
|
||||
bootbox.alert($msg);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Forgot Password
|
||||
/*
|
||||
var $forgot_password = $('.forgot-password', $form_login);
|
||||
if($forgot_password.size() > 0) {
|
||||
$forgot_password.popover({
|
||||
html : true,
|
||||
title: 'Forgot Password',
|
||||
content: function() {
|
||||
return $('#form-forgotpassword').html();
|
||||
}
|
||||
}).on('click.btn-forgot', function () {
|
||||
|
||||
$('.btn-forgot').click(function () {
|
||||
alert('click form');
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.btn-close').click(function () {
|
||||
$forgot_password.popover('hide');
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
//.Form Filters
|
||||
$('#form-filters').each(function () {
|
||||
var $form = $(this);
|
||||
|
||||
$form
|
||||
.on('change.filter', ':checkbox', function () {
|
||||
$loader.show();
|
||||
$form.submit();
|
||||
})
|
||||
.find('.group-btn > .btn').addClass('sr-only');
|
||||
});
|
||||
|
||||
// Product details Thumbnails
|
||||
$(document).on('click.thumbnails', '#product-thumbnails .thumbnail', function () {
|
||||
if ($(this).hasClass('active')) { return false; }
|
||||
|
||||
var $productGallery = $(this).closest("#product-gallery");
|
||||
$('.product-image > img', $productGallery).attr('src',$(this).attr('href'));
|
||||
$('.thumbnail', $productGallery).removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Show Carousel control if needed
|
||||
$('#product-gallery').each(function () {
|
||||
if ($('.item', this).size() > 1) {
|
||||
$('#product-thumbnails', this).carousel({interval: false}).find('.carousel-control').show();
|
||||
}
|
||||
});
|
||||
|
||||
// Payment Method
|
||||
$('#payment-method').each(function () {
|
||||
var $label = $('label', this);
|
||||
$label.on('change', ':radio', function () {
|
||||
$label.removeClass('active');
|
||||
$label.filter('[for="' + $(this).attr('id') + '"]').addClass('active');
|
||||
}).filter(':has(:checked)').addClass('active');
|
||||
});
|
||||
|
||||
// Apply validation
|
||||
$('#form-contact, #form-register, #form-address').validate({
|
||||
highlight: function (element) {
|
||||
$(element).closest('.form-group').addClass('has-error');
|
||||
},
|
||||
unhighlight: function (element) {
|
||||
$(element).closest('.form-group').removeClass('has-error');
|
||||
},
|
||||
errorElement: 'span',
|
||||
errorClass: 'help-block'
|
||||
});
|
||||
|
||||
// Toolbar filter
|
||||
$('#content').on('change.toolbarfilter', '#limit-top, #sortby-top', function () {
|
||||
window.location = $(this).val();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
||||
|
||||
@@ -1,604 +0,0 @@
|
||||
/**
|
||||
* bootbox.js v4.0.0
|
||||
*
|
||||
* http://bootboxjs.com/license.txt
|
||||
*/
|
||||
// @see https://github.com/makeusabrew/bootbox/issues/71
|
||||
window.bootbox = window.bootbox || (function init($, undefined) {
|
||||
"use strict";
|
||||
|
||||
// the base DOM structure needed to create a modal
|
||||
var templates = {
|
||||
dialog:
|
||||
"<div class='bootbox modal' tabindex='-1' role='dialog'>" +
|
||||
"<div class='modal-dialog'>" +
|
||||
"<div class='modal-content'>" +
|
||||
"<div class='modal-body'><div class='bootbox-body'></div></div>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
"</div>",
|
||||
header:
|
||||
"<div class='modal-header'>" +
|
||||
"<h4 class='modal-title'></h4>" +
|
||||
"</div>",
|
||||
footer:
|
||||
"<div class='modal-footer'></div>",
|
||||
closeButton:
|
||||
"<button type='button' class='bootbox-close-button close'>×</button>",
|
||||
form:
|
||||
"<form class='bootbox-form'></form>",
|
||||
inputs: {
|
||||
text:
|
||||
"<input class='bootbox-input form-control' autocomplete=off type=text />"
|
||||
}
|
||||
};
|
||||
|
||||
// cache a reference to the jQueryfied body element
|
||||
var appendTo = $("body");
|
||||
|
||||
var defaults = {
|
||||
// default language
|
||||
locale: "en",
|
||||
// show backdrop or not
|
||||
backdrop: true,
|
||||
// animate the modal in/out
|
||||
animate: true,
|
||||
// additional class string applied to the top level dialog
|
||||
className: null,
|
||||
// whether or not to include a close button
|
||||
closeButton: true,
|
||||
// show the dialog immediately by default
|
||||
show: true
|
||||
};
|
||||
|
||||
// our public object; augmented after our private API
|
||||
var exports = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function _t(key) {
|
||||
var locale = locales[defaults.locale];
|
||||
return locale ? locale[key] : locales.en[key];
|
||||
}
|
||||
|
||||
function processCallback(e, dialog, callback) {
|
||||
e.preventDefault();
|
||||
|
||||
// by default we assume a callback will get rid of the dialog,
|
||||
// although it is given the opportunity to override this
|
||||
|
||||
// so, if the callback can be invoked and it *explicitly returns false*
|
||||
// then we'll set a flag to keep the dialog active...
|
||||
var preserveDialog = $.isFunction(callback) && callback(e) === false;
|
||||
|
||||
// ... otherwise we'll bin it
|
||||
if (!preserveDialog) {
|
||||
dialog.modal("hide");
|
||||
}
|
||||
}
|
||||
|
||||
function getKeyLength(obj) {
|
||||
// @TODO defer to Object.keys(x).length if available?
|
||||
var k, t = 0;
|
||||
for (k in obj) {
|
||||
t ++;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
function each(collection, iterator) {
|
||||
var index = 0;
|
||||
$.each(collection, function(key, value) {
|
||||
iterator(key, value, index++);
|
||||
});
|
||||
}
|
||||
|
||||
function sanitize(options) {
|
||||
var buttons;
|
||||
var total;
|
||||
|
||||
|
||||
if (typeof options !== "object") {
|
||||
throw new Error("Please supply an object of options");
|
||||
}
|
||||
|
||||
if (!options.message) {
|
||||
throw new Error("Please specify a message");
|
||||
}
|
||||
|
||||
// make sure any supplied options take precedence over defaults
|
||||
options = $.extend({}, defaults, options);
|
||||
|
||||
if (!options.buttons) {
|
||||
options.buttons = {};
|
||||
}
|
||||
|
||||
// we only support Bootstrap's "static" and false backdrop args
|
||||
// supporting true would mean you could dismiss the dialog without
|
||||
// explicitly interacting with it
|
||||
options.backdrop = options.backdrop ? "static" : false;
|
||||
|
||||
buttons = options.buttons;
|
||||
|
||||
total = getKeyLength(buttons);
|
||||
|
||||
each(buttons, function(key, button, index) {
|
||||
|
||||
if ($.isFunction(button)) {
|
||||
// short form, assume value is our callback. Since button
|
||||
// isn't an object it isn't a reference either so re-assign it
|
||||
button = buttons[key] = {
|
||||
callback: button
|
||||
};
|
||||
}
|
||||
|
||||
// before any further checks make sure by now button is the correct type
|
||||
if ($.type(button) !== "object") {
|
||||
throw new Error("button with key " + key + " must be an object");
|
||||
}
|
||||
|
||||
if (!button.label) {
|
||||
// the lack of an explicit label means we'll assume the key is good enough
|
||||
button.label = key;
|
||||
}
|
||||
|
||||
if (!button.className) {
|
||||
if (total <= 2 && index === total-1) {
|
||||
// always add a primary to the main option in a two-button dialog
|
||||
button.className = "btn-primary";
|
||||
} else {
|
||||
button.className = "btn-default";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function mapArguments(args, properties) {
|
||||
var argn = args.length;
|
||||
var options = {};
|
||||
|
||||
if (argn < 1 || argn > 2) {
|
||||
throw new Error("Invalid argument length");
|
||||
}
|
||||
|
||||
if (argn === 2 || typeof args[0] === "string") {
|
||||
options[properties[0]] = args[0];
|
||||
options[properties[1]] = args[1];
|
||||
} else {
|
||||
options = args[0];
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function mergeArguments(defaults, args, properties) {
|
||||
return $.extend(true, {}, defaults, mapArguments(args, properties));
|
||||
}
|
||||
|
||||
function mergeButtons(labels, args, properties) {
|
||||
return validateButtons(
|
||||
mergeArguments(createButtons.apply(null, labels), args, properties),
|
||||
labels
|
||||
);
|
||||
}
|
||||
|
||||
function createLabels() {
|
||||
var buttons = {};
|
||||
|
||||
for (var i = 0, j = arguments.length; i < j; i++) {
|
||||
var argument = arguments[i];
|
||||
var key = argument.toLowerCase();
|
||||
var value = argument.toUpperCase();
|
||||
|
||||
buttons[key] = {
|
||||
label: _t(value)
|
||||
};
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
function createButtons() {
|
||||
return {
|
||||
buttons: createLabels.apply(null, arguments)
|
||||
};
|
||||
}
|
||||
|
||||
function validateButtons(options, buttons) {
|
||||
var allowedButtons = {};
|
||||
each(buttons, function(key, value) {
|
||||
allowedButtons[value] = true;
|
||||
});
|
||||
|
||||
each(options.buttons, function(key) {
|
||||
if (allowedButtons[key] === undefined) {
|
||||
throw new Error("button key " + key + " is not allowed (options are " + buttons.join("\n") + ")");
|
||||
}
|
||||
});
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
exports.alert = function() {
|
||||
var options;
|
||||
|
||||
options = mergeButtons(["ok"], arguments, ["message", "callback"]);
|
||||
|
||||
if (options.callback && !$.isFunction(options.callback)) {
|
||||
throw new Error("alert requires callback property to be a function when provided");
|
||||
}
|
||||
|
||||
/**
|
||||
* overrides
|
||||
*/
|
||||
options.buttons.ok.callback = options.onEscape = function() {
|
||||
if ($.isFunction(options.callback)) {
|
||||
return options.callback();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return exports.dialog(options);
|
||||
};
|
||||
|
||||
exports.confirm = function() {
|
||||
var options;
|
||||
|
||||
options = mergeButtons(["cancel", "confirm"], arguments, ["message", "callback"]);
|
||||
|
||||
/**
|
||||
* overrides; undo anything the user tried to set they shouldn't have
|
||||
*/
|
||||
options.buttons.cancel.callback = options.onEscape = function() {
|
||||
return options.callback(false);
|
||||
};
|
||||
|
||||
options.buttons.confirm.callback = function() {
|
||||
return options.callback(true);
|
||||
};
|
||||
|
||||
// confirm specific validation
|
||||
if (!$.isFunction(options.callback)) {
|
||||
throw new Error("confirm requires a callback");
|
||||
}
|
||||
|
||||
return exports.dialog(options);
|
||||
};
|
||||
|
||||
exports.prompt = function() {
|
||||
var options;
|
||||
var defaults;
|
||||
var dialog;
|
||||
var form;
|
||||
var input;
|
||||
var shouldShow;
|
||||
|
||||
// we have to create our form first otherwise
|
||||
// its value is undefined when gearing up our options
|
||||
// @TODO this could be solved by allowing message to
|
||||
// be a function instead...
|
||||
form = $(templates.form);
|
||||
|
||||
defaults = {
|
||||
buttons: createLabels("cancel", "confirm"),
|
||||
value: ""
|
||||
};
|
||||
|
||||
options = validateButtons(
|
||||
mergeArguments(defaults, arguments, ["title", "callback"]),
|
||||
["cancel", "confirm"]
|
||||
);
|
||||
|
||||
// capture the user's show value; we always set this to false before
|
||||
// spawning the dialog to give us a chance to attach some handlers to
|
||||
// it, but we need to make sure we respect a preference not to show it
|
||||
shouldShow = (options.show === undefined) ? true : options.show;
|
||||
|
||||
/**
|
||||
* overrides; undo anything the user tried to set they shouldn't have
|
||||
*/
|
||||
options.message = form;
|
||||
|
||||
options.buttons.cancel.callback = options.onEscape = function() {
|
||||
return options.callback(null);
|
||||
};
|
||||
|
||||
options.buttons.confirm.callback = function() {
|
||||
return options.callback(input.val());
|
||||
};
|
||||
|
||||
options.show = false;
|
||||
|
||||
// prompt specific validation
|
||||
if (!options.title) {
|
||||
throw new Error("prompt requires a title");
|
||||
}
|
||||
|
||||
if (!$.isFunction(options.callback)) {
|
||||
throw new Error("prompt requires a callback");
|
||||
}
|
||||
|
||||
// create the input
|
||||
input = $(templates.inputs.text);
|
||||
input.val(options.value);
|
||||
|
||||
// now place it in our form
|
||||
form.append(input);
|
||||
|
||||
form.on("submit", function(e) {
|
||||
e.preventDefault();
|
||||
// @TODO can we actually click *the* button object instead?
|
||||
// e.g. buttons.confirm.click() or similar
|
||||
dialog.find(".btn-primary").click();
|
||||
});
|
||||
|
||||
dialog = exports.dialog(options);
|
||||
|
||||
// clear the existing handler focusing the submit button...
|
||||
dialog.off("shown.bs.modal");
|
||||
|
||||
// ...and replace it with one focusing our input, if possible
|
||||
dialog.on("shown.bs.modal", function() {
|
||||
input.focus();
|
||||
});
|
||||
|
||||
if (shouldShow === true) {
|
||||
dialog.modal("show");
|
||||
}
|
||||
|
||||
return dialog;
|
||||
};
|
||||
|
||||
exports.dialog = function(options) {
|
||||
options = sanitize(options);
|
||||
|
||||
var dialog = $(templates.dialog);
|
||||
var body = dialog.find(".modal-body");
|
||||
var buttons = options.buttons;
|
||||
var buttonStr = "";
|
||||
var callbacks = {
|
||||
onEscape: options.onEscape
|
||||
};
|
||||
|
||||
each(buttons, function(key, button) {
|
||||
|
||||
// @TODO I don't like this string appending to itself; bit dirty. Needs reworking
|
||||
// can we just build up button elements instead? slower but neater. Then button
|
||||
// can just become a template too
|
||||
buttonStr += "<button data-bb-handler='" + key + "' type='button' class='btn " + button.className + "'>" + button.label + "</button>";
|
||||
callbacks[key] = button.callback;
|
||||
});
|
||||
|
||||
body.find(".bootbox-body").html(options.message);
|
||||
|
||||
if (options.animate === true) {
|
||||
dialog.addClass("fade");
|
||||
}
|
||||
|
||||
if (options.className) {
|
||||
dialog.addClass(options.className);
|
||||
}
|
||||
|
||||
if (options.title) {
|
||||
body.before(templates.header);
|
||||
}
|
||||
|
||||
if (options.closeButton) {
|
||||
var closeButton = $(templates.closeButton);
|
||||
|
||||
if (options.title) {
|
||||
dialog.find(".modal-header").prepend(closeButton);
|
||||
} else {
|
||||
closeButton.css("margin-top", "-10px").prependTo(body);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.title) {
|
||||
dialog.find(".modal-title").html(options.title);
|
||||
}
|
||||
|
||||
if (buttonStr.length) {
|
||||
body.after(templates.footer);
|
||||
dialog.find(".modal-footer").html(buttonStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Bootstrap event listeners; used handle extra
|
||||
* setup & teardown required after the underlying
|
||||
* modal has performed certain actions
|
||||
*/
|
||||
|
||||
dialog.on("hidden.bs.modal", function(e) {
|
||||
// ensure we don't accidentally intercept hidden events triggered
|
||||
// by children of the current dialog. We shouldn't anymore now BS
|
||||
// namespaces its events; but still worth doing
|
||||
if (e.target === this) {
|
||||
dialog.remove();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
dialog.on("show.bs.modal", function() {
|
||||
// sadly this doesn't work; show is called *just* before
|
||||
// the backdrop is added so we'd need a setTimeout hack or
|
||||
// otherwise... leaving in as would be nice
|
||||
if (options.backdrop) {
|
||||
dialog.next(".modal-backdrop").addClass("bootbox-backdrop");
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
dialog.on("shown.bs.modal", function() {
|
||||
dialog.find(".btn-primary:first").focus();
|
||||
});
|
||||
|
||||
/**
|
||||
* Bootbox event listeners; experimental and may not last
|
||||
* just an attempt to decouple some behaviours from their
|
||||
* respective triggers
|
||||
*/
|
||||
|
||||
dialog.on("escape.close.bb", function(e) {
|
||||
if (callbacks.onEscape) {
|
||||
processCallback(e, dialog, callbacks.onEscape);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Standard jQuery event listeners; used to handle user
|
||||
* interaction with our dialog
|
||||
*/
|
||||
|
||||
dialog.on("click", ".modal-footer button", function(e) {
|
||||
var callbackKey = $(this).data("bb-handler");
|
||||
|
||||
processCallback(e, dialog, callbacks[callbackKey]);
|
||||
|
||||
});
|
||||
|
||||
dialog.on("click", ".bootbox-close-button", function(e) {
|
||||
// onEscape might be falsy but that's fine; the fact is
|
||||
// if the user has managed to click the close button we
|
||||
// have to close the dialog, callback or not
|
||||
processCallback(e, dialog, callbacks.onEscape);
|
||||
});
|
||||
|
||||
dialog.on("keyup", function(e) {
|
||||
if (e.which === 27) {
|
||||
dialog.trigger("escape.close.bb");
|
||||
}
|
||||
});
|
||||
|
||||
// the remainder of this method simply deals with adding our
|
||||
// dialogent to the DOM, augmenting it with Bootstrap's modal
|
||||
// functionality and then giving the resulting object back
|
||||
// to our caller
|
||||
|
||||
appendTo.append(dialog);
|
||||
|
||||
dialog.modal({
|
||||
backdrop: options.backdrop,
|
||||
keyboard: false,
|
||||
show: false
|
||||
});
|
||||
|
||||
if (options.show) {
|
||||
dialog.modal("show");
|
||||
}
|
||||
|
||||
// @TODO should we return the raw element here or should
|
||||
// we wrap it in an object on which we can expose some neater
|
||||
// methods, e.g. var d = bootbox.alert(); d.hide(); instead
|
||||
// of d.modal("hide");
|
||||
|
||||
/*
|
||||
function BBDialog(elem) {
|
||||
this.elem = elem;
|
||||
}
|
||||
|
||||
BBDialog.prototype = {
|
||||
hide: function() {
|
||||
return this.elem.modal("hide");
|
||||
},
|
||||
show: function() {
|
||||
return this.elem.modal("show");
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
return dialog;
|
||||
|
||||
};
|
||||
|
||||
exports.setDefaults = function(values) {
|
||||
$.extend(defaults, values);
|
||||
};
|
||||
|
||||
exports.hideAll = function() {
|
||||
$(".bootbox").modal("hide");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* standard locales. Please add more according to ISO 639-1 standard. Multiple language variants are
|
||||
* unlikely to be required. If this gets too large it can be split out into separate JS files.
|
||||
*/
|
||||
var locales = {
|
||||
br : {
|
||||
OK : "OK",
|
||||
CANCEL : "Cancelar",
|
||||
CONFIRM : "Sim"
|
||||
},
|
||||
da : {
|
||||
OK : "OK",
|
||||
CANCEL : "Annuller",
|
||||
CONFIRM : "Accepter"
|
||||
},
|
||||
de : {
|
||||
OK : "OK",
|
||||
CANCEL : "Abbrechen",
|
||||
CONFIRM : "Akzeptieren"
|
||||
},
|
||||
en : {
|
||||
OK : "OK",
|
||||
CANCEL : "Cancel",
|
||||
CONFIRM : "OK"
|
||||
},
|
||||
es : {
|
||||
OK : "OK",
|
||||
CANCEL : "Cancelar",
|
||||
CONFIRM : "Aceptar"
|
||||
},
|
||||
fi : {
|
||||
OK : "OK",
|
||||
CANCEL : "Peruuta",
|
||||
CONFIRM : "OK"
|
||||
},
|
||||
fr : {
|
||||
OK : "OK",
|
||||
CANCEL : "Annuler",
|
||||
CONFIRM : "D'accord"
|
||||
},
|
||||
it : {
|
||||
OK : "OK",
|
||||
CANCEL : "Annulla",
|
||||
CONFIRM : "Conferma"
|
||||
},
|
||||
nl : {
|
||||
OK : "OK",
|
||||
CANCEL : "Annuleren",
|
||||
CONFIRM : "Accepteren"
|
||||
},
|
||||
pl : {
|
||||
OK : "OK",
|
||||
CANCEL : "Anuluj",
|
||||
CONFIRM : "Potwierdź"
|
||||
},
|
||||
ru : {
|
||||
OK : "OK",
|
||||
CANCEL : "Отмена",
|
||||
CONFIRM : "Применить"
|
||||
},
|
||||
zh_CN : {
|
||||
OK : "OK",
|
||||
CANCEL : "取消",
|
||||
CONFIRM : "确认"
|
||||
},
|
||||
zh_TW : {
|
||||
OK : "OK",
|
||||
CANCEL : "取消",
|
||||
CONFIRM : "確認"
|
||||
}
|
||||
};
|
||||
|
||||
exports.init = function(_$) {
|
||||
window.bootbox = init(_$ || $);
|
||||
};
|
||||
|
||||
return exports;
|
||||
|
||||
}(window.jQuery));
|
||||