Introduce Prettier (#15131)
This commit is contained in:
Родитель
f93cc66f3a
Коммит
c2e92e7482
|
@ -0,0 +1,29 @@
|
||||||
|
# exclude everything by default
|
||||||
|
*.*
|
||||||
|
|
||||||
|
# exclude these files
|
||||||
|
Dockerfile*
|
||||||
|
LICENSE
|
||||||
|
Makefile*
|
||||||
|
|
||||||
|
# exclude these directories
|
||||||
|
.pytest_cache/
|
||||||
|
.tox/
|
||||||
|
/docker/
|
||||||
|
/docs/
|
||||||
|
/private/
|
||||||
|
/scripts/
|
||||||
|
/site-static/
|
||||||
|
/src/
|
||||||
|
/storage/
|
||||||
|
__pycache__/
|
||||||
|
i18n/
|
||||||
|
img/
|
||||||
|
node_lib/
|
||||||
|
|
||||||
|
# safe-list files we want to process
|
||||||
|
!*.js
|
||||||
|
|
||||||
|
# exclude generated JS files
|
||||||
|
*-all.js
|
||||||
|
*-min.js
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"arrowParens": "always",
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"proseWrap": "never"
|
||||||
|
}
|
|
@ -190,6 +190,7 @@ setup-codestyle:
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: ## lint the code
|
lint: ## lint the code
|
||||||
flake8 src/ services/ tests/
|
flake8 src/ services/ tests/
|
||||||
|
$(shell npm $(NPM_ARGS) bin)/prettier --check '**'
|
||||||
|
|
||||||
lint-codestyle: lint
|
lint-codestyle: lint
|
||||||
|
|
||||||
|
@ -250,6 +251,10 @@ run_js_tests: ## Run the JavaScript test suite (requires compiled/compressed ass
|
||||||
watch_js_tests: ## Run+watch the JavaScript test suite (requires compiled/compressed assets).
|
watch_js_tests: ## Run+watch the JavaScript test suite (requires compiled/compressed assets).
|
||||||
NODE_PATH=$(NODE_MODULES) $$(npm bin $(NPM_ARGS))/jest --watch
|
NODE_PATH=$(NODE_MODULES) $$(npm bin $(NPM_ARGS))/jest --watch
|
||||||
|
|
||||||
|
.PHONY: format
|
||||||
|
format: ## Autoformat our codebase.
|
||||||
|
$(shell npm $(NPM_ARGS) bin)/prettier --write '**'
|
||||||
|
|
||||||
.PHONY: help_submake
|
.PHONY: help_submake
|
||||||
help_submake:
|
help_submake:
|
||||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile-docker | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile-docker | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
"node": ">= 10.13"
|
"node": ">= 10.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@claviska/jquery-minicolors": "2.3.5",
|
||||||
"addons-linter": "2.1.0",
|
"addons-linter": "2.1.0",
|
||||||
"clean-css": "4.2.3",
|
"clean-css": "4.2.3",
|
||||||
"clean-css-cli": "4.3.0",
|
"clean-css-cli": "4.3.0",
|
||||||
"jqmodal": "1.4.2",
|
"jqmodal": "1.4.2",
|
||||||
"jquery": "3.5.1",
|
"jquery": "3.5.1",
|
||||||
"jquery.browser": "0.1.0",
|
|
||||||
"jquery.cookie": "1.4.1",
|
|
||||||
"@claviska/jquery-minicolors": "2.3.5",
|
|
||||||
"jquery-pjax": "2.0.1",
|
"jquery-pjax": "2.0.1",
|
||||||
"jquery-ui": "1.12.1",
|
"jquery-ui": "1.12.1",
|
||||||
|
"jquery.browser": "0.1.0",
|
||||||
|
"jquery.cookie": "1.4.1",
|
||||||
"jszip": "3.5.0",
|
"jszip": "3.5.0",
|
||||||
"less": "3.12.2",
|
"less": "3.12.2",
|
||||||
"source-map": "0.7.3",
|
"source-map": "0.7.3",
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest": "^26.1.0",
|
"jest": "^26.1.0",
|
||||||
"jest-date-mock": "^1.0.8"
|
"jest-date-mock": "^1.0.8",
|
||||||
|
"prettier": "^2.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
django.jQuery(document).ready(function ($) {
|
django.jQuery(document).ready(function ($) {
|
||||||
if (!$('body.change-form').length) {
|
if (!$('body.change-form').length) {
|
||||||
// This is only for change forms.
|
// This is only for change forms.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each localized field will be inside a <div class="trans">. We are
|
// Each localized field will be inside a <div class="trans">. We are
|
||||||
// displaying all of them, so we want to add individual labels to let the
|
// displaying all of them, so we want to add individual labels to let the
|
||||||
// user know which one is for which locale.
|
// user know which one is for which locale.
|
||||||
$('div.trans :input:visible').before(function() {
|
$('div.trans :input:visible').before(function () {
|
||||||
let $elm = $(this);
|
let $elm = $(this);
|
||||||
let $label = $('<label>');
|
let $label = $('<label>');
|
||||||
$label.prop('for', $elm.attr('id'))
|
$label.prop('for', $elm.attr('id'));
|
||||||
$label.text('[' + $elm.attr('lang') + ']');
|
$label.text('[' + $elm.attr('lang') + ']');
|
||||||
return $label;
|
return $label;
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,31 +1,30 @@
|
||||||
django.jQuery(document).ready(function ($){
|
django.jQuery(document).ready(function ($) {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
// Recalculate Hash
|
// Recalculate Hash
|
||||||
$('.recalc').click(function(e) {
|
$('.recalc').click(function (e) {
|
||||||
var $this = $(this),
|
var $this = $(this),
|
||||||
csrf = $("input[name='csrfmiddlewaretoken']").val();
|
csrf = $("input[name='csrfmiddlewaretoken']").val();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
$.ajax($this.attr('href'), {
|
$.ajax($this.attr('href'), {
|
||||||
|
headers: { 'X-CSRFToken': csrf },
|
||||||
"headers": {'X-CSRFToken': csrf},
|
dataType: 'json',
|
||||||
"dataType": "json",
|
method: 'POST',
|
||||||
"method": "POST",
|
beforeSend: function () {
|
||||||
"beforeSend": function () {
|
$this.html('Recalcing…');
|
||||||
$this.html('Recalcing…');
|
},
|
||||||
},
|
success: function () {
|
||||||
"success": function() {
|
$this.text('Done!');
|
||||||
$this.text('Done!');
|
},
|
||||||
},
|
error: function () {
|
||||||
"error": function() {
|
$this.text('Error :(');
|
||||||
$this.text('Error :(');
|
},
|
||||||
},
|
complete: function () {
|
||||||
"complete": function() {
|
setTimeout(function () {
|
||||||
setTimeout(function() {
|
$this.text('Recalc Hash');
|
||||||
$this.text('Recalc Hash');
|
}, 2000);
|
||||||
}, 2000);
|
},
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
|
|
||||||
/* TODO(jbalogh): save from amo2009. */
|
/* TODO(jbalogh): save from amo2009. */
|
||||||
/**
|
/**
|
||||||
* bandwagon: fire a custom refresh event for bandwagon extension
|
* bandwagon: fire a custom refresh event for bandwagon extension
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function bandwagonRefreshEvent() {
|
function bandwagonRefreshEvent() {
|
||||||
if (document.createEvent) {
|
if (document.createEvent) {
|
||||||
var bandwagonSubscriptionsRefreshEvent = document.createEvent("Events");
|
var bandwagonSubscriptionsRefreshEvent = document.createEvent('Events');
|
||||||
bandwagonSubscriptionsRefreshEvent.initEvent("bandwagonRefresh", true, false);
|
bandwagonSubscriptionsRefreshEvent.initEvent(
|
||||||
document.dispatchEvent(bandwagonSubscriptionsRefreshEvent);
|
'bandwagonRefresh',
|
||||||
}
|
true,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
document.dispatchEvent(bandwagonSubscriptionsRefreshEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(jbalogh): save from amo2009. */
|
/* TODO(jbalogh): save from amo2009. */
|
||||||
/* Remove "Go" buttons from <form class="go" */
|
/* Remove "Go" buttons from <form class="go" */
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
$('form.go').change(function() { this.submit(); })
|
$('form.go')
|
||||||
.find('button').hide();
|
.change(function () {
|
||||||
|
this.submit();
|
||||||
|
})
|
||||||
|
.find('button')
|
||||||
|
.hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO(jbalogh): save from amo2009.
|
// TODO(jbalogh): save from amo2009.
|
||||||
var AMO = {};
|
var AMO = {};
|
||||||
|
|
|
@ -1,288 +1,311 @@
|
||||||
(function($) {
|
(function ($) {
|
||||||
window.Slideshow = function() {
|
window.Slideshow = function () {
|
||||||
this.itemTotal = 0;
|
this.itemTotal = 0;
|
||||||
this.currentItem = 1;
|
this.currentItem = 1;
|
||||||
this.itemWidth = 0;
|
this.itemWidth = 0;
|
||||||
|
|
||||||
// Set these properties when you instantiate an instance of this object.
|
// Set these properties when you instantiate an instance of this object.
|
||||||
this.speed = 300; // the speed in milliseconds of the animation
|
this.speed = 300; // the speed in milliseconds of the animation
|
||||||
|
|
||||||
this.itemContainer = ''; // the selector for the element containing the items.
|
this.itemContainer = ''; // the selector for the element containing the items.
|
||||||
this.wrapperElement = ''; // the tagName that will wrap the itemContainer.
|
this.wrapperElement = ''; // the tagName that will wrap the itemContainer.
|
||||||
this.wrapperClass = ''; //the classname of the element that will wrap the itemContainer.
|
this.wrapperClass = ''; //the classname of the element that will wrap the itemContainer.
|
||||||
this.controlsMarkup = ''; // the markup for the controls.
|
this.controlsMarkup = ''; // the markup for the controls.
|
||||||
this.leftController = ''; // the selector for the left controller.
|
this.leftController = ''; // the selector for the left controller.
|
||||||
this.rightContorller = ''; // the selector for the right controller.
|
this.rightContorller = ''; // the selector for the right controller.
|
||||||
this.activeClass = ''; // the classname to indicate that a controller is active.
|
this.activeClass = ''; // the classname to indicate that a controller is active.
|
||||||
this.container = ''; //the complete container for all of the slideshow
|
this.container = ''; //the complete container for all of the slideshow
|
||||||
this.interval = null;
|
this.interval = null;
|
||||||
this.scroll = true;
|
this.scroll = true;
|
||||||
};
|
};
|
||||||
Slideshow.prototype.init = function() {
|
Slideshow.prototype.init = function () {
|
||||||
this.itemTotal = parseInt($(this.itemContainer+'>li').length,10);
|
this.itemTotal = parseInt($(this.itemContainer + '>li').length, 10);
|
||||||
if (this.itemTotal <= 1) {
|
if (this.itemTotal <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$(this.itemContainer).wrap('<'+this.wrapperElement+' class="'+this.wrapperClass+'"></'+this.wrapperElement+'>');
|
$(this.itemContainer).wrap(
|
||||||
this.itemWidth = this.getItemWidth();
|
'<' +
|
||||||
// applying controls to 2nd parent rather than 1st fixes stacking context issue in FF2
|
this.wrapperElement +
|
||||||
$($(this.itemContainer).parents()[1]).append(this.controlsMarkup);
|
' class="' +
|
||||||
$(this.itemContainer+'>li').width(this.itemWidth+'px');
|
this.wrapperClass +
|
||||||
|
'"></' +
|
||||||
|
this.wrapperElement +
|
||||||
|
'>',
|
||||||
|
);
|
||||||
|
this.itemWidth = this.getItemWidth();
|
||||||
|
// applying controls to 2nd parent rather than 1st fixes stacking context issue in FF2
|
||||||
|
$($(this.itemContainer).parents()[1]).append(this.controlsMarkup);
|
||||||
|
$(this.itemContainer + '>li').width(this.itemWidth + 'px');
|
||||||
|
|
||||||
this.checkControls();
|
this.checkControls();
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
$(self.leftController).live('click', function() {
|
$(self.leftController).live('click', function () {
|
||||||
if ($(this).hasClass(self.activeClass)) {
|
if ($(this).hasClass(self.activeClass)) {
|
||||||
self.moveToItem(self.currentItem-1);
|
self.moveToItem(self.currentItem - 1);
|
||||||
}
|
}
|
||||||
self.scroll = false;
|
self.scroll = false;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$(self.rightController).live('click', function() {
|
$(self.rightController).live('click', function () {
|
||||||
if ($(this).hasClass(self.activeClass)) {
|
if ($(this).hasClass(self.activeClass)) {
|
||||||
self.moveToItem(self.currentItem+1);
|
self.moveToItem(self.currentItem + 1);
|
||||||
}
|
}
|
||||||
self.scroll = false;
|
self.scroll = false;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$(self.container).mouseenter(function() {
|
$(self.container).mouseenter(function () {
|
||||||
clearInterval(self.interval);
|
clearInterval(self.interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(self.container).on('newPopup', function() {
|
$(self.container).on('newPopup', function () {
|
||||||
clearInterval(self.interval);
|
clearInterval(self.interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(self.container).mouseleave(function() {
|
$(self.container).mouseleave(function () {
|
||||||
self.autoRotate();
|
self.autoRotate();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.autoRotate();
|
self.autoRotate();
|
||||||
|
|
||||||
$(window).resize(function() {
|
$(window).resize(function () {
|
||||||
self.itemWidth = self.getItemWidth();
|
self.itemWidth = self.getItemWidth();
|
||||||
$(self.itemContainer+'>li').width(self.itemWidth+'px');
|
$(self.itemContainer + '>li').width(self.itemWidth + 'px');
|
||||||
self.popToItem(self.currentItem);
|
self.popToItem(self.currentItem);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Slideshow.prototype.autoRotate = function() {
|
Slideshow.prototype.autoRotate = function () {
|
||||||
if(this.scroll) {
|
if (this.scroll) {
|
||||||
var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this'
|
var that = this; //closure due to setInterval's 'this' refers to window, not the current 'this'
|
||||||
clearInterval(this.interval);
|
clearInterval(this.interval);
|
||||||
this.interval = setInterval(function() {
|
this.interval = setInterval(function () {
|
||||||
if(that.currentItem != that.itemTotal) {
|
if (that.currentItem != that.itemTotal) {
|
||||||
that.moveToItem(that.currentItem+1);
|
that.moveToItem(that.currentItem + 1);
|
||||||
} else {
|
} else {
|
||||||
that.moveToItem(1);
|
that.moveToItem(1);
|
||||||
}
|
}
|
||||||
}, 8000);
|
}, 8000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Slideshow.prototype.getItemWidth = function() {
|
Slideshow.prototype.getItemWidth = function () {
|
||||||
return $(this.itemContainer).parents('.'+this.wrapperClass).width();
|
return $(this.itemContainer)
|
||||||
};
|
.parents('.' + this.wrapperClass)
|
||||||
Slideshow.prototype.popToItem = function(itemNumber) {
|
.width();
|
||||||
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) {
|
};
|
||||||
$($(this.itemContainer).children("li").get(this.currentItem-1)).hide();
|
Slideshow.prototype.popToItem = function (itemNumber) {
|
||||||
$($(this.itemContainer).children("li").get(itemNumber-1)).show();
|
if (
|
||||||
this.currentItem = itemNumber;
|
!$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
|
||||||
this.checkControls();
|
.length
|
||||||
}
|
) {
|
||||||
};
|
$(
|
||||||
Slideshow.prototype.moveToItem = function(itemNumber) {
|
$(this.itemContainer)
|
||||||
if (!$(this.itemContainer).parents('.'+this.wrapperClass+' :animated').length) {
|
.children('li')
|
||||||
var lis = $(this.itemContainer).children("li");
|
.get(this.currentItem - 1),
|
||||||
$(lis.get(this.currentItem-1)).fadeOut("fast", function () {
|
).hide();
|
||||||
$(lis.get(itemNumber-1)).fadeIn("fast");
|
$(
|
||||||
});
|
$(this.itemContainer)
|
||||||
this.currentItem = itemNumber;
|
.children('li')
|
||||||
this.checkControls();
|
.get(itemNumber - 1),
|
||||||
}
|
).show();
|
||||||
};
|
this.currentItem = itemNumber;
|
||||||
Slideshow.prototype.checkControls = function() {
|
this.checkControls();
|
||||||
if (this.currentItem == 1) {
|
}
|
||||||
$(this.leftController).removeClass(this.activeClass);
|
};
|
||||||
} else {
|
Slideshow.prototype.moveToItem = function (itemNumber) {
|
||||||
$(this.leftController).addClass(this.activeClass);
|
if (
|
||||||
}
|
!$(this.itemContainer).parents('.' + this.wrapperClass + ' :animated')
|
||||||
if (this.currentItem == this.itemTotal) {
|
.length
|
||||||
$(this.rightController).removeClass(this.activeClass);
|
) {
|
||||||
} else {
|
var lis = $(this.itemContainer).children('li');
|
||||||
$(this.rightController).addClass(this.activeClass);
|
$(lis.get(this.currentItem - 1)).fadeOut('fast', function () {
|
||||||
}
|
$(lis.get(itemNumber - 1)).fadeIn('fast');
|
||||||
};
|
});
|
||||||
|
this.currentItem = itemNumber;
|
||||||
|
this.checkControls();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Slideshow.prototype.checkControls = function () {
|
||||||
|
if (this.currentItem == 1) {
|
||||||
|
$(this.leftController).removeClass(this.activeClass);
|
||||||
|
} else {
|
||||||
|
$(this.leftController).addClass(this.activeClass);
|
||||||
|
}
|
||||||
|
if (this.currentItem == this.itemTotal) {
|
||||||
|
$(this.rightController).removeClass(this.activeClass);
|
||||||
|
} else {
|
||||||
|
$(this.rightController).addClass(this.activeClass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// slidey dropdown area
|
// slidey dropdown area
|
||||||
window.DropdownArea = function() {
|
window.DropdownArea = function () {
|
||||||
this.trigger = null;
|
this.trigger = null;
|
||||||
this.target = '';
|
this.target = '';
|
||||||
this.targetParent = '';
|
this.targetParent = '';
|
||||||
this.callbackFunction = function(){};
|
this.callbackFunction = function () {};
|
||||||
this.preventDefault = true;
|
this.preventDefault = true;
|
||||||
this.showSpeed = 200;
|
this.showSpeed = 200;
|
||||||
this.hideSpeed = 200;
|
this.hideSpeed = 200;
|
||||||
this.hideOnBodyClick = true;
|
this.hideOnBodyClick = true;
|
||||||
};
|
};
|
||||||
DropdownArea.prototype.bodyclick = function(e) {
|
DropdownArea.prototype.bodyclick = function (e) {
|
||||||
// this will get fired on click of body, we need to close the dropdown
|
// this will get fired on click of body, we need to close the dropdown
|
||||||
if (this.bodyWatching && this.hideOnBodyClick) {
|
if (this.bodyWatching && this.hideOnBodyClick) {
|
||||||
if (!
|
if (
|
||||||
($(e.target).get(0) == $(this.targetParent).get(0) ||
|
!(
|
||||||
$(e.target).parents(this.targetParent).length)
|
$(e.target).get(0) == $(this.targetParent).get(0) ||
|
||||||
) {
|
$(e.target).parents(this.targetParent).length
|
||||||
this.hide();
|
)
|
||||||
}
|
) {
|
||||||
|
this.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DropdownArea.prototype.hide = function() {
|
};
|
||||||
var self = this;
|
DropdownArea.prototype.hide = function () {
|
||||||
$(self.targetParent).removeClass('expanded');
|
var self = this;
|
||||||
$(self.target).slideUp(self.hideSpeed, function() {
|
$(self.targetParent).removeClass('expanded');
|
||||||
//unbind bodyclick
|
$(self.target).slideUp(self.hideSpeed, function () {
|
||||||
self.bodyWatching = false;
|
//unbind bodyclick
|
||||||
});
|
self.bodyWatching = false;
|
||||||
}
|
});
|
||||||
DropdownArea.prototype.show = function() {
|
};
|
||||||
var self = this;
|
DropdownArea.prototype.show = function () {
|
||||||
$(self.targetParent).addClass('expanded');
|
var self = this;
|
||||||
$(self.target).slideDown(self.showSpeed, function() {
|
$(self.targetParent).addClass('expanded');
|
||||||
self.bodyWatching = true;
|
$(self.target).slideDown(self.showSpeed, function () {
|
||||||
});
|
self.bodyWatching = true;
|
||||||
}
|
});
|
||||||
DropdownArea.prototype.init = function() {
|
};
|
||||||
|
DropdownArea.prototype.init = function () {
|
||||||
// advanced dropdown
|
// advanced dropdown
|
||||||
var self = this;
|
var self = this;
|
||||||
$(this.target).hide();
|
$(this.target).hide();
|
||||||
if (this.trigger) {
|
if (this.trigger) {
|
||||||
this.trigger.click(
|
this.trigger.click(function (e) {
|
||||||
function(e) {
|
if (!$(self.target + ':animated').length) {
|
||||||
if(! $(self.target+':animated').length) {
|
if ($(self.target + ':visible').length) {
|
||||||
if ($(self.target+':visible').length){
|
self.callbackFunction();
|
||||||
self.callbackFunction();
|
self.hide();
|
||||||
self.hide();
|
} else {
|
||||||
} else {
|
self.callbackFunction();
|
||||||
self.callbackFunction();
|
self.show();
|
||||||
self.show();
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
$(self.target).trigger('click');
|
|
||||||
return !self.preventDefault;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// if box now showing bind bodyclick
|
|
||||||
$('body').on("click", function(e) {
|
|
||||||
self.bodyclick(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A special slideshow that updates the teaser 'selected' list item
|
|
||||||
window.AmoSlideshow = function() {
|
|
||||||
/* This is a convenience function that performs all the slideshow
|
|
||||||
* setup we shouldn't have to think about if the slideshow code
|
|
||||||
* was written with an eye for abstraction and reusability.
|
|
||||||
* First one to refactor it gets a cookie.
|
|
||||||
*/
|
|
||||||
function HeaderSlideshow() {
|
|
||||||
if($('.teaser-items').hasClass('no-autorotate')) {
|
|
||||||
Slideshow.prototype.autoRotate = function(){}
|
|
||||||
}
|
|
||||||
Slideshow.call(this);
|
|
||||||
}
|
}
|
||||||
HeaderSlideshow.prototype = new Slideshow();
|
$(self.target).trigger('click');
|
||||||
HeaderSlideshow.prototype.moveToItem = function(itemNumber) {
|
return !self.preventDefault;
|
||||||
Slideshow.prototype.moveToItem.call(this, itemNumber);
|
});
|
||||||
$('.section-teaser .teaser-header li').removeClass('selected');
|
// if box now showing bind bodyclick
|
||||||
$('.section-teaser .teaser-header li').eq(itemNumber - 1).addClass('selected');
|
$('body').on('click', function (e) {
|
||||||
};
|
self.bodyclick(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var homepageSlider = new HeaderSlideshow();
|
// A special slideshow that updates the teaser 'selected' list item
|
||||||
homepageSlider.itemContainer = '.teaser-items';
|
window.AmoSlideshow = function () {
|
||||||
homepageSlider.wrapperElement = 'div';
|
/* This is a convenience function that performs all the slideshow
|
||||||
homepageSlider.wrapperClass = 'window';
|
* setup we shouldn't have to think about if the slideshow code
|
||||||
homepageSlider.controlsMarkup = (
|
* was written with an eye for abstraction and reusability.
|
||||||
'<p class="slideshow-controls">' +
|
* First one to refactor it gets a cookie.
|
||||||
'<a href="#" class="prev" rel="prev">Previous</a>' +
|
*/
|
||||||
'<a href="#" class="next" rel="next">Next</a></p>'
|
function HeaderSlideshow() {
|
||||||
);
|
if ($('.teaser-items').hasClass('no-autorotate')) {
|
||||||
homepageSlider.leftController = '.section-teaser a[rel="prev"]';
|
Slideshow.prototype.autoRotate = function () {};
|
||||||
homepageSlider.rightController = '.section-teaser a[rel="next"]';
|
}
|
||||||
homepageSlider.activeClass = 'active';
|
Slideshow.call(this);
|
||||||
homepageSlider.container = '.section-teaser .featured-inner';
|
}
|
||||||
homepageSlider.init();
|
HeaderSlideshow.prototype = new Slideshow();
|
||||||
|
HeaderSlideshow.prototype.moveToItem = function (itemNumber) {
|
||||||
//Move the list of promo categories below the controls to allow all content to expand
|
Slideshow.prototype.moveToItem.call(this, itemNumber);
|
||||||
$('.teaser-header').insertBefore(".slideshow-controls");
|
$('.section-teaser .teaser-header li').removeClass('selected');
|
||||||
|
$('.section-teaser .teaser-header li')
|
||||||
var headerListItems = $('.section-teaser .teaser-header li a');
|
.eq(itemNumber - 1)
|
||||||
|
.addClass('selected');
|
||||||
headerListItems.click(function() {
|
|
||||||
headerListItems.parent('li').removeClass('selected');
|
|
||||||
$(this).parent('li').addClass('selected');
|
|
||||||
homepageSlider.moveToItem(headerListItems.index(this) + 1);
|
|
||||||
homepageSlider.scroll = false;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return homepageSlider;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
})(jQuery);
|
var homepageSlider = new HeaderSlideshow();
|
||||||
|
homepageSlider.itemContainer = '.teaser-items';
|
||||||
|
homepageSlider.wrapperElement = 'div';
|
||||||
|
homepageSlider.wrapperClass = 'window';
|
||||||
|
homepageSlider.controlsMarkup =
|
||||||
|
'<p class="slideshow-controls">' +
|
||||||
|
'<a href="#" class="prev" rel="prev">Previous</a>' +
|
||||||
|
'<a href="#" class="next" rel="next">Next</a></p>';
|
||||||
|
homepageSlider.leftController = '.section-teaser a[rel="prev"]';
|
||||||
|
homepageSlider.rightController = '.section-teaser a[rel="next"]';
|
||||||
|
homepageSlider.activeClass = 'active';
|
||||||
|
homepageSlider.container = '.section-teaser .featured-inner';
|
||||||
|
homepageSlider.init();
|
||||||
|
|
||||||
jQuery(function($) {
|
//Move the list of promo categories below the controls to allow all content to expand
|
||||||
// Greys out the favorites icon when it is clicked
|
$('.teaser-header').insertBefore('.slideshow-controls');
|
||||||
$(".item-info li.favorite").click(function () {
|
|
||||||
var self = this;
|
var headerListItems = $('.section-teaser .teaser-header li a');
|
||||||
$(self).addClass("favorite-loading");
|
|
||||||
setTimeout(function() {
|
headerListItems.click(function () {
|
||||||
$(self).addClass("favorite-added");
|
headerListItems.parent('li').removeClass('selected');
|
||||||
},2000);
|
$(this).parent('li').addClass('selected');
|
||||||
|
homepageSlider.moveToItem(headerListItems.index(this) + 1);
|
||||||
|
homepageSlider.scroll = false;
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// account dropdown in auxillary menu
|
return homepageSlider;
|
||||||
var accountDropdown = new DropdownArea();
|
};
|
||||||
// set up variables for object
|
})(jQuery);
|
||||||
accountDropdown.trigger = ($('ul.account .controller')); // node
|
|
||||||
accountDropdown.target = ('ul.account ul'); // reference
|
|
||||||
accountDropdown.targetParent = ('ul.account'); // reference
|
|
||||||
accountDropdown.init();
|
|
||||||
|
|
||||||
// tools dropdown in auxillary menu
|
jQuery(function ($) {
|
||||||
var toolsDropdown = new DropdownArea();
|
// Greys out the favorites icon when it is clicked
|
||||||
// set up variables for object
|
$('.item-info li.favorite').click(function () {
|
||||||
toolsDropdown.trigger = ($('ul.tools .controller')); // node
|
var self = this;
|
||||||
toolsDropdown.target = ('ul.tools ul'); // reference
|
$(self).addClass('favorite-loading');
|
||||||
toolsDropdown.targetParent = ('ul.tools'); // reference
|
setTimeout(function () {
|
||||||
toolsDropdown.init();
|
$(self).addClass('favorite-added');
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
// change dropdown in auxillary menu
|
// account dropdown in auxillary menu
|
||||||
var changeDropdown = new DropdownArea();
|
var accountDropdown = new DropdownArea();
|
||||||
// set up variables for object
|
// set up variables for object
|
||||||
changeDropdown.trigger = ($('ul.change .controller')); // node
|
accountDropdown.trigger = $('ul.account .controller'); // node
|
||||||
changeDropdown.target = ('ul.change ul'); // reference
|
accountDropdown.target = 'ul.account ul'; // reference
|
||||||
changeDropdown.targetParent = ('ul.change'); // reference
|
accountDropdown.targetParent = 'ul.account'; // reference
|
||||||
changeDropdown.init();
|
accountDropdown.init();
|
||||||
|
|
||||||
// notification dropdown
|
// tools dropdown in auxillary menu
|
||||||
var notificationHelpDropdown = new DropdownArea();
|
var toolsDropdown = new DropdownArea();
|
||||||
// set up variables for object
|
// set up variables for object
|
||||||
notificationHelpDropdown.trigger = ($('.notification .toggle-help')); // node
|
toolsDropdown.trigger = $('ul.tools .controller'); // node
|
||||||
notificationHelpDropdown.target = ('.notification .toggle-info'); // reference
|
toolsDropdown.target = 'ul.tools ul'; // reference
|
||||||
notificationHelpDropdown.targetParent = ('.notification'); // reference
|
toolsDropdown.targetParent = 'ul.tools'; // reference
|
||||||
notificationHelpDropdown.init();
|
toolsDropdown.init();
|
||||||
$('.notification a.close').click(function() {
|
|
||||||
notificationHelpDropdown.hide();
|
// change dropdown in auxillary menu
|
||||||
return false;
|
var changeDropdown = new DropdownArea();
|
||||||
})
|
// set up variables for object
|
||||||
|
changeDropdown.trigger = $('ul.change .controller'); // node
|
||||||
|
changeDropdown.target = 'ul.change ul'; // reference
|
||||||
|
changeDropdown.targetParent = 'ul.change'; // reference
|
||||||
|
changeDropdown.init();
|
||||||
|
|
||||||
|
// notification dropdown
|
||||||
|
var notificationHelpDropdown = new DropdownArea();
|
||||||
|
// set up variables for object
|
||||||
|
notificationHelpDropdown.trigger = $('.notification .toggle-help'); // node
|
||||||
|
notificationHelpDropdown.target = '.notification .toggle-info'; // reference
|
||||||
|
notificationHelpDropdown.targetParent = '.notification'; // reference
|
||||||
|
notificationHelpDropdown.init();
|
||||||
|
$('.notification a.close').click(function () {
|
||||||
|
notificationHelpDropdown.hide();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Initialization things that get run on every page. */
|
/* Initialization things that get run on every page. */
|
||||||
|
|
||||||
$(".hidden").hide(); // hide anything that should be hidden
|
$('.hidden').hide(); // hide anything that should be hidden
|
||||||
|
|
|
@ -2,48 +2,55 @@ z.visitor = z.Storage('visitor');
|
||||||
z.currentVisit = z.SessionStorage('current-visit');
|
z.currentVisit = z.SessionStorage('current-visit');
|
||||||
|
|
||||||
function initBanners() {
|
function initBanners() {
|
||||||
var $body = $(document.body);
|
var $body = $(document.body);
|
||||||
|
|
||||||
if ($body.hasClass('editor-tools')) {
|
if ($body.hasClass('editor-tools')) {
|
||||||
// Don't bother showing those on editor tools, it has a bunch of weird
|
// Don't bother showing those on editor tools, it has a bunch of weird
|
||||||
// styles for the menu that don't play nice with those banners.
|
// styles for the menu that don't play nice with those banners.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the various banners, but only one at a time, and only if they
|
// Show the various banners, but only one at a time, and only if they
|
||||||
// haven't been dimissed before.
|
// haven't been dimissed before.
|
||||||
// To reset dismissal state: z.visitor.remove('xx')
|
// To reset dismissal state: z.visitor.remove('xx')
|
||||||
|
|
||||||
// Show the bad-browser message
|
// Show the bad-browser message
|
||||||
if (!z.visitor.get('seen_badbrowser_warning') && $body.hasClass('badbrowser')) {
|
if (
|
||||||
$('#site-nonfx').show();
|
!z.visitor.get('seen_badbrowser_warning') &&
|
||||||
}
|
$body.hasClass('badbrowser')
|
||||||
// Show the first visit banner.
|
) {
|
||||||
else if (!z.visitor.get('seen_impala_first_visit')) {
|
$('#site-nonfx').show();
|
||||||
$body.addClass('firstvisit');
|
}
|
||||||
z.visitor.set('seen_impala_first_visit', 1);
|
// Show the first visit banner.
|
||||||
}
|
else if (!z.visitor.get('seen_impala_first_visit')) {
|
||||||
// Show the link to try the new frontend (only on the homepage for now).
|
$body.addClass('firstvisit');
|
||||||
else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) {
|
z.visitor.set('seen_impala_first_visit', 1);
|
||||||
$('#try-new-frontend').show();
|
}
|
||||||
}
|
// Show the link to try the new frontend (only on the homepage for now).
|
||||||
// Show the ACR pitch if it has not been dismissed.
|
else if (!z.visitor.get('seen_try_new_frontend') && $body.hasClass('home')) {
|
||||||
else if (!z.visitor.get('seen_acr_pitch') && $body.hasClass('acr-pitch')) {
|
$('#try-new-frontend').show();
|
||||||
$body.find('#acr-pitch').show();
|
}
|
||||||
}
|
// Show the ACR pitch if it has not been dismissed.
|
||||||
|
else if (!z.visitor.get('seen_acr_pitch') && $body.hasClass('acr-pitch')) {
|
||||||
|
$body.find('#acr-pitch').show();
|
||||||
|
}
|
||||||
|
|
||||||
// Allow dismissal of site-balloons.
|
// Allow dismissal of site-balloons.
|
||||||
$body.on('click', '.site-balloon .close, .site-tip .close', _pd(function() {
|
$body.on(
|
||||||
var $parent = $(this).closest('.site-balloon, .site-tip');
|
'click',
|
||||||
$parent.fadeOut();
|
'.site-balloon .close, .site-tip .close',
|
||||||
if ($parent.is('#site-nonfx')) {
|
_pd(function () {
|
||||||
z.visitor.set('seen_badbrowser_warning', 1);
|
var $parent = $(this).closest('.site-balloon, .site-tip');
|
||||||
} else if ($parent.is('#acr-pitch')) {
|
$parent.fadeOut();
|
||||||
z.visitor.set('seen_acr_pitch', 1);
|
if ($parent.is('#site-nonfx')) {
|
||||||
} else if ($parent.is('#appruntime-pitch')) {
|
z.visitor.set('seen_badbrowser_warning', 1);
|
||||||
z.visitor.set('seen_appruntime_pitch', 1);
|
} else if ($parent.is('#acr-pitch')) {
|
||||||
} else if ($parent.is('#try-new-frontend')) {
|
z.visitor.set('seen_acr_pitch', 1);
|
||||||
z.visitor.set('seen_try_new_frontend', 1);
|
} else if ($parent.is('#appruntime-pitch')) {
|
||||||
}
|
z.visitor.set('seen_appruntime_pitch', 1);
|
||||||
}));
|
} else if ($parent.is('#try-new-frontend')) {
|
||||||
|
z.visitor.set('seen_try_new_frontend', 1);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
// Named key codes. That's the key idea here.
|
// Named key codes. That's the key idea here.
|
||||||
z.keys = {
|
z.keys = {
|
||||||
'SHIFT': 16,
|
SHIFT: 16,
|
||||||
'CONTROL': 17,
|
CONTROL: 17,
|
||||||
'ALT': 18,
|
ALT: 18,
|
||||||
'PAUSE': 19,
|
PAUSE: 19,
|
||||||
'CAPS_LOCK': 20,
|
CAPS_LOCK: 20,
|
||||||
'ESCAPE': 27,
|
ESCAPE: 27,
|
||||||
'ENTER': 13,
|
ENTER: 13,
|
||||||
'PAGE_UP': 33,
|
PAGE_UP: 33,
|
||||||
'PAGE_DOWN': 34,
|
PAGE_DOWN: 34,
|
||||||
'LEFT': 37,
|
LEFT: 37,
|
||||||
'UP': 38,
|
UP: 38,
|
||||||
'RIGHT': 39,
|
RIGHT: 39,
|
||||||
'DOWN': 40,
|
DOWN: 40,
|
||||||
'HOME': 36,
|
HOME: 36,
|
||||||
'END': 35,
|
END: 35,
|
||||||
'COMMAND': 91,
|
COMMAND: 91,
|
||||||
'WINDOWS_RIGHT': 92,
|
WINDOWS_RIGHT: 92,
|
||||||
'COMMAND_RIGHT': 93,
|
COMMAND_RIGHT: 93,
|
||||||
'WINDOWS_LEFT_OPERA': 219,
|
WINDOWS_LEFT_OPERA: 219,
|
||||||
'WINDOWS_RIGHT_OPERA': 220,
|
WINDOWS_RIGHT_OPERA: 220,
|
||||||
'APPLE': 24
|
APPLE: 24,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
/* Remove "Go" buttons from <form class="go" */
|
/* Remove "Go" buttons from <form class="go" */
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
$('form.go').change(function() { this.submit(); })
|
$('form.go')
|
||||||
.find('button').hide();
|
.change(function () {
|
||||||
|
this.submit();
|
||||||
|
})
|
||||||
|
.find('button')
|
||||||
|
.hide();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,61 +1,70 @@
|
||||||
// Replaces rating selectboxes with the rating widget
|
// Replaces rating selectboxes with the rating widget
|
||||||
$.fn.ratingwidget = function(classes) {
|
$.fn.ratingwidget = function (classes) {
|
||||||
this.each(function(n, el) {
|
this.each(function (n, el) {
|
||||||
if (!classes) {
|
if (!classes) {
|
||||||
classes = '';
|
classes = '';
|
||||||
|
}
|
||||||
|
var $el = $(el),
|
||||||
|
allClasses = 'ratingwidget stars stars-0 ' + classes,
|
||||||
|
$widget = $('<span class="' + allClasses + '"></span>'),
|
||||||
|
rs = '',
|
||||||
|
showStars = function (n) {
|
||||||
|
$widget
|
||||||
|
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
|
||||||
|
.addClass('stars-' + n);
|
||||||
|
},
|
||||||
|
setStars = function (n) {
|
||||||
|
if (rating == n) return;
|
||||||
|
var e = $widget.find(format('[value="{0}"]', n));
|
||||||
|
e.click();
|
||||||
|
showStars(n);
|
||||||
|
rating = n;
|
||||||
|
},
|
||||||
|
rating = null;
|
||||||
|
// Existing rating found so initialize the widget.
|
||||||
|
if ($('option[selected]', $el).length) {
|
||||||
|
var temp_rating = $el.val();
|
||||||
|
setStars(temp_rating);
|
||||||
|
rating = parseInt(temp_rating, 10);
|
||||||
|
}
|
||||||
|
for (var i = 1; i <= 5; i++) {
|
||||||
|
var checked = rating === i ? ' checked' : '';
|
||||||
|
rs += format(
|
||||||
|
'<label data-stars="{0}">{1}<input type="radio" name="rating"{2} value="{3}"></label>',
|
||||||
|
[i, format(ngettext('{0} star', '{0} stars', i), [i]), checked, i],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$widget
|
||||||
|
.click(function (evt) {
|
||||||
|
var t = $(evt.target);
|
||||||
|
if (t.is('input[type=radio]')) {
|
||||||
|
showStars((rating = t.val()));
|
||||||
|
if (!t.val()) {
|
||||||
|
// If the user caused a radio button to become unchecked,
|
||||||
|
// re-check it because that shouldn't happen.
|
||||||
|
t.prop('checked', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var $el = $(el),
|
})
|
||||||
allClasses = 'ratingwidget stars stars-0 ' + classes,
|
.mouseover(function (evt) {
|
||||||
$widget = $('<span class="' + allClasses + '"></span>'),
|
var t = $(evt.target);
|
||||||
rs = '',
|
if (t.attr('data-stars')) {
|
||||||
showStars = function(n) {
|
showStars(t.attr('data-stars'));
|
||||||
$widget.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + n);
|
|
||||||
},
|
|
||||||
setStars = function(n) {
|
|
||||||
if (rating == n) return;
|
|
||||||
var e = $widget.find(format('[value="{0}"]', n));
|
|
||||||
e.click();
|
|
||||||
showStars(n);
|
|
||||||
rating = n;
|
|
||||||
},
|
|
||||||
rating = null;
|
|
||||||
// Existing rating found so initialize the widget.
|
|
||||||
if ($('option[selected]', $el).length) {
|
|
||||||
var temp_rating = $el.val();
|
|
||||||
setStars(temp_rating);
|
|
||||||
rating = parseInt(temp_rating, 10);
|
|
||||||
}
|
}
|
||||||
for (var i=1; i<=5; i++) {
|
})
|
||||||
var checked = rating === i ? ' checked' : '';
|
.mouseout(function () {
|
||||||
rs += format('<label data-stars="{0}">{1}<input type="radio" name="rating"{2} value="{3}"></label>',
|
showStars(rating || 0);
|
||||||
[i, format(ngettext('{0} star', '{0} stars', i), [i]), checked, i]);
|
})
|
||||||
}
|
.on('touchmove touchend', function (e) {
|
||||||
$widget.click(function(evt) {
|
var wid = $widget.width();
|
||||||
var t = $(evt.target);
|
var left = $widget.offset().left;
|
||||||
if (t.is('input[type=radio]')) {
|
var r =
|
||||||
showStars(rating = t.val());
|
((e.originalEvent.changedTouches[0].clientX - left) / wid) * 5 + 1;
|
||||||
if (!t.val()) {
|
r = ~~Math.min(Math.max(r, 1), 5);
|
||||||
// If the user caused a radio button to become unchecked,
|
setStars(r);
|
||||||
// re-check it because that shouldn't happen.
|
});
|
||||||
t.prop('checked', true);
|
$widget.html(rs);
|
||||||
}
|
$el.before($widget).detach();
|
||||||
}
|
});
|
||||||
}).mouseover(function(evt) {
|
return this;
|
||||||
var t = $(evt.target);
|
|
||||||
if (t.attr('data-stars')) {
|
|
||||||
showStars(t.attr('data-stars'));
|
|
||||||
}
|
|
||||||
}).mouseout(function() {
|
|
||||||
showStars(rating || 0);
|
|
||||||
}).on('touchmove touchend', function(e) {
|
|
||||||
var wid = $widget.width();
|
|
||||||
var left = $widget.offset().left;
|
|
||||||
var r = (e.originalEvent.changedTouches[0].clientX - left) / wid * 5 + 1;
|
|
||||||
r = ~~Math.min(Math.max(r,1),5);
|
|
||||||
setStars(r);
|
|
||||||
});
|
|
||||||
$widget.html(rs);
|
|
||||||
$el.before($widget).detach();
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
};
|
};
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,114 +2,138 @@
|
||||||
* extended by addonUploader(). Eventually imageUploader() should as well */
|
* extended by addonUploader(). Eventually imageUploader() should as well */
|
||||||
|
|
||||||
function fileSizeFormat(bytes) {
|
function fileSizeFormat(bytes) {
|
||||||
var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
var s = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
||||||
if(bytes === 0) return bytes + " " + s[1];
|
if (bytes === 0) return bytes + ' ' + s[1];
|
||||||
var e = Math.floor( Math.log(bytes) / Math.log(1024) );
|
var e = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||||
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
|
return (bytes / Math.pow(1024, Math.floor(e))).toFixed(2) + ' ' + s[e];
|
||||||
}
|
}
|
||||||
|
|
||||||
(function($) {
|
(function ($) {
|
||||||
function getErrors(results) {
|
function getErrors(results) {
|
||||||
return results.errors;
|
return results.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var settings = {'filetypes': [], 'getErrors': getErrors, 'cancel': $(), 'maxSize': null};
|
var settings = {
|
||||||
|
filetypes: [],
|
||||||
|
getErrors: getErrors,
|
||||||
|
cancel: $(),
|
||||||
|
maxSize: null,
|
||||||
|
};
|
||||||
|
|
||||||
$.fn.fileUploader = function( options ) {
|
$.fn.fileUploader = function (options) {
|
||||||
|
return $(this).each(function () {
|
||||||
|
var $upload_field = $(this),
|
||||||
|
formData = false,
|
||||||
|
errors = false,
|
||||||
|
aborted = false;
|
||||||
|
|
||||||
return $(this).each(function(){
|
if (options) {
|
||||||
var $upload_field = $(this),
|
$.extend(settings, options);
|
||||||
formData = false,
|
}
|
||||||
errors = false,
|
|
||||||
aborted = false;
|
|
||||||
|
|
||||||
if (options) {
|
$upload_field.on('change', uploaderStart);
|
||||||
$.extend( settings, options );
|
|
||||||
}
|
|
||||||
|
|
||||||
$upload_field.on("change", uploaderStart);
|
$(settings.cancel).click(
|
||||||
|
_pd(function () {
|
||||||
|
$upload_field.trigger('upload_action_abort');
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
$(settings.cancel).click(_pd(function(){
|
function uploaderStart() {
|
||||||
$upload_field.trigger('upload_action_abort');
|
if ($upload_field[0].files.length === 0) {
|
||||||
}));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function uploaderStart() {
|
var domfile = $upload_field[0].files[0],
|
||||||
if($upload_field[0].files.length === 0) {
|
url = $upload_field.attr('data-upload-url'),
|
||||||
return;
|
csrf = $('input[name=csrfmiddlewaretoken]').val(),
|
||||||
}
|
file = {
|
||||||
|
name: domfile.name || domfile.fileName,
|
||||||
|
size: domfile.size,
|
||||||
|
type: domfile.type,
|
||||||
|
};
|
||||||
|
|
||||||
var domfile = $upload_field[0].files[0],
|
formData = new z.FormData();
|
||||||
url = $upload_field.attr('data-upload-url'),
|
aborted = false;
|
||||||
csrf = $("input[name=csrfmiddlewaretoken]").val(),
|
|
||||||
file = {'name': domfile.name || domfile.fileName,
|
|
||||||
'size': domfile.size,
|
|
||||||
'type': domfile.type};
|
|
||||||
|
|
||||||
formData = new z.FormData();
|
$upload_field.trigger('upload_start', [file]);
|
||||||
aborted = false;
|
|
||||||
|
|
||||||
$upload_field.trigger("upload_start", [file]);
|
/* Disable uploading while something is uploading */
|
||||||
|
$upload_field.prop('disabled', true);
|
||||||
/* Disable uploading while something is uploading */
|
$upload_field.parent().find('a').addClass('disabled');
|
||||||
$upload_field.prop('disabled', true);
|
$upload_field.on('reenable_uploader', function () {
|
||||||
$upload_field.parent().find('a').addClass("disabled");
|
$upload_field.prop('disabled', false);
|
||||||
$upload_field.on("reenable_uploader", function() {
|
$upload_field.parent().find('a').removeClass('disabled');
|
||||||
$upload_field.prop('disabled', false);
|
|
||||||
$upload_field.parent().find('a').removeClass("disabled");
|
|
||||||
});
|
|
||||||
|
|
||||||
var exts = new RegExp("\\\.("+settings.filetypes.join('|')+")$", "i");
|
|
||||||
|
|
||||||
if(!file.name.match(exts)) {
|
|
||||||
errors = [gettext("The filetype you uploaded isn't recognized.")];
|
|
||||||
|
|
||||||
$upload_field.trigger("upload_errors", [file, errors]);
|
|
||||||
$upload_field.trigger("upload_finished", [file]);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.maxSize && domfile.size > settings.maxSize) {
|
|
||||||
errors = [format(gettext("Your file exceeds the maximum size of {0}."), [fileSizeFormat(settings.maxSize)])];
|
|
||||||
$upload_field.trigger("upload_errors", [file, errors]);
|
|
||||||
$upload_field.trigger("upload_finished", [file]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We should be good to go!
|
|
||||||
formData.open("POST", url, true);
|
|
||||||
formData.append("csrfmiddlewaretoken", csrf);
|
|
||||||
if(options.appendFormData) {
|
|
||||||
options.appendFormData(formData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(domfile instanceof File) { // Needed b/c of tests.
|
|
||||||
formData.append("upload", domfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
$upload_field.off("upload_action_abort").on("upload_action_abort", function() {
|
|
||||||
aborted = true;
|
|
||||||
formData.xhr.abort();
|
|
||||||
errors = [gettext("You cancelled the upload.")];
|
|
||||||
$upload_field.trigger("upload_errors", [file, errors]);
|
|
||||||
$upload_field.trigger("upload_finished", [file]);
|
|
||||||
});
|
|
||||||
|
|
||||||
formData.xhr.upload.addEventListener("progress", function(e) {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
var pct = Math.round((e.loaded * 100) / e.total);
|
|
||||||
$upload_field.trigger("upload_progress", [file, pct]);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
formData.xhr.onreadystatechange = function(){
|
|
||||||
$upload_field.trigger("upload_onreadystatechange",
|
|
||||||
[file, formData.xhr, aborted]);
|
|
||||||
};
|
|
||||||
|
|
||||||
formData.send();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
var exts = new RegExp(
|
||||||
|
'\\.(' + settings.filetypes.join('|') + ')$',
|
||||||
|
'i',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!file.name.match(exts)) {
|
||||||
|
errors = [gettext("The filetype you uploaded isn't recognized.")];
|
||||||
|
|
||||||
|
$upload_field.trigger('upload_errors', [file, errors]);
|
||||||
|
$upload_field.trigger('upload_finished', [file]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.maxSize && domfile.size > settings.maxSize) {
|
||||||
|
errors = [
|
||||||
|
format(gettext('Your file exceeds the maximum size of {0}.'), [
|
||||||
|
fileSizeFormat(settings.maxSize),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
$upload_field.trigger('upload_errors', [file, errors]);
|
||||||
|
$upload_field.trigger('upload_finished', [file]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should be good to go!
|
||||||
|
formData.open('POST', url, true);
|
||||||
|
formData.append('csrfmiddlewaretoken', csrf);
|
||||||
|
if (options.appendFormData) {
|
||||||
|
options.appendFormData(formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domfile instanceof File) {
|
||||||
|
// Needed b/c of tests.
|
||||||
|
formData.append('upload', domfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
$upload_field
|
||||||
|
.off('upload_action_abort')
|
||||||
|
.on('upload_action_abort', function () {
|
||||||
|
aborted = true;
|
||||||
|
formData.xhr.abort();
|
||||||
|
errors = [gettext('You cancelled the upload.')];
|
||||||
|
$upload_field.trigger('upload_errors', [file, errors]);
|
||||||
|
$upload_field.trigger('upload_finished', [file]);
|
||||||
|
});
|
||||||
|
|
||||||
|
formData.xhr.upload.addEventListener(
|
||||||
|
'progress',
|
||||||
|
function (e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
var pct = Math.round((e.loaded * 100) / e.total);
|
||||||
|
$upload_field.trigger('upload_progress', [file, pct]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
formData.xhr.onreadystatechange = function () {
|
||||||
|
$upload_field.trigger('upload_onreadystatechange', [
|
||||||
|
file,
|
||||||
|
formData.xhr,
|
||||||
|
aborted,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
formData.send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -34,117 +34,122 @@
|
||||||
file in the upload box when the "onchange" event is fired.]
|
file in the upload box when the "onchange" event is fired.]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// Get an object URL across browsers.
|
// Get an object URL across browsers.
|
||||||
$.fn.objectUrl = function(offset) {
|
$.fn.objectUrl = function (offset) {
|
||||||
var files = $(this)[0].files,
|
var files = $(this)[0].files,
|
||||||
url = false;
|
url = false;
|
||||||
if (z.capabilities.fileAPI && files.length) {
|
if (z.capabilities.fileAPI && files.length) {
|
||||||
offset = offset || 0;
|
offset = offset || 0;
|
||||||
var f = files[offset];
|
var f = files[offset];
|
||||||
if (typeof window.URL !== 'undefined') {
|
if (typeof window.URL !== 'undefined') {
|
||||||
url = window.URL.createObjectURL(f);
|
url = window.URL.createObjectURL(f);
|
||||||
} else if (typeof window.webkitURL == 'function') {
|
} else if (typeof window.webkitURL == 'function') {
|
||||||
url = window.webkitURL.createObjectURL(f);
|
url = window.webkitURL.createObjectURL(f);
|
||||||
} else if(typeof f.getAsDataURL == 'function') {
|
} else if (typeof f.getAsDataURL == 'function') {
|
||||||
url = f.getAsDataURL();
|
url = f.getAsDataURL();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return url;
|
}
|
||||||
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
|
var instance_id = 0;
|
||||||
|
|
||||||
(function($) {
|
$.fn.imageUploader = function () {
|
||||||
var instance_id = 0;
|
var $upload_field = this,
|
||||||
|
outstanding_uploads = 0,
|
||||||
|
files = $upload_field[0].files,
|
||||||
|
url = $upload_field.attr('data-upload-url'),
|
||||||
|
csrf = $upload_field.closest('form').find('input[name^=csrf]').val();
|
||||||
|
|
||||||
$.fn.imageUploader = function() {
|
// No files? No API support? No shirt? No service.
|
||||||
var $upload_field = this,
|
if (!z.capabilities.fileAPI || files.length === 0) {
|
||||||
outstanding_uploads = 0,
|
return false;
|
||||||
files = $upload_field[0].files,
|
}
|
||||||
url = $upload_field.attr('data-upload-url'),
|
|
||||||
csrf = $upload_field.closest('form').find('input[name^=csrf]').val();
|
|
||||||
|
|
||||||
// No files? No API support? No shirt? No service.
|
$upload_field.trigger('upload_start_all');
|
||||||
if (!z.capabilities.fileAPI || files.length === 0) {
|
|
||||||
return false;
|
// Loop through the files.
|
||||||
|
$.each(files, function (v, f) {
|
||||||
|
var data = '',
|
||||||
|
file = {
|
||||||
|
instance: instance_id,
|
||||||
|
name: f.name || f.fileName,
|
||||||
|
size: f.size,
|
||||||
|
type: f.type,
|
||||||
|
aborted: false,
|
||||||
|
dataURL: false,
|
||||||
|
},
|
||||||
|
finished = function () {
|
||||||
|
outstanding_uploads--;
|
||||||
|
if (outstanding_uploads <= 0) {
|
||||||
|
$upload_field.trigger('upload_finished_all');
|
||||||
|
}
|
||||||
|
$upload_field.trigger('upload_finished', [file]);
|
||||||
|
},
|
||||||
|
formData = new z.FormData();
|
||||||
|
|
||||||
|
instance_id++;
|
||||||
|
outstanding_uploads++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
$upload_field.attr('data-allowed-types').split('|').indexOf(file.type) <
|
||||||
|
0
|
||||||
|
) {
|
||||||
|
var errors = [gettext('Images must be either PNG or JPG.')];
|
||||||
|
if (typeof $upload_field.attr('multiple') !== 'undefined') {
|
||||||
|
// If we have a `multiple` attribute, assume not an icon.
|
||||||
|
if ($upload_field.attr('data-allowed-types').indexOf('video') > -1) {
|
||||||
|
errors.push([gettext('Videos must be in WebM.')]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$upload_field.trigger('upload_start', [file]);
|
||||||
|
$upload_field.trigger('upload_errors', [file, errors]);
|
||||||
|
finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$upload_field.trigger("upload_start_all");
|
file.dataURL = $upload_field.objectUrl(v);
|
||||||
|
|
||||||
// Loop through the files.
|
// And we're off!
|
||||||
$.each(files, function(v, f){
|
$upload_field.trigger('upload_start', [file]);
|
||||||
var data = "",
|
|
||||||
file = {
|
|
||||||
'instance': instance_id,
|
|
||||||
'name': f.name || f.fileName,
|
|
||||||
'size': f.size,
|
|
||||||
'type': f.type,
|
|
||||||
'aborted': false,
|
|
||||||
'dataURL': false},
|
|
||||||
finished = function(){
|
|
||||||
outstanding_uploads--;
|
|
||||||
if(outstanding_uploads <= 0) {
|
|
||||||
$upload_field.trigger("upload_finished_all");
|
|
||||||
}
|
|
||||||
$upload_field.trigger("upload_finished", [file]);
|
|
||||||
},
|
|
||||||
formData = new z.FormData();
|
|
||||||
|
|
||||||
instance_id++;
|
// Set things up
|
||||||
outstanding_uploads++;
|
formData.open('POST', url, true);
|
||||||
|
formData.append('csrfmiddlewaretoken', csrf);
|
||||||
|
formData.append('upload_image', f);
|
||||||
|
|
||||||
if ($upload_field.attr('data-allowed-types').split('|').indexOf(file.type) < 0) {
|
// Monitor progress and report back.
|
||||||
var errors = [gettext('Images must be either PNG or JPG.')];
|
formData.xhr.onreadystatechange = function () {
|
||||||
if (typeof $upload_field.attr('multiple') !== 'undefined') {
|
if (
|
||||||
// If we have a `multiple` attribute, assume not an icon.
|
formData.xhr.readyState == 4 &&
|
||||||
if ($upload_field.attr('data-allowed-types').indexOf('video') > -1) {
|
formData.xhr.responseText &&
|
||||||
errors.push([gettext('Videos must be in WebM.')]);
|
(formData.xhr.status == 200 || formData.xhr.status == 304)
|
||||||
}
|
) {
|
||||||
}
|
var json = {};
|
||||||
$upload_field.trigger('upload_start', [file]);
|
try {
|
||||||
$upload_field.trigger('upload_errors', [file, errors]);
|
json = JSON.parse(formData.xhr.responseText);
|
||||||
finished();
|
} catch (err) {
|
||||||
return;
|
var error = gettext('There was a problem contacting the server.');
|
||||||
}
|
$upload_field.trigger('upload_errors', [file, error]);
|
||||||
|
finished();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
file.dataURL = $upload_field.objectUrl(v);
|
if (json.errors.length) {
|
||||||
|
$upload_field.trigger('upload_errors', [file, json.errors]);
|
||||||
|
} else {
|
||||||
|
$upload_field.trigger('upload_success', [file, json.upload_hash]);
|
||||||
|
}
|
||||||
|
finished();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// And we're off!
|
// Actually do the sending.
|
||||||
$upload_field.trigger("upload_start", [file]);
|
formData.send();
|
||||||
|
});
|
||||||
|
|
||||||
// Set things up
|
// Clear out images, since we uploaded them.
|
||||||
formData.open("POST", url, true);
|
$upload_field.val('');
|
||||||
formData.append("csrfmiddlewaretoken", csrf);
|
};
|
||||||
formData.append("upload_image", f);
|
|
||||||
|
|
||||||
// Monitor progress and report back.
|
|
||||||
formData.xhr.onreadystatechange = function(){
|
|
||||||
if (formData.xhr.readyState == 4 && formData.xhr.responseText &&
|
|
||||||
(formData.xhr.status == 200 || formData.xhr.status == 304)) {
|
|
||||||
var json = {};
|
|
||||||
try {
|
|
||||||
json = JSON.parse(formData.xhr.responseText);
|
|
||||||
} catch(err) {
|
|
||||||
var error = gettext("There was a problem contacting the server.");
|
|
||||||
$upload_field.trigger("upload_errors", [file, error]);
|
|
||||||
finished();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(json.errors.length) {
|
|
||||||
$upload_field.trigger("upload_errors", [file, json.errors]);
|
|
||||||
} else {
|
|
||||||
$upload_field.trigger("upload_success", [file, json.upload_hash]);
|
|
||||||
}
|
|
||||||
finished();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Actually do the sending.
|
|
||||||
formData.send();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear out images, since we uploaded them.
|
|
||||||
$upload_field.val("");
|
|
||||||
};
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
if(document.getElementById('live_refresh')) {
|
if (document.getElementById('live_refresh')) {
|
||||||
less.env = "development";
|
less.env = 'development';
|
||||||
less.watch();
|
less.watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
var links = document.getElementsByTagName('link');
|
var links = document.getElementsByTagName('link');
|
||||||
for (var i = 0; i < links.length; i++) {
|
for (var i = 0; i < links.length; i++) {
|
||||||
if (/\.less($|\?)/.test(links[i].href)) {
|
if (/\.less($|\?)/.test(links[i].href)) {
|
||||||
links[i].type = "text/x-less";
|
links[i].type = 'text/x-less';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
$(function() {
|
$(function () {
|
||||||
var $abuse = $('fieldset.abuse');
|
var $abuse = $('fieldset.abuse');
|
||||||
if ($abuse.find('legend a').length) {
|
if ($abuse.find('legend a').length) {
|
||||||
var $ol = $abuse.find('ol');
|
var $ol = $abuse.find('ol');
|
||||||
$ol.hide();
|
$ol.hide();
|
||||||
$abuse.find('legend a, .cancel').click(_pd(function() {
|
$abuse.find('legend a, .cancel').click(
|
||||||
$ol.slideToggle('fast');
|
_pd(function () {
|
||||||
}));
|
$ol.slideToggle('fast');
|
||||||
}
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,166 +1,178 @@
|
||||||
$(function () {
|
$(function () {
|
||||||
if (!$("body").hasClass('addon-details')) return;
|
if (!$('body').hasClass('addon-details')) return;
|
||||||
|
|
||||||
if ($("body.restyle").length === 1) {
|
if ($('body.restyle').length === 1) {
|
||||||
$('#background-wrapper').height(
|
$('#background-wrapper').height(
|
||||||
$('.amo-header').height() +
|
$('.amo-header').height() +
|
||||||
($('.notification-box').length ? 80 : 0) +
|
($('.notification-box').length ? 80 : 0) +
|
||||||
$('.addon-description-header').height() + 20
|
$('.addon-description-header').height() +
|
||||||
);
|
20,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.previews').zCarousel({
|
||||||
|
btnNext: '.previews .next',
|
||||||
|
btnPrev: '.previews .prev',
|
||||||
|
itemsPerPage: 3,
|
||||||
|
});
|
||||||
|
(function () {
|
||||||
|
var $document = $(document),
|
||||||
|
$lightbox = $('#lightbox'),
|
||||||
|
$content = $('#lightbox .content'),
|
||||||
|
$caption = $('#lightbox .caption span'),
|
||||||
|
$previews = $('.previews'),
|
||||||
|
current,
|
||||||
|
$strip,
|
||||||
|
lbImage = template('<img id="preview{0}" src="{1}" alt="">');
|
||||||
|
if (!$lightbox.length) return;
|
||||||
|
function showLightbox() {
|
||||||
|
$lightbox.show();
|
||||||
|
showImage(this);
|
||||||
|
$(window).on('keydown.lightboxDismiss', function (e) {
|
||||||
|
switch (e.which) {
|
||||||
|
case z.keys.ESCAPE:
|
||||||
|
e.preventDefault();
|
||||||
|
hideLightbox();
|
||||||
|
break;
|
||||||
|
case z.keys.LEFT:
|
||||||
|
e.preventDefault();
|
||||||
|
showPrev();
|
||||||
|
break;
|
||||||
|
case z.keys.RIGHT:
|
||||||
|
e.preventDefault();
|
||||||
|
showNext();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//I want to ensure the lightbox is painted before fading it in.
|
||||||
|
setTimeout(function () {
|
||||||
|
$lightbox.addClass('show');
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
function hideLightbox() {
|
||||||
$(".previews").zCarousel({
|
$lightbox.removeClass('show');
|
||||||
btnNext: ".previews .next",
|
// We can't trust transitionend to fire in all cases.
|
||||||
btnPrev: ".previews .prev",
|
setTimeout(function () {
|
||||||
itemsPerPage: 3
|
$lightbox.hide();
|
||||||
});
|
}, 500);
|
||||||
(function() {
|
$(window).off('keydown.lightboxDismiss');
|
||||||
var $document = $(document),
|
}
|
||||||
$lightbox = $("#lightbox"),
|
function showImage(a) {
|
||||||
$content = $("#lightbox .content"),
|
var $a = $(a),
|
||||||
$caption = $("#lightbox .caption span"),
|
$oldimg = $lightbox.find('img');
|
||||||
$previews = $('.previews'),
|
current = $a.parent().index();
|
||||||
current, $strip,
|
$strip = $a.closest('ul').find('li');
|
||||||
lbImage = template('<img id="preview{0}" src="{1}" alt="">');
|
$previews
|
||||||
if (!$lightbox.length) return;
|
.find('.panel')
|
||||||
function showLightbox() {
|
.removeClass('active')
|
||||||
$lightbox.show();
|
.eq(current)
|
||||||
showImage(this);
|
.addClass('active');
|
||||||
$(window).on('keydown.lightboxDismiss', function(e) {
|
var $img = $('#preview' + current);
|
||||||
switch(e.which) {
|
if ($img.length) {
|
||||||
case z.keys.ESCAPE:
|
$oldimg.css({ opacity: '0', 'z-index': '0' });
|
||||||
e.preventDefault();
|
$img.css({
|
||||||
hideLightbox();
|
opacity: '1',
|
||||||
break;
|
'z-index': '1',
|
||||||
case z.keys.LEFT:
|
|
||||||
e.preventDefault();
|
|
||||||
showPrev();
|
|
||||||
break;
|
|
||||||
case z.keys.RIGHT:
|
|
||||||
e.preventDefault();
|
|
||||||
showNext();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//I want to ensure the lightbox is painted before fading it in.
|
|
||||||
setTimeout(function () {
|
|
||||||
$lightbox.addClass("show");
|
|
||||||
},0);
|
|
||||||
}
|
|
||||||
function hideLightbox() {
|
|
||||||
$lightbox.removeClass("show");
|
|
||||||
// We can't trust transitionend to fire in all cases.
|
|
||||||
setTimeout(function() {
|
|
||||||
$lightbox.hide();
|
|
||||||
}, 500);
|
|
||||||
$(window).off('keydown.lightboxDismiss');
|
|
||||||
}
|
|
||||||
function showImage(a) {
|
|
||||||
var $a = $(a),
|
|
||||||
$oldimg = $lightbox.find("img");
|
|
||||||
current = $a.parent().index();
|
|
||||||
$strip = $a.closest("ul").find("li");
|
|
||||||
$previews.find('.panel').removeClass('active')
|
|
||||||
.eq(current).addClass('active');
|
|
||||||
var $img = $("#preview"+current);
|
|
||||||
if ($img.length) {
|
|
||||||
$oldimg.css({"opacity": "0", "z-index": "0"});
|
|
||||||
$img.css({
|
|
||||||
"opacity": "1", "z-index": "1"
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$img = $(lbImage([current, $a.attr("href")]));
|
|
||||||
$content.append($img);
|
|
||||||
$img.on("load", function(e) {
|
|
||||||
$oldimg.css({"opacity": "0", "z-index": "0"});
|
|
||||||
$img.css({
|
|
||||||
"opacity": "1", "z-index": "1"
|
|
||||||
});
|
|
||||||
for (var i=0; i<$strip.length; i++) {
|
|
||||||
if (i != current) {
|
|
||||||
var $p = $strip.eq(i).find("a");
|
|
||||||
$content.append(lbImage([i, $p.attr("href")]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$caption.text($a.attr("title"));
|
|
||||||
$lightbox.find(".control").removeClass("disabled");
|
|
||||||
if (current < 1) {
|
|
||||||
$lightbox.find(".control.prev").addClass("disabled");
|
|
||||||
}
|
|
||||||
if (current == $strip.length-1){
|
|
||||||
$lightbox.find(".control.next").addClass("disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function showNext() {
|
|
||||||
if (current < $strip.length-1) {
|
|
||||||
showImage($strip.eq(current+1).find("a"));
|
|
||||||
if (!this.window) {
|
|
||||||
$(this).blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function showPrev() {
|
|
||||||
if (current > 0) {
|
|
||||||
showImage($strip.eq(current-1).find("a"));
|
|
||||||
if (!this.window) {
|
|
||||||
$(this).blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$("#lightbox .next").click(_pd(showNext));
|
|
||||||
$("#lightbox .prev").click(_pd(showPrev));
|
|
||||||
$(".previews ul a").click(_pd(showLightbox));
|
|
||||||
$('#lightbox').click(_pd(function(e) {
|
|
||||||
if ($(e.target).is('.close, #lightbox')) {
|
|
||||||
hideLightbox();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
})();
|
|
||||||
|
|
||||||
if ($('#more-webpage').exists()) {
|
|
||||||
var $moreEl = $('#more-webpage');
|
|
||||||
url = $moreEl.attr('data-more-url');
|
|
||||||
$.get(url, function(resp) {
|
|
||||||
var $document = $(document);
|
|
||||||
var scrollTop = $document.scrollTop();
|
|
||||||
var origHeight = $document.height();
|
|
||||||
|
|
||||||
// We need to correct scrolling position if the user scrolled down
|
|
||||||
// already (e.g. by using a link with anchor). This correction is
|
|
||||||
// only necessary if the scrolling position is below the element we
|
|
||||||
// replace or the user scrolled down to the bottom of the document.
|
|
||||||
var shouldCorrectScrolling = scrollTop > $moreEl.offset().top;
|
|
||||||
if (scrollTop && scrollTop >= origHeight - $(window).height()) {
|
|
||||||
shouldCorrectScrolling = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip the leading whitespace so that $() treats this as html and
|
|
||||||
// not a selector.
|
|
||||||
var $newContent = $(resp.trim());
|
|
||||||
$moreEl.replaceWith($newContent);
|
|
||||||
$newContent.find('.listing-grid h3').truncate( {dir: 'h'} );
|
|
||||||
$newContent.find('.install').installButton();
|
|
||||||
$newContent.find('.listing-grid').each(listing_grid);
|
|
||||||
$('#reviews-link').addClass('scrollto').attr('href', '#reviews');
|
|
||||||
|
|
||||||
if (shouldCorrectScrolling) {
|
|
||||||
// User scrolled down already, adjust scrolling position so
|
|
||||||
// that the same content stays visible.
|
|
||||||
var heightDifference = $document.height() - origHeight;
|
|
||||||
$document.scrollTop(scrollTop + heightDifference);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
$img = $(lbImage([current, $a.attr('href')]));
|
||||||
|
$content.append($img);
|
||||||
|
$img.on('load', function (e) {
|
||||||
|
$oldimg.css({ opacity: '0', 'z-index': '0' });
|
||||||
|
$img.css({
|
||||||
|
opacity: '1',
|
||||||
|
'z-index': '1',
|
||||||
|
});
|
||||||
|
for (var i = 0; i < $strip.length; i++) {
|
||||||
|
if (i != current) {
|
||||||
|
var $p = $strip.eq(i).find('a');
|
||||||
|
$content.append(lbImage([i, $p.attr('href')]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$caption.text($a.attr('title'));
|
||||||
|
$lightbox.find('.control').removeClass('disabled');
|
||||||
|
if (current < 1) {
|
||||||
|
$lightbox.find('.control.prev').addClass('disabled');
|
||||||
|
}
|
||||||
|
if (current == $strip.length - 1) {
|
||||||
|
$lightbox.find('.control.next').addClass('disabled');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
function showNext() {
|
||||||
|
if (current < $strip.length - 1) {
|
||||||
|
showImage($strip.eq(current + 1).find('a'));
|
||||||
|
if (!this.window) {
|
||||||
|
$(this).blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showPrev() {
|
||||||
|
if (current > 0) {
|
||||||
|
showImage($strip.eq(current - 1).find('a'));
|
||||||
|
if (!this.window) {
|
||||||
|
$(this).blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('#lightbox .next').click(_pd(showNext));
|
||||||
|
$('#lightbox .prev').click(_pd(showPrev));
|
||||||
|
$('.previews ul a').click(_pd(showLightbox));
|
||||||
|
$('#lightbox').click(
|
||||||
|
_pd(function (e) {
|
||||||
|
if ($(e.target).is('.close, #lightbox')) {
|
||||||
|
hideLightbox();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
if ($('#review-add-box').exists())
|
if ($('#more-webpage').exists()) {
|
||||||
$('#review-add-box').modal('#add-review', { delegate: '#page', width: '650px' });
|
var $moreEl = $('#more-webpage');
|
||||||
|
url = $moreEl.attr('data-more-url');
|
||||||
|
$.get(url, function (resp) {
|
||||||
|
var $document = $(document);
|
||||||
|
var scrollTop = $document.scrollTop();
|
||||||
|
var origHeight = $document.height();
|
||||||
|
|
||||||
if ($('#privacy-policy').exists())
|
// We need to correct scrolling position if the user scrolled down
|
||||||
$('#privacy-policy').modal('.privacy-policy', { width: '500px' });
|
// already (e.g. by using a link with anchor). This correction is
|
||||||
if ($('#webext-permissions').exists())
|
// only necessary if the scrolling position is below the element we
|
||||||
$('#webext-permissions').modal('.webext-permissions', { width: '500px' });
|
// replace or the user scrolled down to the bottom of the document.
|
||||||
|
var shouldCorrectScrolling = scrollTop > $moreEl.offset().top;
|
||||||
|
if (scrollTop && scrollTop >= origHeight - $(window).height()) {
|
||||||
|
shouldCorrectScrolling = true;
|
||||||
|
}
|
||||||
|
|
||||||
$('#abuse-modal').modal('#report-abuse', {delegate: '#page'});
|
// Strip the leading whitespace so that $() treats this as html and
|
||||||
|
// not a selector.
|
||||||
|
var $newContent = $(resp.trim());
|
||||||
|
$moreEl.replaceWith($newContent);
|
||||||
|
$newContent.find('.listing-grid h3').truncate({ dir: 'h' });
|
||||||
|
$newContent.find('.install').installButton();
|
||||||
|
$newContent.find('.listing-grid').each(listing_grid);
|
||||||
|
$('#reviews-link').addClass('scrollto').attr('href', '#reviews');
|
||||||
|
|
||||||
|
if (shouldCorrectScrolling) {
|
||||||
|
// User scrolled down already, adjust scrolling position so
|
||||||
|
// that the same content stays visible.
|
||||||
|
var heightDifference = $document.height() - origHeight;
|
||||||
|
$document.scrollTop(scrollTop + heightDifference);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($('#review-add-box').exists())
|
||||||
|
$('#review-add-box').modal('#add-review', {
|
||||||
|
delegate: '#page',
|
||||||
|
width: '650px',
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($('#privacy-policy').exists())
|
||||||
|
$('#privacy-policy').modal('.privacy-policy', { width: '500px' });
|
||||||
|
if ($('#webext-permissions').exists())
|
||||||
|
$('#webext-permissions').modal('.webext-permissions', { width: '500px' });
|
||||||
|
|
||||||
|
$('#abuse-modal').modal('#report-abuse', { delegate: '#page' });
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,98 +1,97 @@
|
||||||
function objEqual(a, b) {
|
function objEqual(a, b) {
|
||||||
return JSON.stringify(a) == JSON.stringify(b);
|
return JSON.stringify(a) == JSON.stringify(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
z._AjaxCache = {};
|
z._AjaxCache = {};
|
||||||
z.AjaxCache = (function() {
|
z.AjaxCache = (function () {
|
||||||
return function(namespace) {
|
return function (namespace) {
|
||||||
if (z._AjaxCache[namespace] === undefined) {
|
if (z._AjaxCache[namespace] === undefined) {
|
||||||
z._AjaxCache[namespace] = {
|
z._AjaxCache[namespace] = {
|
||||||
'previous': {'args': '', 'data': ''},
|
previous: { args: '', data: '' },
|
||||||
'items': {}
|
items: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return z._AjaxCache[namespace];
|
return z._AjaxCache[namespace];
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
(function($) {
|
$.ajaxCache = function (o) {
|
||||||
|
o = $.extend(
|
||||||
$.ajaxCache = function(o) {
|
{
|
||||||
o = $.extend({
|
|
||||||
url: '',
|
url: '',
|
||||||
type: 'get',
|
type: 'get',
|
||||||
data: {}, // Key/value pairs of form data.
|
data: {}, // Key/value pairs of form data.
|
||||||
newItems: $.noop, // Callback upon success of items fetched.
|
newItems: $.noop, // Callback upon success of items fetched.
|
||||||
cacheSuccess: $.noop, // Callback upon success of items fetched
|
cacheSuccess: $.noop, // Callback upon success of items fetched
|
||||||
// in cache.
|
// in cache.
|
||||||
ajaxSuccess: $.noop, // Callback upon success of Ajax request.
|
ajaxSuccess: $.noop, // Callback upon success of Ajax request.
|
||||||
ajaxFailure: $.noop // Callback upon failure of Ajax request.
|
ajaxFailure: $.noop, // Callback upon failure of Ajax request.
|
||||||
}, o);
|
},
|
||||||
|
o,
|
||||||
|
);
|
||||||
|
|
||||||
if (!z.capabilities.JSON || parseFloat(jQuery.fn.jquery) < 1.5) {
|
if (!z.capabilities.JSON || parseFloat(jQuery.fn.jquery) < 1.5) {
|
||||||
// jqXHR objects allow Deferred methods as of jQuery 1.5. Some of our
|
// jqXHR objects allow Deferred methods as of jQuery 1.5. Some of our
|
||||||
// old pages are stuck on jQuery 1.4, so hopefully this'll disappear
|
// old pages are stuck on jQuery 1.4, so hopefully this'll disappear
|
||||||
// sooner than later.
|
// sooner than later.
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url: o.url,
|
url: o.url,
|
||||||
type: o.method,
|
type: o.method,
|
||||||
data: o.data,
|
data: o.data,
|
||||||
success: function(data) {
|
success: function (data) {
|
||||||
o.newItems(data, data);
|
o.newItems(data, data);
|
||||||
o.ajaxSuccess(data, items);
|
o.ajaxSuccess(data, items);
|
||||||
},
|
},
|
||||||
errors: function(data) {
|
errors: function (data) {
|
||||||
o.ajaxFailure(data);
|
o.ajaxFailure(data);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var cache = z.AjaxCache(o.url + ':' + o.type),
|
var cache = z.AjaxCache(o.url + ':' + o.type),
|
||||||
args = JSON.stringify(o.data),
|
args = JSON.stringify(o.data),
|
||||||
previous_args = JSON.stringify(cache.previous.args),
|
previous_args = JSON.stringify(cache.previous.args),
|
||||||
items,
|
items,
|
||||||
request;
|
request;
|
||||||
|
|
||||||
if (args != previous_args) {
|
if (args != previous_args) {
|
||||||
if (!!cache.items[args]) {
|
if (!!cache.items[args]) {
|
||||||
items = cache.items[args];
|
items = cache.items[args];
|
||||||
if (o.newItems) {
|
if (o.newItems) {
|
||||||
o.newItems(null, items);
|
o.newItems(null, items);
|
||||||
}
|
|
||||||
if (o.cacheSuccess) {
|
|
||||||
o.cacheSuccess(null, items);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Make a request to fetch new items.
|
|
||||||
request = $.ajax({url: o.url, type: o.method, data: o.data});
|
|
||||||
|
|
||||||
request.done(function(data) {
|
|
||||||
var items;
|
|
||||||
if (!objEqual(data, cache.previous.data)) {
|
|
||||||
items = data;
|
|
||||||
}
|
|
||||||
o.newItems(data, items);
|
|
||||||
o.ajaxSuccess(data, items);
|
|
||||||
|
|
||||||
// Store items returned from this request.
|
|
||||||
cache.items[args] = data;
|
|
||||||
|
|
||||||
// Store current list of items and form data (arguments).
|
|
||||||
cache.previous.data = data;
|
|
||||||
cache.previous.args = args;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Optional failure callback.
|
|
||||||
if (o.failure) {
|
|
||||||
request.fail(function(data) {
|
|
||||||
o.ajaxFailure(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (o.cacheSuccess) {
|
||||||
|
o.cacheSuccess(null, items);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Make a request to fetch new items.
|
||||||
|
request = $.ajax({ url: o.url, type: o.method, data: o.data });
|
||||||
|
|
||||||
|
request.done(function (data) {
|
||||||
|
var items;
|
||||||
|
if (!objEqual(data, cache.previous.data)) {
|
||||||
|
items = data;
|
||||||
|
}
|
||||||
|
o.newItems(data, items);
|
||||||
|
o.ajaxSuccess(data, items);
|
||||||
|
|
||||||
|
// Store items returned from this request.
|
||||||
|
cache.items[args] = data;
|
||||||
|
|
||||||
|
// Store current list of items and form data (arguments).
|
||||||
|
cache.previous.data = data;
|
||||||
|
cache.previous.args = args;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Optional failure callback.
|
||||||
|
if (o.failure) {
|
||||||
|
request.fail(function (data) {
|
||||||
|
o.ajaxFailure(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return request;
|
return request;
|
||||||
};
|
};
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
z.capabilities = {
|
z.capabilities = {
|
||||||
'JSON': window.JSON && typeof JSON.parse == 'function',
|
JSON: window.JSON && typeof JSON.parse == 'function',
|
||||||
'debug': (('' + document.location).indexOf('dbg') >= 0),
|
debug: ('' + document.location).indexOf('dbg') >= 0,
|
||||||
'debug_in_page': (('' + document.location).indexOf('dbginpage') >= 0),
|
debug_in_page: ('' + document.location).indexOf('dbginpage') >= 0,
|
||||||
'console': window.console && (typeof window.console.log == 'function'),
|
console: window.console && typeof window.console.log == 'function',
|
||||||
'replaceState': typeof history.replaceState === 'function',
|
replaceState: typeof history.replaceState === 'function',
|
||||||
'chromeless': window.locationbar && !window.locationbar.visible,
|
chromeless: window.locationbar && !window.locationbar.visible,
|
||||||
'localStorage': false,
|
localStorage: false,
|
||||||
'sessionStorage': false,
|
sessionStorage: false,
|
||||||
'fileAPI': !!window.FileReader,
|
fileAPI: !!window.FileReader,
|
||||||
'performance': !!(window.performance || window.msPerformance || window.webkitPerformance || window.mozPerformance),
|
performance: !!(
|
||||||
'webactivities': !!(window.setMessageHandler || window.mozSetMessageHandler)
|
window.performance ||
|
||||||
|
window.msPerformance ||
|
||||||
|
window.webkitPerformance ||
|
||||||
|
window.mozPerformance
|
||||||
|
),
|
||||||
|
webactivities: !!(window.setMessageHandler || window.mozSetMessageHandler),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ('localStorage' in window && window['localStorage'] !== null) {
|
||||||
|
z.capabilities.localStorage = true;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if ('localStorage' in window && window['localStorage'] !== null) {
|
if ('sessionStorage' in window && window['sessionStorage'] !== null) {
|
||||||
z.capabilities.localStorage = true;
|
z.capabilities.sessionStorage = true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if ('sessionStorage' in window && window['sessionStorage'] !== null) {
|
|
||||||
z.capabilities.sessionStorage = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,49 +5,52 @@
|
||||||
* handles fluid layouts like a champ!
|
* handles fluid layouts like a champ!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function($) {
|
(function ($) {
|
||||||
|
$.fn.zCarousel = function (o) {
|
||||||
$.fn.zCarousel = function(o) {
|
o = $.extend(
|
||||||
o = $.extend({
|
{
|
||||||
itemsPerPage: 1,
|
itemsPerPage: 1,
|
||||||
circular: false
|
circular: false,
|
||||||
}, o);
|
},
|
||||||
|
o,
|
||||||
|
);
|
||||||
|
|
||||||
var $self = $(this).eq(0),
|
var $self = $(this).eq(0),
|
||||||
$strip = $(".slider", $self),
|
$strip = $('.slider', $self),
|
||||||
$lis = $strip.find(".panel"),
|
$lis = $strip.find('.panel'),
|
||||||
$prev = $(o.btnPrev),
|
$prev = $(o.btnPrev),
|
||||||
$next = $(o.btnNext),
|
$next = $(o.btnNext),
|
||||||
prop = o.prop || ($("body").hasClass("html-rtl") ? "right" : "left"),
|
prop = o.prop || ($('body').hasClass('html-rtl') ? 'right' : 'left'),
|
||||||
currentPos = 0,
|
currentPos = 0,
|
||||||
maxPos = Math.ceil($lis.length / o.itemsPerPage);
|
maxPos = Math.ceil($lis.length / o.itemsPerPage);
|
||||||
|
|
||||||
if (!$strip.length) return $self;
|
if (!$strip.length) return $self;
|
||||||
|
|
||||||
function render(pos) {
|
function render(pos) {
|
||||||
if (o.circular) {
|
if (o.circular) {
|
||||||
currentPos = pos > maxPos+1 ? pos-maxPos : (pos < 0 ? pos+maxPos : pos);
|
currentPos =
|
||||||
if ($strip.hasClass("noslide")) {
|
pos > maxPos + 1 ? pos - maxPos : pos < 0 ? pos + maxPos : pos;
|
||||||
currentPos = (pos > maxPos) ? 1 : (pos < 1 ? maxPos : pos);
|
if ($strip.hasClass('noslide')) {
|
||||||
}
|
currentPos = pos > maxPos ? 1 : pos < 1 ? maxPos : pos;
|
||||||
} else {
|
|
||||||
currentPos = Math.min(Math.max(0, pos), maxPos-1);
|
|
||||||
}
|
}
|
||||||
$strip.css(prop, currentPos * -100 + "%");
|
} else {
|
||||||
$prev.toggleClass("disabled", currentPos == 0 && !o.circular);
|
currentPos = Math.min(Math.max(0, pos), maxPos - 1);
|
||||||
$next.toggleClass("disabled", currentPos == maxPos-1 && !o.circular);
|
}
|
||||||
//wait for paint to clear the class. lame.
|
$strip.css(prop, currentPos * -100 + '%');
|
||||||
setTimeout(function() {
|
$prev.toggleClass('disabled', currentPos == 0 && !o.circular);
|
||||||
$strip.removeClass('noslide');
|
$next.toggleClass('disabled', currentPos == maxPos - 1 && !o.circular);
|
||||||
}, 0);
|
//wait for paint to clear the class. lame.
|
||||||
|
setTimeout(function () {
|
||||||
|
$strip.removeClass('noslide');
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//wire up controls.
|
//wire up controls.
|
||||||
function fwd() {
|
function fwd() {
|
||||||
render(currentPos+1);
|
render(currentPos + 1);
|
||||||
}
|
}
|
||||||
function prev() {
|
function prev() {
|
||||||
render(currentPos-1);
|
render(currentPos - 1);
|
||||||
}
|
}
|
||||||
$next.click(_pd(fwd));
|
$next.click(_pd(fwd));
|
||||||
$prev.click(_pd(prev));
|
$prev.click(_pd(prev));
|
||||||
|
@ -56,30 +59,30 @@ $.fn.zCarousel = function(o) {
|
||||||
|
|
||||||
// Strip text nodes so inline-block works properly.
|
// Strip text nodes so inline-block works properly.
|
||||||
var cn = $strip[0].childNodes;
|
var cn = $strip[0].childNodes;
|
||||||
for(var i = 0; i < cn.length; i++) {
|
for (var i = 0; i < cn.length; i++) {
|
||||||
if (cn[i].nodeType == 3) {
|
if (cn[i].nodeType == 3) {
|
||||||
$strip[0].removeChild(cn[i]);
|
$strip[0].removeChild(cn[i]);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (o.circular) {
|
if (o.circular) {
|
||||||
//pad the beginning with a page from the end vice-versa.
|
//pad the beginning with a page from the end vice-versa.
|
||||||
$strip.prepend($lis.slice(-o.itemsPerPage).clone().addClass("cloned"))
|
$strip
|
||||||
.append($lis.slice(0,o.itemsPerPage).clone().addClass("cloned"));
|
.prepend($lis.slice(-o.itemsPerPage).clone().addClass('cloned'))
|
||||||
$strip.addClass('noslide');
|
.append($lis.slice(0, o.itemsPerPage).clone().addClass('cloned'));
|
||||||
$strip.on("transitionend webkitTransitionEnd", function() {
|
$strip.addClass('noslide');
|
||||||
if (currentPos > maxPos || currentPos < 1) {
|
$strip.on('transitionend webkitTransitionEnd', function () {
|
||||||
$strip.addClass("noslide");
|
if (currentPos > maxPos || currentPos < 1) {
|
||||||
setTimeout(function() {
|
$strip.addClass('noslide');
|
||||||
render(currentPos);
|
setTimeout(function () {
|
||||||
}, 0);
|
render(currentPos);
|
||||||
}
|
}, 0);
|
||||||
});
|
}
|
||||||
render(o.itemsPerPage);
|
});
|
||||||
|
render(o.itemsPerPage);
|
||||||
} else {
|
} else {
|
||||||
render(0);
|
render(0);
|
||||||
}
|
}
|
||||||
return $self;
|
return $self;
|
||||||
};
|
};
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
(function() {
|
(function () {
|
||||||
var $footer = $('#footer'),
|
var $footer = $('#footer'),
|
||||||
$page = $('#page'),
|
$page = $('#page'),
|
||||||
$win = $(window);
|
$win = $(window);
|
||||||
function stickyFooter() {
|
function stickyFooter() {
|
||||||
// Stick the footer to the bottom when there's head(foot)room.
|
// Stick the footer to the bottom when there's head(foot)room.
|
||||||
$footer.toggleClass('sticky', $win.height() - $footer.outerHeight() > $page.outerHeight());
|
$footer.toggleClass(
|
||||||
}
|
'sticky',
|
||||||
stickyFooter();
|
$win.height() - $footer.outerHeight() > $page.outerHeight(),
|
||||||
$win.resize(_.debounce(stickyFooter, 200));
|
);
|
||||||
|
}
|
||||||
|
stickyFooter();
|
||||||
|
$win.resize(_.debounce(stickyFooter, 200));
|
||||||
})();
|
})();
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
$(window).trigger('resize');
|
$(window).trigger('resize');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,31 +1,27 @@
|
||||||
function clearErrors(context) {
|
function clearErrors(context) {
|
||||||
$('.errorlist', context).remove();
|
$('.errorlist', context).remove();
|
||||||
$('.error', context).removeClass('error');
|
$('.error', context).removeClass('error');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function populateErrors(context, o) {
|
function populateErrors(context, o) {
|
||||||
clearErrors(context);
|
clearErrors(context);
|
||||||
var $list = $('<ul class="errorlist"></ul>');
|
var $list = $('<ul class="errorlist"></ul>');
|
||||||
$.each(o, function(i, v) {
|
$.each(o, function (i, v) {
|
||||||
var $row = $('[name=' + i + ']', context).closest('.row');
|
var $row = $('[name=' + i + ']', context).closest('.row');
|
||||||
$row.addClass('error');
|
$row.addClass('error');
|
||||||
$row.append($list.append($(format('<li>{0}</li>', _.escape(v)))));
|
$row.append($list.append($(format('<li>{0}</li>', _.escape(v)))));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function fieldFocused(e) {
|
function fieldFocused(e) {
|
||||||
var tags = /input|keygen|meter|option|output|progress|select|textarea/i;
|
var tags = /input|keygen|meter|option|output|progress|select|textarea/i;
|
||||||
return tags.test(e.target.nodeName);
|
return tags.test(e.target.nodeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function postUnsaved(data) {
|
function postUnsaved(data) {
|
||||||
$('input[name="unsaved_data"]').val(JSON.stringify(data));
|
$('input[name="unsaved_data"]').val(JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function loadUnsaved() {
|
function loadUnsaved() {
|
||||||
return JSON.parse($('input[name="unsaved_data"]').val() || '{}');
|
return JSON.parse($('input[name="unsaved_data"]').val() || '{}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,232 +1,245 @@
|
||||||
function updateTotalForms(prefix, inc) {
|
function updateTotalForms(prefix, inc) {
|
||||||
var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'),
|
var $totalForms = $('#id_' + prefix + '-TOTAL_FORMS'),
|
||||||
$maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'),
|
$maxForms = $('#id_' + prefix + '-MAX_NUM_FORMS'),
|
||||||
inc = inc || 1,
|
inc = inc || 1,
|
||||||
num = parseInt($totalForms.val(), 10) + inc;
|
num = parseInt($totalForms.val(), 10) + inc;
|
||||||
if ($maxForms.length && $maxForms.val().length) {
|
if ($maxForms.length && $maxForms.val().length) {
|
||||||
var maxNum = parseInt($maxForms.val(), 10);
|
var maxNum = parseInt($maxForms.val(), 10);
|
||||||
if (num > maxNum) {
|
if (num > maxNum) {
|
||||||
return num - 1;
|
return num - 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$totalForms.val(num);
|
}
|
||||||
return num;
|
$totalForms.val(num);
|
||||||
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* zAutoFormset: handles Django formsets with autocompletion like a champ!
|
* zAutoFormset: handles Django formsets with autocompletion like a champ!
|
||||||
* by cvan
|
* by cvan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function($) {
|
(function ($) {
|
||||||
|
$.zAutoFormset = function (o) {
|
||||||
|
o = $.extend(
|
||||||
|
{
|
||||||
|
delegate: document.body, // Delegate (probably some nearby parent).
|
||||||
|
|
||||||
$.zAutoFormset = function(o) {
|
forms: null, // Where all the forms live (maybe a <ul>).
|
||||||
|
|
||||||
o = $.extend({
|
extraForm: '.extra-form', // Selector for element that contains the
|
||||||
delegate: document.body, // Delegate (probably some nearby parent).
|
// HTML for extra-form template.
|
||||||
|
|
||||||
forms: null, // Where all the forms live (maybe a <ul>).
|
maxForms: 3, // Maximum number of forms allowed.
|
||||||
|
|
||||||
extraForm: '.extra-form', // Selector for element that contains the
|
prefix: 'form', // Formset prefix (Django default: 'form').
|
||||||
// HTML for extra-form template.
|
|
||||||
|
|
||||||
maxForms: 3, // Maximum number of forms allowed.
|
hiddenField: null, // This is the name of a (hidden) field
|
||||||
|
// that will contain the value of the
|
||||||
|
// formPK for each newly added form.
|
||||||
|
|
||||||
prefix: 'form', // Formset prefix (Django default: 'form').
|
removeClass: 'remove', // Class for button triggering form removal.
|
||||||
|
|
||||||
hiddenField: null, // This is the name of a (hidden) field
|
formSelector: 'li', // Selector for each form container.
|
||||||
// that will contain the value of the
|
|
||||||
// formPK for each newly added form.
|
|
||||||
|
|
||||||
removeClass: 'remove', // Class for button triggering form removal.
|
formPK: 'id', // Primary key for initial forms.
|
||||||
|
|
||||||
formSelector: 'li', // Selector for each form container.
|
src: null, // Source URL of JSON search results.
|
||||||
|
|
||||||
formPK: 'id', // Primary key for initial forms.
|
input: null, // Input field for autocompletion search.
|
||||||
|
|
||||||
src: null, // Source URL of JSON search results.
|
searchField: 'q', // Name of form field for search query.
|
||||||
|
|
||||||
input: null, // Input field for autocompletion search.
|
minSearchLength: 3, // Minimum character length for queries.
|
||||||
|
|
||||||
searchField: 'q', // Name of form field for search query.
|
width: 300, // Width (pixels) of autocomplete dropdown.
|
||||||
|
|
||||||
minSearchLength: 3, // Minimum character length for queries.
|
addedCB: null, // Callback for each new form added.
|
||||||
|
|
||||||
width: 300, // Width (pixels) of autocomplete dropdown.
|
removedCB: null, // Callback for each form removed.
|
||||||
|
|
||||||
addedCB: null, // Callback for each new form added.
|
autocomplete: null, // Custom handler you can provide to handle
|
||||||
|
// autocompletion yourself.
|
||||||
removedCB: null, // Callback for each form removed.
|
},
|
||||||
|
o,
|
||||||
autocomplete: null // Custom handler you can provide to handle
|
);
|
||||||
// autocompletion yourself.
|
|
||||||
}, o);
|
|
||||||
|
|
||||||
var $delegate = $(o.delegate),
|
var $delegate = $(o.delegate),
|
||||||
$forms = o.forms ? $delegate.find(o.forms) : $delegate,
|
$forms = o.forms ? $delegate.find(o.forms) : $delegate,
|
||||||
$extraForm = $delegate.find(o.extraForm),
|
$extraForm = $delegate.find(o.extraForm),
|
||||||
formsetPrefix = o.prefix,
|
formsetPrefix = o.prefix,
|
||||||
hiddenField = o.hiddenField,
|
hiddenField = o.hiddenField,
|
||||||
removeClass = o.removeClass,
|
removeClass = o.removeClass,
|
||||||
formSelector = o.formSelector,
|
formSelector = o.formSelector,
|
||||||
formPK = o.formPK,
|
formPK = o.formPK,
|
||||||
src = o.src || $delegate.attr('data-src'),
|
src = o.src || $delegate.attr('data-src'),
|
||||||
$input = o.input ? $(o.input) : $delegate.find('input.autocomplete'),
|
$input = o.input ? $(o.input) : $delegate.find('input.autocomplete'),
|
||||||
searchField = o.searchField,
|
searchField = o.searchField,
|
||||||
minLength = o.minSearchLength,
|
minLength = o.minSearchLength,
|
||||||
$maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'),
|
$maxForms = $('#id_' + formsetPrefix + '-MAX_NUM_FORMS'),
|
||||||
width = o.width,
|
width = o.width,
|
||||||
addedCB = o.addedCB,
|
addedCB = o.addedCB,
|
||||||
removedCB = o.removedCB,
|
removedCB = o.removedCB,
|
||||||
autocomplete = o.autocomplete,
|
autocomplete = o.autocomplete,
|
||||||
maxItems;
|
maxItems;
|
||||||
|
|
||||||
if ($maxForms.length && $maxForms.val()) {
|
if ($maxForms.length && $maxForms.val()) {
|
||||||
maxItems = parseInt($maxForms.val(), 10);
|
maxItems = parseInt($maxForms.val(), 10);
|
||||||
} else if (o.maxForms) {
|
} else if (o.maxForms) {
|
||||||
maxItems = o.maxForms;
|
maxItems = o.maxForms;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findItem(item) {
|
function findItem(item) {
|
||||||
if (item) {
|
if (item) {
|
||||||
var $item = $forms.find('[name$=-' + hiddenField + '][value=' + item[formPK] + ']');
|
var $item = $forms.find(
|
||||||
if ($item.length) {
|
'[name$=-' + hiddenField + '][value=' + item[formPK] + ']',
|
||||||
var $f = $item.closest(formSelector);
|
);
|
||||||
return {'exists': true, 'visible': $f.is(':visible'), 'item': $f};
|
if ($item.length) {
|
||||||
}
|
var $f = $item.closest(formSelector);
|
||||||
|
return { exists: true, visible: $f.is(':visible'), item: $f };
|
||||||
}
|
}
|
||||||
return {'exists': false, 'visible': false};
|
}
|
||||||
|
return { exists: false, visible: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearInput() {
|
function clearInput() {
|
||||||
$input.val('');
|
$input.val('');
|
||||||
$input.removeAttr('data-item');
|
$input.removeAttr('data-item');
|
||||||
toggleInput();
|
toggleInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleInput() {
|
function toggleInput() {
|
||||||
if (!maxItems) {
|
if (!maxItems) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var $visible = $forms.find(formSelector + ':visible').length;
|
var $visible = $forms.find(formSelector + ':visible').length;
|
||||||
if ($visible >= maxItems) {
|
if ($visible >= maxItems) {
|
||||||
$input.prop('disabled', true).slideUp();
|
$input.prop('disabled', true).slideUp();
|
||||||
$('.ui-autocomplete').hide();
|
$('.ui-autocomplete').hide();
|
||||||
} else if ($visible < maxItems) {
|
} else if ($visible < maxItems) {
|
||||||
$input.filter(':disabled').prop('disabled', false).slideDown();
|
$input.filter(':disabled').prop('disabled', false).slideDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function added() {
|
function added() {
|
||||||
var item = JSON.parse($input.attr('data-item'));
|
var item = JSON.parse($input.attr('data-item'));
|
||||||
|
|
||||||
// Check if this item has already been added.
|
// Check if this item has already been added.
|
||||||
var dupe = findItem(item);
|
var dupe = findItem(item);
|
||||||
if (dupe.exists) {
|
if (dupe.exists) {
|
||||||
if (!dupe.visible) {
|
if (!dupe.visible) {
|
||||||
// Undelete the item.
|
// Undelete the item.
|
||||||
var $item = dupe.item;
|
var $item = dupe.item;
|
||||||
$item.find('input[name$=-DELETE]').prop('checked', false);
|
$item.find('input[name$=-DELETE]').prop('checked', false);
|
||||||
$item.slideDown(toggleInput);
|
$item.slideDown(toggleInput);
|
||||||
}
|
|
||||||
clearInput();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearInput();
|
clearInput();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var formId = updateTotalForms(formsetPrefix, 1) - 1,
|
clearInput();
|
||||||
emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
|
|
||||||
|
|
||||||
var $f;
|
var formId = updateTotalForms(formsetPrefix, 1) - 1,
|
||||||
if (addedCB) {
|
emptyForm = $extraForm.html().replace(/__prefix__/g, formId);
|
||||||
$f = addedCB(emptyForm, item);
|
|
||||||
} else {
|
|
||||||
$f = $(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
$f.hide().appendTo($forms).slideDown(toggleInput);
|
var $f;
|
||||||
|
if (addedCB) {
|
||||||
|
$f = addedCB(emptyForm, item);
|
||||||
|
} else {
|
||||||
|
$f = $(f);
|
||||||
|
}
|
||||||
|
|
||||||
// Update hidden field.
|
$f.hide().appendTo($forms).slideDown(toggleInput);
|
||||||
$forms.find(formSelector + ':last [name$=-' + hiddenField + ']')
|
|
||||||
.val(item[formPK]);
|
// Update hidden field.
|
||||||
|
$forms
|
||||||
|
.find(formSelector + ':last [name$=-' + hiddenField + ']')
|
||||||
|
.val(item[formPK]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removed(el) {
|
function removed(el) {
|
||||||
el.slideUp(toggleInput);
|
el.slideUp(toggleInput);
|
||||||
// Mark as deleted.
|
// Mark as deleted.
|
||||||
el.find('input[name$=-DELETE]').prop('checked', true);
|
el.find('input[name$=-DELETE]').prop('checked', true);
|
||||||
|
|
||||||
if (removedCB) {
|
if (removedCB) {
|
||||||
removedCB(el);
|
removedCB(el);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this was not an initial form (i.e., an extra form), delete the
|
// If this was not an initial form (i.e., an extra form), delete the
|
||||||
// form and decrement the TOTAL_FORMS count.
|
// form and decrement the TOTAL_FORMS count.
|
||||||
if (!el.find('input[name$=-' + formPK + ']').length) {
|
if (!el.find('input[name$=-' + formPK + ']').length) {
|
||||||
el.remove();
|
el.remove();
|
||||||
updateTotalForms(formsetPrefix, -1);
|
updateTotalForms(formsetPrefix, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _renderItem(ul, item) {
|
function _renderItem(ul, item) {
|
||||||
if (!findItem(item).visible) {
|
if (!findItem(item).visible) {
|
||||||
var $a = $(format('<a><img src="{0}" alt="">{1}</a>',
|
var $a = $(
|
||||||
[item.icons['32'], _.escape(item.name)]));
|
format('<a><img src="{0}" alt="">{1}</a>', [
|
||||||
return $('<li>').data('item.autocomplete', item)
|
item.icons['32'],
|
||||||
.append($a).appendTo(ul);
|
_.escape(item.name),
|
||||||
}
|
]),
|
||||||
|
);
|
||||||
|
return $('<li>')
|
||||||
|
.data('item.autocomplete', item)
|
||||||
|
.append($a)
|
||||||
|
.appendTo(ul);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _renderItemData(ul, item) {
|
function _renderItemData(ul, item) {
|
||||||
var rendered = _renderItem( ul, item );
|
var rendered = _renderItem(ul, item);
|
||||||
|
|
||||||
// We are overwriting `_renderItem` in some places and return
|
// We are overwriting `_renderItem` in some places and return
|
||||||
// nothing in case of duplicate filtering.
|
// nothing in case of duplicate filtering.
|
||||||
if (rendered) {
|
if (rendered) {
|
||||||
rendered.data("ui-autocomplete-item", item);
|
rendered.data('ui-autocomplete-item', item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autocomplete) {
|
if (autocomplete) {
|
||||||
autocomplete();
|
autocomplete();
|
||||||
} else {
|
} else {
|
||||||
$input.autocomplete({
|
$input
|
||||||
minLength: minLength,
|
.autocomplete({
|
||||||
width: width,
|
minLength: minLength,
|
||||||
source: function(request, response) {
|
width: width,
|
||||||
var d = {};
|
source: function (request, response) {
|
||||||
d[searchField] = request.term;
|
var d = {};
|
||||||
$.getJSON(src, d, response);
|
d[searchField] = request.term;
|
||||||
},
|
$.getJSON(src, d, response);
|
||||||
focus: function(event, ui) {
|
},
|
||||||
event.preventDefault();
|
focus: function (event, ui) {
|
||||||
$input.val(ui.item.name);
|
event.preventDefault();
|
||||||
},
|
$input.val(ui.item.name);
|
||||||
select: function(event, ui) {
|
},
|
||||||
event.preventDefault();
|
select: function (event, ui) {
|
||||||
if (ui.item) {
|
event.preventDefault();
|
||||||
$input.val(ui.item.name);
|
if (ui.item) {
|
||||||
$input.attr('data-item', JSON.stringify(ui.item));
|
$input.val(ui.item.name);
|
||||||
added();
|
$input.attr('data-item', JSON.stringify(ui.item));
|
||||||
}
|
added();
|
||||||
}
|
}
|
||||||
}).data('ui-autocomplete')._renderMenu = function(ul, items) {
|
},
|
||||||
// Overwrite _renderMenu to patch in our custom `_renderItemData`
|
})
|
||||||
// and `_renderItem` to allow for our custom list-filter.
|
.data('ui-autocomplete')._renderMenu = function (ul, items) {
|
||||||
$.each(items, function(index, item) {
|
// Overwrite _renderMenu to patch in our custom `_renderItemData`
|
||||||
_renderItemData(ul, item);
|
// and `_renderItem` to allow for our custom list-filter.
|
||||||
});
|
$.each(items, function (index, item) {
|
||||||
};
|
_renderItemData(ul, item);
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleInput();
|
toggleInput();
|
||||||
|
|
||||||
$delegate.on('click', '.' + removeClass, _pd(function() {
|
$delegate.on(
|
||||||
|
'click',
|
||||||
|
'.' + removeClass,
|
||||||
|
_pd(function () {
|
||||||
removed($(this).closest(formSelector));
|
removed($(this).closest(formSelector));
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,151 +1,175 @@
|
||||||
//bind pager controls to our addon grids
|
//bind pager controls to our addon grids
|
||||||
$('.island .listing-grid').on('grid.init', function(e, data) {
|
$('.island .listing-grid').on('grid.init', function (e, data) {
|
||||||
var $grid = data.self,
|
var $grid = data.self,
|
||||||
numPages = data.maxPage;
|
numPages = data.maxPage;
|
||||||
|
|
||||||
if (numPages > 0) {
|
if (numPages > 0) {
|
||||||
var $nav = $('<nav class="pager">');
|
var $nav = $('<nav class="pager">');
|
||||||
$nav.append('<a href="#" class="prev">«</a>');
|
$nav.append('<a href="#" class="prev">«</a>');
|
||||||
for (var i=0; i<=numPages; i++) {
|
for (var i = 0; i <= numPages; i++) {
|
||||||
$nav.append('<a href="#" class="' + (i==0 ? 'selected ': '') + 'dot"><b></b></a>');
|
$nav.append(
|
||||||
}
|
'<a href="#" class="' +
|
||||||
$nav.append('<a href="#" class="next">»</a>');
|
(i == 0 ? 'selected ' : '') +
|
||||||
$grid.parent().prepend($nav);
|
'dot"><b></b></a>',
|
||||||
$nav.on('click', 'a', function(e) {
|
);
|
||||||
e.preventDefault();
|
|
||||||
var $tgt = $(this);
|
|
||||||
if ($tgt.hasClass('dot')) {
|
|
||||||
$grid.go($tgt.index() - 1);
|
|
||||||
} else if ($tgt.hasClass('prev')){
|
|
||||||
$grid.prevPage();
|
|
||||||
} else if ($tgt.hasClass('next')){
|
|
||||||
$grid.nextPage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$grid.on('grid.update', function(e, data) {
|
|
||||||
$nav.find('.dot').removeClass('selected')
|
|
||||||
.eq(data.current).addClass('selected');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
$nav.append('<a href="#" class="next">»</a>');
|
||||||
|
$grid.parent().prepend($nav);
|
||||||
|
$nav.on('click', 'a', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $tgt = $(this);
|
||||||
|
if ($tgt.hasClass('dot')) {
|
||||||
|
$grid.go($tgt.index() - 1);
|
||||||
|
} else if ($tgt.hasClass('prev')) {
|
||||||
|
$grid.prevPage();
|
||||||
|
} else if ($tgt.hasClass('next')) {
|
||||||
|
$grid.nextPage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$grid.on('grid.update', function (e, data) {
|
||||||
|
$nav
|
||||||
|
.find('.dot')
|
||||||
|
.removeClass('selected')
|
||||||
|
.eq(data.current)
|
||||||
|
.addClass('selected');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function hoverTruncate(grid) {
|
function hoverTruncate(grid) {
|
||||||
var $grid = $(grid);
|
var $grid = $(grid);
|
||||||
if ($grid.hasClass('hovercard')) {
|
if ($grid.hasClass('hovercard')) {
|
||||||
$grid = $grid.parent();
|
$grid = $grid.parent();
|
||||||
}
|
}
|
||||||
$grid.find('.hovercard h3').truncate();
|
$grid.find('.hovercard h3').truncate();
|
||||||
$grid.on('mouseover', '.hovercard', function() {
|
$grid
|
||||||
var $el = $(this);
|
.on('mouseover', '.hovercard', function () {
|
||||||
setTimeout(function() {
|
var $el = $(this);
|
||||||
$el.find('h3').untruncate();
|
setTimeout(function () {
|
||||||
}, 100);
|
$el.find('h3').untruncate();
|
||||||
}).on('mouseout', '.hovercard', function() {
|
}, 100);
|
||||||
var $el = $(this);
|
})
|
||||||
setTimeout(function() {
|
.on('mouseout', '.hovercard', function () {
|
||||||
$el.find('h3').truncate();
|
var $el = $(this);
|
||||||
}, 100);
|
setTimeout(function () {
|
||||||
|
$el.find('h3').truncate();
|
||||||
|
}, 100);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function listing_grid() {
|
function listing_grid() {
|
||||||
var $grid = $(this),
|
var $grid = $(this),
|
||||||
$pages = $grid.find('section'),
|
$pages = $grid.find('section'),
|
||||||
current = 0,
|
current = 0,
|
||||||
maxPage = $pages.length-1;
|
maxPage = $pages.length - 1;
|
||||||
$grid.trigger("grid.init", {self: $grid, current: current, maxPage: maxPage});
|
$grid.trigger('grid.init', {
|
||||||
$grid.go = function(n) {
|
self: $grid,
|
||||||
if (n != current) {
|
current: current,
|
||||||
n = n < 0 ? 0 : (n > maxPage ? maxPage : n);
|
maxPage: maxPage,
|
||||||
current = n;
|
});
|
||||||
$pages.hide().eq(n).show().find('.hovercard h3').truncate();
|
$grid.go = function (n) {
|
||||||
$grid.trigger("grid.update", {self: $grid, current: current, maxPage: maxPage});
|
if (n != current) {
|
||||||
}
|
n = n < 0 ? 0 : n > maxPage ? maxPage : n;
|
||||||
};
|
current = n;
|
||||||
$grid.prevPage = function() {
|
$pages.hide().eq(n).show().find('.hovercard h3').truncate();
|
||||||
$grid.go(current-1);
|
$grid.trigger('grid.update', {
|
||||||
};
|
self: $grid,
|
||||||
$grid.nextPage = function() {
|
current: current,
|
||||||
$grid.go(current+1);
|
maxPage: maxPage,
|
||||||
};
|
});
|
||||||
hoverTruncate(this);
|
}
|
||||||
$grid.css({
|
};
|
||||||
'width': $grid.width() + 'px',
|
$grid.prevPage = function () {
|
||||||
'height': $grid.height() + 'px'
|
$grid.go(current - 1);
|
||||||
});
|
};
|
||||||
return $grid;
|
$grid.nextPage = function () {
|
||||||
|
$grid.go(current + 1);
|
||||||
|
};
|
||||||
|
hoverTruncate(this);
|
||||||
|
$grid.css({
|
||||||
|
width: $grid.width() + 'px',
|
||||||
|
height: $grid.height() + 'px',
|
||||||
|
});
|
||||||
|
return $grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
$(function() {
|
// Paginate listing grids.
|
||||||
"use strict";
|
$('.listing-grid').each(listing_grid);
|
||||||
|
|
||||||
// Paginate listing grids.
|
// Truncate titles on single hovercards.
|
||||||
$('.listing-grid').each(listing_grid);
|
$('.hovercard').each(function () {
|
||||||
|
hoverTruncate(this);
|
||||||
|
});
|
||||||
|
|
||||||
// Truncate titles on single hovercards.
|
// load deferred images.
|
||||||
$('.hovercard').each(function() {
|
$('img[data-defer-src]').each(function () {
|
||||||
hoverTruncate(this);
|
var $img = $(this);
|
||||||
});
|
$img.attr('src', $img.attr('data-defer-src'));
|
||||||
|
});
|
||||||
|
|
||||||
// load deferred images.
|
// Email obfuscation.
|
||||||
$('img[data-defer-src]').each(function() {
|
$('span.emaillink').each(function () {
|
||||||
var $img = $(this);
|
var $this = $(this);
|
||||||
$img.attr('src', $img.attr('data-defer-src'));
|
$this.find('.i').remove();
|
||||||
});
|
var em = $this.text().split('').reverse().join('');
|
||||||
|
$this.prev('a').attr('href', 'mailto:' + em);
|
||||||
|
// Allows the email to be selected and pasted in webmails, see #919160.
|
||||||
|
$this.text(em).css('direction', 'ltr');
|
||||||
|
});
|
||||||
|
|
||||||
// Email obfuscation.
|
$('#page').on(
|
||||||
$('span.emaillink').each(function() {
|
'click',
|
||||||
var $this = $(this);
|
'.expando .toggle',
|
||||||
$this.find('.i').remove();
|
_pd(function () {
|
||||||
var em = $this.text().split('').reverse().join('');
|
$(this).closest('.expando').toggleClass('expanded');
|
||||||
$this.prev('a').attr('href', 'mailto:' + em);
|
}),
|
||||||
// Allows the email to be selected and pasted in webmails, see #919160.
|
);
|
||||||
$this.text(em).css('direction', 'ltr');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#page').on('click', '.expando .toggle', _pd(function() {
|
var fragment = window.location.hash;
|
||||||
$(this).closest('.expando').toggleClass('expanded');
|
if (fragment && /^#[\w.-]+$/.test(fragment)) {
|
||||||
}));
|
// If the page URL is pointing directly to an expando section (e.g.
|
||||||
|
// external link to that section), make sure the contents are visible.
|
||||||
|
var $target = $(fragment);
|
||||||
|
if ($target.hasClass('expando')) $target.addClass('expanded');
|
||||||
|
}
|
||||||
|
|
||||||
var fragment = window.location.hash;
|
$('#page').on('click', '.scrollto', function (e) {
|
||||||
if (fragment && /^#[\w.-]+$/.test(fragment)) {
|
e.preventDefault();
|
||||||
// If the page URL is pointing directly to an expando section (e.g.
|
var href = $(this).attr('href'),
|
||||||
// external link to that section), make sure the contents are visible.
|
$target = $(href.match(/#.*$/)[0]);
|
||||||
var $target = $(fragment);
|
if ($target.hasClass('expando')) {
|
||||||
if ($target.hasClass('expando'))
|
$target.addClass('expanded');
|
||||||
$target.addClass('expanded');
|
|
||||||
}
|
}
|
||||||
|
var top = $target.offset().top - 15;
|
||||||
|
$(document.documentElement).animate({ scrollTop: top }, 500);
|
||||||
|
});
|
||||||
|
|
||||||
$('#page').on('click', '.scrollto', function(e) {
|
$("select[name='rating']").ratingwidget();
|
||||||
e.preventDefault();
|
|
||||||
var href = $(this).attr('href'),
|
|
||||||
$target = $(href.match(/#.*$/)[0]);
|
|
||||||
if ($target.hasClass('expando')) {
|
|
||||||
$target.addClass('expanded');
|
|
||||||
}
|
|
||||||
var top = $target.offset().top - 15;
|
|
||||||
$(document.documentElement).animate({ scrollTop: top }, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("select[name='rating']").ratingwidget();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// AJAX form submit
|
// AJAX form submit
|
||||||
|
|
||||||
$(function() {
|
$(function () {
|
||||||
$(document).on('submit', 'form.ajax-submit, .ajax-submit form', function() {
|
$(document).on('submit', 'form.ajax-submit, .ajax-submit form', function () {
|
||||||
var $form = $(this),
|
var $form = $(this),
|
||||||
$parent = $form.is('.ajax-submit') ? $form : $form.closest('.ajax-submit'),
|
$parent = $form.is('.ajax-submit')
|
||||||
params = $form.serializeArray();
|
? $form
|
||||||
|
: $form.closest('.ajax-submit'),
|
||||||
|
params = $form.serializeArray();
|
||||||
|
|
||||||
$form.find('.submit, button[type=submit], submit').prop('disabled', true).addClass('loading-submit');
|
$form
|
||||||
$.post($form.attr('action'), params, function(d) {
|
.find('.submit, button[type=submit], submit')
|
||||||
var $replacement = $(d);
|
.prop('disabled', true)
|
||||||
$parent.replaceWith($replacement);
|
.addClass('loading-submit');
|
||||||
$replacement.trigger('ajax-submit-loaded');
|
$.post($form.attr('action'), params, function (d) {
|
||||||
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded');
|
var $replacement = $(d);
|
||||||
});
|
$parent.replaceWith($replacement);
|
||||||
return false;
|
$replacement.trigger('ajax-submit-loaded');
|
||||||
|
$replacement.find('.ajax-submit').trigger('ajax-submit-loaded');
|
||||||
|
});
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,44 +1,48 @@
|
||||||
$(function() {
|
$(function () {
|
||||||
initListingCompat();
|
initListingCompat();
|
||||||
|
|
||||||
$('.theme-grid .hovercard.theme').each(function() {
|
$('.theme-grid .hovercard.theme').each(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
if ($this.find('.acr-override').length) {
|
if ($this.find('.acr-override').length) {
|
||||||
$this.addClass('acr');
|
$this.addClass('acr');
|
||||||
} else if ($this.find('.concealed').length == $this.find('.button').length) {
|
} else if (
|
||||||
$this.addClass('incompatible');
|
$this.find('.concealed').length == $this.find('.button').length
|
||||||
// L10n: {0} is an app name.
|
) {
|
||||||
var msg = format(gettext('This theme is incompatible with your version of {0}'),
|
$this.addClass('incompatible');
|
||||||
[z.appName]);
|
// L10n: {0} is an app name.
|
||||||
$this.append(format('<span class="notavail">{0}</span>', msg));
|
var msg = format(
|
||||||
}
|
gettext('This theme is incompatible with your version of {0}'),
|
||||||
});
|
[z.appName],
|
||||||
|
);
|
||||||
|
$this.append(format('<span class="notavail">{0}</span>', msg));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make this row appear 'static' so the installation buttons and pop-ups
|
||||||
// Make this row appear 'static' so the installation buttons and pop-ups
|
// stay open when hovering outside the item row.
|
||||||
// stay open when hovering outside the item row.
|
$(document.body)
|
||||||
$(document.body).on('newStatic', function() {
|
.on('newStatic', function () {
|
||||||
$('.install-note:visible').closest('.item').addClass('static');
|
$('.install-note:visible').closest('.item').addClass('static');
|
||||||
}).on('closeStatic', function() {
|
})
|
||||||
$('.item.static').removeClass('static');
|
.on('closeStatic', function () {
|
||||||
|
$('.item.static').removeClass('static');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function initListingCompat(domContext) {
|
function initListingCompat(domContext) {
|
||||||
domContext = domContext || document.body;
|
domContext = domContext || document.body;
|
||||||
// Mark incompatible add-ons on listing pages unless marked with ignore.
|
// Mark incompatible add-ons on listing pages unless marked with ignore.
|
||||||
$('.listing .item.addon', domContext).each(function() {
|
$('.listing .item.addon', domContext).each(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var isIncompatible = (!$this.hasClass('ignore-compatibility') &&
|
var isIncompatible =
|
||||||
($this.find('.concealed').length == $this.find('.button').length ||
|
!$this.hasClass('ignore-compatibility') &&
|
||||||
$this.find('button.not-available').length)
|
($this.find('.concealed').length == $this.find('.button').length ||
|
||||||
);
|
$this.find('button.not-available').length);
|
||||||
|
|
||||||
if ($this.find('.acr-override').length) {
|
if ($this.find('.acr-override').length) {
|
||||||
$this.addClass('acr');
|
$this.addClass('acr');
|
||||||
} else if (isIncompatible) {
|
} else if (isIncompatible) {
|
||||||
$this.addClass('incompatible');
|
$this.addClass('incompatible');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,23 +11,23 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
var $window = $(window);
|
var $window = $(window);
|
||||||
$window.on('click', '.install-button a.button', function(e) {
|
$window.on('click', '.install-button a.button', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var $el = $(this);
|
var $el = $(this);
|
||||||
|
|
||||||
// When everything is loaded, trigger a click on the button
|
// When everything is loaded, trigger a click on the button
|
||||||
$window.on('buttons_loaded_click', function() {
|
$window.on('buttons_loaded_click', function () {
|
||||||
$el.trigger('click');
|
$el.trigger('click');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
$window.on('buttons_loaded', function() {
|
});
|
||||||
// Trigger all the clicks
|
$window.on('buttons_loaded', function () {
|
||||||
$window.trigger('buttons_loaded_click');
|
// Trigger all the clicks
|
||||||
|
$window.trigger('buttons_loaded_click');
|
||||||
|
|
||||||
// Clean up after ourselves
|
// Clean up after ourselves
|
||||||
$window.off('buttons_loaded buttons_loaded_click');
|
$window.off('buttons_loaded buttons_loaded_click');
|
||||||
$window.off('click', '.install-button a.button');
|
$window.off('click', '.install-button a.button');
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,163 +1,185 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
var report = $('.review-reason').html(),
|
||||||
|
$window = $(window);
|
||||||
|
|
||||||
var report = $('.review-reason').html(),
|
$('.review-reason').popup('.flag-review', {
|
||||||
$window = $(window);
|
delegate: $(document.body),
|
||||||
|
width: 'inherit',
|
||||||
|
callback: function (obj) {
|
||||||
|
var ct = $(obj.click_target),
|
||||||
|
$popup = this;
|
||||||
|
//reset our event handlers
|
||||||
|
$popup.hideMe();
|
||||||
|
|
||||||
$('.review-reason').popup('.flag-review', {
|
function addFlag(flag, note) {
|
||||||
delegate: $(document.body),
|
$.ajax({
|
||||||
width: 'inherit',
|
type: 'POST',
|
||||||
callback: function(obj) {
|
url: ct.attr('href'),
|
||||||
var ct = $(obj.click_target),
|
data: { flag: flag, note: note },
|
||||||
$popup = this;
|
success: function () {
|
||||||
//reset our event handlers
|
$popup.removeClass('other').hideMe();
|
||||||
$popup.hideMe();
|
ct.closest('.item').addClass('flagged');
|
||||||
|
ct.replaceWith(gettext('Flagged for review')).addClass('flagged');
|
||||||
function addFlag(flag, note) {
|
},
|
||||||
$.ajax({type: 'POST',
|
error: function () {},
|
||||||
url: ct.attr('href'),
|
dataType: 'json',
|
||||||
data: {flag: flag, note: note},
|
|
||||||
success: function() {
|
|
||||||
$popup.removeClass('other')
|
|
||||||
.hideMe();
|
|
||||||
ct.closest('.item').addClass('flagged');
|
|
||||||
ct.replaceWith(gettext('Flagged for review'))
|
|
||||||
.addClass('flagged');
|
|
||||||
},
|
|
||||||
error: function(){ },
|
|
||||||
dataType: 'json'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$popup.on('click', 'li a', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var el = $(e.target);
|
|
||||||
if (el.attr('href') == '#review_flag_reason_other') {
|
|
||||||
$popup.addClass('other')
|
|
||||||
.on('submit', 'form', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var note = $popup.find('#id_note').val();
|
|
||||||
if (!note) {
|
|
||||||
alert(gettext('Your input is required'));
|
|
||||||
} else {
|
|
||||||
addFlag('review_flag_reason_other', note);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setPos(ct)
|
|
||||||
.find('input[type=text]')
|
|
||||||
.focus();
|
|
||||||
} else {
|
|
||||||
addFlag(el.attr('href').slice(1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$popup.removeClass("other");
|
|
||||||
$popup.html(report);
|
|
||||||
return { pointTo: ct };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// A review comment can either be a review or a review reply
|
|
||||||
function review_comment_edit_click(comment_form_id, comment_title_widget_id, comment_body_widget_id, comment_cancel_btn_id) {
|
|
||||||
return function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var $form = $('#' + comment_form_id),
|
|
||||||
$review = $(this).closest('.review'),
|
|
||||||
edit_url = $('a.permalink', $review).attr('href') + 'edit',
|
|
||||||
$cancel = $('#' + comment_cancel_btn_id),
|
|
||||||
title_selector;
|
|
||||||
|
|
||||||
clearErrors($form);
|
|
||||||
$form.off().hide();
|
|
||||||
$('.review').not($review).show();
|
|
||||||
$form.detach().insertAfter($review);
|
|
||||||
|
|
||||||
if ($review.find('h4').length) {
|
|
||||||
$form.find('fieldset h3').remove();
|
|
||||||
title_selector = 'h4 > b';
|
|
||||||
$form.find('fieldset').prepend($review.find('h3').clone());
|
|
||||||
} else {
|
|
||||||
title_selector = 'h3 > b';
|
|
||||||
}
|
|
||||||
|
|
||||||
$form.find('#' + comment_title_widget_id).val($review.find(title_selector).text());
|
|
||||||
$form.find('#' + comment_body_widget_id).val($review.children('p.description').html().replace(/<br>/g, '\n'));
|
|
||||||
$review.hide();
|
|
||||||
$form.show();
|
|
||||||
$window.resize();
|
|
||||||
location.hash = '#' + comment_form_id;
|
|
||||||
|
|
||||||
function done_edit() {
|
|
||||||
clearErrors($form);
|
|
||||||
$form.off().hide();
|
|
||||||
$review.show();
|
|
||||||
$cancel.off();
|
|
||||||
$window.resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
$cancel.click(_pd(done_edit));
|
|
||||||
|
|
||||||
$form.submit(function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: edit_url,
|
|
||||||
data: $form.serialize(),
|
|
||||||
success: function(response, status) {
|
|
||||||
clearErrors($form);
|
|
||||||
$review.find(title_selector).text($form.find('#' + comment_title_widget_id).val());
|
|
||||||
var rating = $form.find('.ratingwidget input:radio:checked').val();
|
|
||||||
$('.stars', $review).removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + rating);
|
|
||||||
rating = $review.attr('data-rating', rating);
|
|
||||||
$review.children('p.description').html(
|
|
||||||
$form.find('#' + comment_body_widget_id).val()
|
|
||||||
.replace(/&/g,'&')
|
|
||||||
.replace(/</g,'<')
|
|
||||||
.replace(/>/g,'>')
|
|
||||||
.replace(/\n/g, '<br>'));
|
|
||||||
done_edit();
|
|
||||||
},
|
|
||||||
error: function(xhr) {
|
|
||||||
var errors = JSON.parse(xhr.responseText);
|
|
||||||
populateErrors($form, errors);
|
|
||||||
},
|
|
||||||
dataType: 'json'
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.primary').on('click', '.review-reply-edit',
|
|
||||||
review_comment_edit_click(
|
|
||||||
'review-reply-edit-form',
|
|
||||||
'id_review_reply_title',
|
|
||||||
'id_review_reply_body',
|
|
||||||
'review-reply-edit-cancel'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$('.primary').on('click', '.review-edit',
|
|
||||||
review_comment_edit_click(
|
|
||||||
'review-edit-form',
|
|
||||||
'id_review_title',
|
|
||||||
'id_review_body',
|
|
||||||
'review-edit-cancel'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$('.delete-review').click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var target = $(e.target);
|
|
||||||
$.post(target.attr('href'), function() {
|
|
||||||
target.replaceWith(gettext('Marked for deletion'));
|
|
||||||
});
|
});
|
||||||
target.closest('.review').addClass('deleted');
|
}
|
||||||
|
|
||||||
|
$popup.on('click', 'li a', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var el = $(e.target);
|
||||||
|
if (el.attr('href') == '#review_flag_reason_other') {
|
||||||
|
$popup
|
||||||
|
.addClass('other')
|
||||||
|
.on('submit', 'form', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var note = $popup.find('#id_note').val();
|
||||||
|
if (!note) {
|
||||||
|
alert(gettext('Your input is required'));
|
||||||
|
} else {
|
||||||
|
addFlag('review_flag_reason_other', note);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setPos(ct)
|
||||||
|
.find('input[type=text]')
|
||||||
|
.focus();
|
||||||
|
} else {
|
||||||
|
addFlag(el.attr('href').slice(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$popup.removeClass('other');
|
||||||
|
$popup.html(report);
|
||||||
|
return { pointTo: ct };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// A review comment can either be a review or a review reply
|
||||||
|
function review_comment_edit_click(
|
||||||
|
comment_form_id,
|
||||||
|
comment_title_widget_id,
|
||||||
|
comment_body_widget_id,
|
||||||
|
comment_cancel_btn_id,
|
||||||
|
) {
|
||||||
|
return function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var $form = $('#' + comment_form_id),
|
||||||
|
$review = $(this).closest('.review'),
|
||||||
|
edit_url = $('a.permalink', $review).attr('href') + 'edit',
|
||||||
|
$cancel = $('#' + comment_cancel_btn_id),
|
||||||
|
title_selector;
|
||||||
|
|
||||||
|
clearErrors($form);
|
||||||
|
$form.off().hide();
|
||||||
|
$('.review').not($review).show();
|
||||||
|
$form.detach().insertAfter($review);
|
||||||
|
|
||||||
|
if ($review.find('h4').length) {
|
||||||
|
$form.find('fieldset h3').remove();
|
||||||
|
title_selector = 'h4 > b';
|
||||||
|
$form.find('fieldset').prepend($review.find('h3').clone());
|
||||||
|
} else {
|
||||||
|
title_selector = 'h3 > b';
|
||||||
|
}
|
||||||
|
|
||||||
|
$form
|
||||||
|
.find('#' + comment_title_widget_id)
|
||||||
|
.val($review.find(title_selector).text());
|
||||||
|
$form
|
||||||
|
.find('#' + comment_body_widget_id)
|
||||||
|
.val($review.children('p.description').html().replace(/<br>/g, '\n'));
|
||||||
|
$review.hide();
|
||||||
|
$form.show();
|
||||||
|
$window.resize();
|
||||||
|
location.hash = '#' + comment_form_id;
|
||||||
|
|
||||||
|
function done_edit() {
|
||||||
|
clearErrors($form);
|
||||||
|
$form.off().hide();
|
||||||
|
$review.show();
|
||||||
|
$cancel.off();
|
||||||
|
$window.resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cancel.click(_pd(done_edit));
|
||||||
|
|
||||||
|
$form.submit(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: edit_url,
|
||||||
|
data: $form.serialize(),
|
||||||
|
success: function (response, status) {
|
||||||
|
clearErrors($form);
|
||||||
|
$review
|
||||||
|
.find(title_selector)
|
||||||
|
.text($form.find('#' + comment_title_widget_id).val());
|
||||||
|
var rating = $form.find('.ratingwidget input:radio:checked').val();
|
||||||
|
$('.stars', $review)
|
||||||
|
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
|
||||||
|
.addClass('stars-' + rating);
|
||||||
|
rating = $review.attr('data-rating', rating);
|
||||||
|
$review.children('p.description').html(
|
||||||
|
$form
|
||||||
|
.find('#' + comment_body_widget_id)
|
||||||
|
.val()
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/\n/g, '<br>'),
|
||||||
|
);
|
||||||
|
done_edit();
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
var errors = JSON.parse(xhr.responseText);
|
||||||
|
populateErrors($form, errors);
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.primary').on(
|
||||||
|
'click',
|
||||||
|
'.review-reply-edit',
|
||||||
|
review_comment_edit_click(
|
||||||
|
'review-reply-edit-form',
|
||||||
|
'id_review_reply_title',
|
||||||
|
'id_review_reply_body',
|
||||||
|
'review-reply-edit-cancel',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$('.primary').on(
|
||||||
|
'click',
|
||||||
|
'.review-edit',
|
||||||
|
review_comment_edit_click(
|
||||||
|
'review-edit-form',
|
||||||
|
'id_review_title',
|
||||||
|
'id_review_body',
|
||||||
|
'review-edit-cancel',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
$('.delete-review').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var target = $(e.target);
|
||||||
|
$.post(target.attr('href'), function () {
|
||||||
|
target.replaceWith(gettext('Marked for deletion'));
|
||||||
});
|
});
|
||||||
|
target.closest('.review').addClass('deleted');
|
||||||
|
});
|
||||||
|
|
||||||
$('select[name="rating"]').ratingwidget();
|
$('select[name="rating"]').ratingwidget();
|
||||||
|
|
||||||
$('#detail-review-link').click(_pd(function(e) {
|
$('#detail-review-link').click(
|
||||||
$('#review-add-box form')
|
_pd(function (e) {
|
||||||
.append('<input type="hidden" name="detailed" value="1">').submit();
|
$('#review-add-box form')
|
||||||
}));
|
.append('<input type="hidden" name="detailed" value="1">')
|
||||||
|
.submit();
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,178 +1,184 @@
|
||||||
(function () {
|
(function () {
|
||||||
var appver_input = $('#id_appver');
|
var appver_input = $('#id_appver');
|
||||||
var platform_input = $('#id_platform');
|
var platform_input = $('#id_platform');
|
||||||
|
|
||||||
function autofillPlatform(context) {
|
function autofillPlatform(context) {
|
||||||
var $context = $(context || document.body);
|
var $context = $(context || document.body);
|
||||||
|
|
||||||
$('#search', $context).on('autofill', function(e) {
|
$('#search', $context)
|
||||||
var $this = $(this);
|
.on('autofill', function (e) {
|
||||||
|
var $this = $(this);
|
||||||
|
|
||||||
// Bail if search is present but not the appver input somehow.
|
// Bail if search is present but not the appver input somehow.
|
||||||
if (!appver_input.length) {
|
if (!appver_input.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate search form with browser version and OS.
|
// Populate search form with browser version and OS.
|
||||||
var gv = z.getVars();
|
var gv = z.getVars();
|
||||||
|
|
||||||
// Facets are either the ones defined in the URL, or the detected
|
// Facets are either the ones defined in the URL, or the detected
|
||||||
// browser version and platform.
|
// browser version and platform.
|
||||||
if (!!(gv.appver)) { // Defined in URL parameter
|
if (!!gv.appver) {
|
||||||
appver_input.val(gv.appver);
|
// Defined in URL parameter
|
||||||
} else if (z.appMatchesUserAgent) { // Fallback to detected
|
appver_input.val(gv.appver);
|
||||||
// Only do this if firefox 57 or higher. Lower versions default
|
} else if (z.appMatchesUserAgent) {
|
||||||
// to searching for all add-ons even if they might be
|
// Fallback to detected
|
||||||
// incompatible. https://github.com/mozilla/addons-server/issues/5482
|
// Only do this if firefox 57 or higher. Lower versions default
|
||||||
if (VersionCompare.compareVersions(z.browserVersion, '57.0') >= 0) {
|
// to searching for all add-ons even if they might be
|
||||||
appver_input.val(z.browserVersion);
|
// incompatible. https://github.com/mozilla/addons-server/issues/5482
|
||||||
}
|
if (VersionCompare.compareVersions(z.browserVersion, '57.0') >= 0) {
|
||||||
}
|
appver_input.val(z.browserVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!!(gv.platform)) { // Defined in URL parameter
|
if (!!gv.platform) {
|
||||||
platform_input.val(gv.platform);
|
// Defined in URL parameter
|
||||||
} else if (z.appMatchesUserAgent) { // Fallback to detected
|
platform_input.val(gv.platform);
|
||||||
platform_input.val(z.platform);
|
} else if (z.appMatchesUserAgent) {
|
||||||
}
|
// Fallback to detected
|
||||||
}).trigger('autofill');
|
platform_input.val(z.platform);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.trigger('autofill');
|
||||||
|
}
|
||||||
|
|
||||||
|
autofillPlatform();
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$('#search-facets')
|
||||||
|
.on('click', 'li.facet', function (e) {
|
||||||
|
var $this = $(this);
|
||||||
|
if ($this.hasClass('active')) {
|
||||||
|
if ($(e.target).is('a')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this.removeClass('active');
|
||||||
|
} else {
|
||||||
|
$this.closest('ul').find('.active').removeClass('active');
|
||||||
|
$this.addClass('active');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('highlight', 'a', function (e) {
|
||||||
|
// Highlight selection on sidebar.
|
||||||
|
var $this = $(this);
|
||||||
|
$this.closest('.facet-group').find('.selected').removeClass('selected');
|
||||||
|
$this.closest('li').addClass('selected');
|
||||||
|
})
|
||||||
|
.on('recount', '.cnt', function (e, newCount) {
|
||||||
|
// Update # of results on sidebar.
|
||||||
|
var $this = $(this);
|
||||||
|
if (newCount.length && $this.html() != newCount.html()) {
|
||||||
|
$this.replaceWith(newCount);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('rebuild', 'a[data-params]', function (e) {
|
||||||
|
var $this = $(this),
|
||||||
|
url = rebuildLink($this.attr('href'), $this.attr('data-params'));
|
||||||
|
$this.attr('href', url);
|
||||||
|
});
|
||||||
|
if ($('body').hasClass('pjax') && $.support.pjax && z.capabilities.JSON) {
|
||||||
|
$('#pjax-results').initSearchPjax($('#search-facets'), '#pjax-results');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function rebuildLink(url, urlparams, qs) {
|
||||||
|
var params = JSON.parseNonNull(urlparams),
|
||||||
|
newVars = $.extend(z.getVars(qs, true), params);
|
||||||
|
return url.split('?')[0] + '?' + $.param(newVars);
|
||||||
|
}
|
||||||
|
|
||||||
autofillPlatform();
|
$.fn.initSearchPjax = function ($filters, containerSelector) {
|
||||||
|
var $container = $(this),
|
||||||
|
container = containerSelector,
|
||||||
|
$triggered;
|
||||||
|
|
||||||
|
function pjaxOpen(url) {
|
||||||
$(function() {
|
var urlBase = location.pathname + location.search;
|
||||||
$('#search-facets').on('click', 'li.facet', function(e) {
|
if (!!url && url != '#' && url != urlBase) {
|
||||||
var $this = $(this);
|
$.pjax({
|
||||||
if ($this.hasClass('active')) {
|
url: url,
|
||||||
if ($(e.target).is('a')) {
|
container: container,
|
||||||
return;
|
timeout: 5000,
|
||||||
}
|
|
||||||
$this.removeClass('active');
|
|
||||||
} else {
|
|
||||||
$this.closest('ul').find('.active').removeClass('active');
|
|
||||||
$this.addClass('active');
|
|
||||||
}
|
|
||||||
}).on('highlight', 'a', function(e) {
|
|
||||||
// Highlight selection on sidebar.
|
|
||||||
var $this = $(this);
|
|
||||||
$this.closest('.facet-group').find('.selected').removeClass('selected');
|
|
||||||
$this.closest('li').addClass('selected');
|
|
||||||
}).on('recount', '.cnt', function(e, newCount) {
|
|
||||||
// Update # of results on sidebar.
|
|
||||||
var $this = $(this);
|
|
||||||
if (newCount.length && $this.html() != newCount.html()) {
|
|
||||||
$this.replaceWith(newCount);
|
|
||||||
}
|
|
||||||
}).on('rebuild', 'a[data-params]', function(e) {
|
|
||||||
var $this = $(this),
|
|
||||||
url = rebuildLink($this.attr('href'), $this.attr('data-params'));
|
|
||||||
$this.attr('href', url);
|
|
||||||
});
|
});
|
||||||
if ($('body').hasClass('pjax') && $.support.pjax && z.capabilities.JSON) {
|
}
|
||||||
$('#pjax-results').initSearchPjax($('#search-facets'), '#pjax-results');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function rebuildLink(url, urlparams, qs) {
|
|
||||||
var params = JSON.parseNonNull(urlparams),
|
|
||||||
newVars = $.extend(z.getVars(qs, true), params);
|
|
||||||
return url.split('?')[0] + '?' + $.param(newVars);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hijackLink() {
|
||||||
|
$triggered = $(this);
|
||||||
|
pjaxOpen($triggered.attr('href'));
|
||||||
|
}
|
||||||
|
|
||||||
$.fn.initSearchPjax = function($filters, containerSelector) {
|
function loading() {
|
||||||
var $container = $(this),
|
var $wrapper = $container.closest('.results'),
|
||||||
container = containerSelector,
|
msg = gettext('Updating results…'),
|
||||||
$triggered;
|
cls = 'updating';
|
||||||
|
$wrapper.addClass('loading');
|
||||||
|
|
||||||
function pjaxOpen(url) {
|
// The loading throbber is absolutely positioned atop the
|
||||||
var urlBase = location.pathname + location.search;
|
// search results, so we do this to ensure a max-margin of sorts.
|
||||||
if (!!url && url != '#' && url != urlBase) {
|
if ($container.outerHeight() > 300) {
|
||||||
$.pjax({
|
cls += ' tall';
|
||||||
url: url,
|
}
|
||||||
container: container,
|
|
||||||
timeout: 5000
|
// Insert the loading throbber.
|
||||||
});
|
$('<div>', { class: cls, html: msg }).insertBefore($container);
|
||||||
}
|
|
||||||
|
$container.trigger('search.loading');
|
||||||
|
}
|
||||||
|
|
||||||
|
function finished() {
|
||||||
|
var $wrapper = $container.closest('.results');
|
||||||
|
|
||||||
|
// Initialize install buttons and compatibility checking.
|
||||||
|
$.when($container.find('.install:not(.triggered)').installButton()).done(
|
||||||
|
function () {
|
||||||
|
$container.find('.install').addClass('triggered');
|
||||||
|
initListingCompat();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove the loading throbber.
|
||||||
|
$wrapper.removeClass('loading').find('.updating').remove();
|
||||||
|
|
||||||
|
// Update the # of matching results on sidebar.
|
||||||
|
$filters.find('.cnt').trigger('recount', [$wrapper.find('.cnt')]);
|
||||||
|
|
||||||
|
// Update GET parameters of sidebar anchors.
|
||||||
|
$filters.find('a[data-params]').trigger('rebuild');
|
||||||
|
|
||||||
|
// Highlight selection on sidebar.
|
||||||
|
if ($triggered) {
|
||||||
|
$triggered.trigger('highlight');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update auto-filled appver/platform if there's a user override.
|
||||||
|
$('#search').trigger('autofill');
|
||||||
|
|
||||||
|
// Scroll up to top of page.
|
||||||
|
$('html, body').animate({ scrollTop: 0 }, 200);
|
||||||
|
|
||||||
|
$container.trigger('search.finished');
|
||||||
|
}
|
||||||
|
|
||||||
|
function turnPages(e) {
|
||||||
|
if (fieldFocused(e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (e.which == $.ui.keyCode.LEFT || e.which == $.ui.keyCode.RIGHT) {
|
||||||
|
e.preventDefault();
|
||||||
|
var sel;
|
||||||
|
if (e.which == $.ui.keyCode.LEFT) {
|
||||||
|
sel = '.paginator .prev:not(.disabled)';
|
||||||
|
} else {
|
||||||
|
sel = '.paginator .next:not(.disabled)';
|
||||||
}
|
}
|
||||||
|
pjaxOpen($container.find(sel).attr('href'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function hijackLink() {
|
$(document).on('click', '.pjax-trigger a', _pd(hijackLink));
|
||||||
$triggered = $(this);
|
$container.on('pjax:start', loading).on('pjax:end', finished);
|
||||||
pjaxOpen($triggered.attr('href'));
|
$(document).keyup(_.throttle(turnPages, 300));
|
||||||
}
|
};
|
||||||
|
|
||||||
function loading() {
|
|
||||||
var $wrapper = $container.closest('.results'),
|
|
||||||
msg = gettext('Updating results…'),
|
|
||||||
cls = 'updating';
|
|
||||||
$wrapper.addClass('loading');
|
|
||||||
|
|
||||||
// The loading throbber is absolutely positioned atop the
|
|
||||||
// search results, so we do this to ensure a max-margin of sorts.
|
|
||||||
if ($container.outerHeight() > 300) {
|
|
||||||
cls += ' tall';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the loading throbber.
|
|
||||||
$('<div>', {'class': cls, 'html': msg}).insertBefore($container);
|
|
||||||
|
|
||||||
$container.trigger('search.loading');
|
|
||||||
}
|
|
||||||
|
|
||||||
function finished() {
|
|
||||||
var $wrapper = $container.closest('.results');
|
|
||||||
|
|
||||||
// Initialize install buttons and compatibility checking.
|
|
||||||
$.when($container.find('.install:not(.triggered)')
|
|
||||||
.installButton()).done(function() {
|
|
||||||
$container.find('.install').addClass('triggered');
|
|
||||||
initListingCompat();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove the loading throbber.
|
|
||||||
$wrapper.removeClass('loading').find('.updating').remove();
|
|
||||||
|
|
||||||
// Update the # of matching results on sidebar.
|
|
||||||
$filters.find('.cnt').trigger('recount', [$wrapper.find('.cnt')]);
|
|
||||||
|
|
||||||
// Update GET parameters of sidebar anchors.
|
|
||||||
$filters.find('a[data-params]').trigger('rebuild');
|
|
||||||
|
|
||||||
// Highlight selection on sidebar.
|
|
||||||
if ($triggered) {
|
|
||||||
$triggered.trigger('highlight');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update auto-filled appver/platform if there's a user override.
|
|
||||||
$('#search').trigger('autofill');
|
|
||||||
|
|
||||||
// Scroll up to top of page.
|
|
||||||
$('html, body').animate({scrollTop: 0}, 200);
|
|
||||||
|
|
||||||
$container.trigger('search.finished');
|
|
||||||
}
|
|
||||||
|
|
||||||
function turnPages(e) {
|
|
||||||
if (fieldFocused(e)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.which == $.ui.keyCode.LEFT || e.which == $.ui.keyCode.RIGHT) {
|
|
||||||
e.preventDefault();
|
|
||||||
var sel;
|
|
||||||
if (e.which == $.ui.keyCode.LEFT) {
|
|
||||||
sel = '.paginator .prev:not(.disabled)';
|
|
||||||
} else {
|
|
||||||
sel = '.paginator .next:not(.disabled)';
|
|
||||||
}
|
|
||||||
pjaxOpen($container.find(sel).attr('href'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).on('click', '.pjax-trigger a', _pd(hijackLink));
|
|
||||||
$container.on('pjax:start', loading).on('pjax:end', finished);
|
|
||||||
$(document).keyup(_.throttle(turnPages, 300));
|
|
||||||
};
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,26 +1,29 @@
|
||||||
z.getVars = function(qs, excl_undefined) {
|
z.getVars = function (qs, excl_undefined) {
|
||||||
if (typeof qs === 'undefined') {
|
if (typeof qs === 'undefined') {
|
||||||
qs = location.search;
|
qs = location.search;
|
||||||
}
|
}
|
||||||
if (qs && qs[0] == '?') {
|
if (qs && qs[0] == '?') {
|
||||||
qs = qs.substr(1); // Filter off the leading ? if it's there.
|
qs = qs.substr(1); // Filter off the leading ? if it's there.
|
||||||
}
|
}
|
||||||
if (!qs) return {};
|
if (!qs) return {};
|
||||||
|
|
||||||
return _.chain(qs.split('&')) // ['a=b', 'c=d']
|
return _.chain(qs.split('&')) // ['a=b', 'c=d']
|
||||||
.map(function(c) {return _.map(c.split('='), escape_);}) // [['a', 'b'], ['c', 'd']]
|
.map(function (c) {
|
||||||
.filter(function(p) { // [['a', 'b'], ['c', undefined]] -> [['a', 'b']]
|
return _.map(c.split('='), escape_);
|
||||||
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1]));
|
}) // [['a', 'b'], ['c', 'd']]
|
||||||
}).object() // {'a': 'b', 'c': 'd'}
|
.filter(function (p) {
|
||||||
.value();
|
// [['a', 'b'], ['c', undefined]] -> [['a', 'b']]
|
||||||
|
return !!p[0] && (!excl_undefined || !_.isUndefined(p[1]));
|
||||||
|
})
|
||||||
|
.object() // {'a': 'b', 'c': 'd'}
|
||||||
|
.value();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
JSON.parseNonNull = function (text) {
|
||||||
JSON.parseNonNull = function(text) {
|
return JSON.parse(text, function (key, value) {
|
||||||
return JSON.parse(text, function(key, value) {
|
if (typeof value === 'object' && value === null) {
|
||||||
if (typeof value === 'object' && value === null) {
|
return '';
|
||||||
return '';
|
}
|
||||||
}
|
return value;
|
||||||
return value;
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,63 +1,65 @@
|
||||||
// Init site search suggestions and populate the suggestions container.
|
// Init site search suggestions and populate the suggestions container.
|
||||||
(function() {
|
(function () {
|
||||||
// AMO search init.
|
// AMO search init.
|
||||||
$('#search #search-q').searchSuggestions($('#site-search-suggestions'),
|
$('#search #search-q').searchSuggestions(
|
||||||
processResults, 'AMO');
|
$('#site-search-suggestions'),
|
||||||
|
processResults,
|
||||||
|
'AMO',
|
||||||
|
);
|
||||||
|
|
||||||
function processResults(settings) {
|
function processResults(settings) {
|
||||||
if (!settings || !settings.category) {
|
if (!settings || !settings.category) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Update the 'Search add-ons for <b>"{addon}"</b>' text.
|
|
||||||
settings['$results'].find('p b').html(format('"{0}"',
|
|
||||||
settings.searchTerm));
|
|
||||||
|
|
||||||
var li_item = template(
|
|
||||||
'<li><a href="{url}"><span {cls} {icon}>{name}</span>{subtitle}</a></li>'
|
|
||||||
);
|
|
||||||
|
|
||||||
$.ajaxCache({
|
|
||||||
url: settings['$results'].attr('data-src'),
|
|
||||||
data: settings['$form'].serialize() + '&cat=' + settings.category,
|
|
||||||
newItems: function(formdata, items) {
|
|
||||||
var eventName;
|
|
||||||
if (items !== undefined) {
|
|
||||||
var ul = '';
|
|
||||||
$.each(items, function(i, item) {
|
|
||||||
var d = {
|
|
||||||
url: escape_(item.url) || '#',
|
|
||||||
icon: '',
|
|
||||||
cls: '',
|
|
||||||
subtitle: ''
|
|
||||||
};
|
|
||||||
if (item.icons && item.icons['32']) {
|
|
||||||
d.icon = format(
|
|
||||||
'style="background-image:url({0})"',
|
|
||||||
escape_(item.icons['32'])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (item.cls) {
|
|
||||||
d.cls = format('class="{0}"',
|
|
||||||
escape_(item.cls));
|
|
||||||
if (item.cls == 'cat') {
|
|
||||||
d.subtitle = format(
|
|
||||||
' <em class="subtitle">{0}</em>',
|
|
||||||
gettext('Category')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (item.name) {
|
|
||||||
d.name = escape_(item.name);
|
|
||||||
// Append the item only if it has a name.
|
|
||||||
ul += li_item(d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
settings['$results'].find('ul').html(ul);
|
|
||||||
}
|
|
||||||
settings['$results'].trigger('highlight', [settings.searchTerm])
|
|
||||||
.trigger('resultsUpdated', [items]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the 'Search add-ons for <b>"{addon}"</b>' text.
|
||||||
|
settings['$results'].find('p b').html(format('"{0}"', settings.searchTerm));
|
||||||
|
|
||||||
|
var li_item = template(
|
||||||
|
'<li><a href="{url}"><span {cls} {icon}>{name}</span>{subtitle}</a></li>',
|
||||||
|
);
|
||||||
|
|
||||||
|
$.ajaxCache({
|
||||||
|
url: settings['$results'].attr('data-src'),
|
||||||
|
data: settings['$form'].serialize() + '&cat=' + settings.category,
|
||||||
|
newItems: function (formdata, items) {
|
||||||
|
var eventName;
|
||||||
|
if (items !== undefined) {
|
||||||
|
var ul = '';
|
||||||
|
$.each(items, function (i, item) {
|
||||||
|
var d = {
|
||||||
|
url: escape_(item.url) || '#',
|
||||||
|
icon: '',
|
||||||
|
cls: '',
|
||||||
|
subtitle: '',
|
||||||
|
};
|
||||||
|
if (item.icons && item.icons['32']) {
|
||||||
|
d.icon = format(
|
||||||
|
'style="background-image:url({0})"',
|
||||||
|
escape_(item.icons['32']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item.cls) {
|
||||||
|
d.cls = format('class="{0}"', escape_(item.cls));
|
||||||
|
if (item.cls == 'cat') {
|
||||||
|
d.subtitle = format(
|
||||||
|
' <em class="subtitle">{0}</em>',
|
||||||
|
gettext('Category'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.name) {
|
||||||
|
d.name = escape_(item.name);
|
||||||
|
// Append the item only if it has a name.
|
||||||
|
ul += li_item(d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
settings['$results'].find('ul').html(ul);
|
||||||
|
}
|
||||||
|
settings['$results']
|
||||||
|
.trigger('highlight', [settings.searchTerm])
|
||||||
|
.trigger('resultsUpdated', [items]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,105 +1,106 @@
|
||||||
(function (){
|
(function () {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var $rangeSelector = $(".criteria.range ul"),
|
var $rangeSelector = $('.criteria.range ul'),
|
||||||
$customRangeForm = $("div.custom.criteria"),
|
$customRangeForm = $('div.custom.criteria'),
|
||||||
$groupSelector = $(".criteria.group ul"),
|
$groupSelector = $('.criteria.group ul'),
|
||||||
minDate = Date.iso($('.primary').attr('data-min-date')),
|
minDate = Date.iso($('.primary').attr('data-min-date')),
|
||||||
msDay = 24 * 60 * 60 * 1000; // One day in milliseconds.
|
msDay = 24 * 60 * 60 * 1000; // One day in milliseconds.
|
||||||
|
|
||||||
$.datepicker.setDefaults({showAnim: ''});
|
$.datepicker.setDefaults({ showAnim: '' });
|
||||||
var $customModal = $("#custom-criteria").modal("#custom-date-range",
|
var $customModal = $('#custom-criteria').modal('#custom-date-range', {
|
||||||
{ width: 520,
|
width: 520,
|
||||||
hideme: true});
|
hideme: true,
|
||||||
var $startPicker = $("#start-date-picker").datepicker({
|
});
|
||||||
maxDate: 0,
|
var $startPicker = $('#start-date-picker').datepicker({
|
||||||
minDate: minDate,
|
maxDate: 0,
|
||||||
dateFormat: 'yy-mm-dd',
|
minDate: minDate,
|
||||||
onSelect: function(dateText) {
|
dateFormat: 'yy-mm-dd',
|
||||||
$("#date-range-start").val(dateText);
|
onSelect: function (dateText) {
|
||||||
|
$('#date-range-start').val(dateText);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var $endPicker = $('#end-date-picker').datepicker({
|
||||||
|
maxDate: 0,
|
||||||
|
minDate: minDate,
|
||||||
|
dateFormat: 'yy-mm-dd',
|
||||||
|
onSelect: function (dateText) {
|
||||||
|
$('#date-range-end').val(dateText);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
$rangeSelector.click(function (e) {
|
||||||
|
var $target = $(e.target).parent();
|
||||||
|
var newRange = $target.attr('data-range');
|
||||||
|
if (newRange && newRange != 'custom') {
|
||||||
|
$target.trigger('changeview', { range: newRange });
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
$groupSelector.on('click', 'a', function (e) {
|
||||||
|
var $target = $(this).parent(),
|
||||||
|
newGroup = $target.attr('data-group');
|
||||||
|
|
||||||
|
$(this).trigger('changeview', { group: newGroup });
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// set controls when `changeview` is detected.
|
||||||
|
$(window).on('changeview', function (e, newState) {
|
||||||
|
if (!newState) return;
|
||||||
|
function populateCustomRange() {
|
||||||
|
var nRange = normalizeRange(newState.range),
|
||||||
|
startStr = nRange.start.iso(),
|
||||||
|
endStr = nRange.end.iso();
|
||||||
|
|
||||||
|
// Trim nRange.end by one day if custom range.
|
||||||
|
if (newState.range.custom) {
|
||||||
|
nRange.end = new Date(nRange.end.getTime() - msDay);
|
||||||
|
endStr = nRange.end.iso();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#date-range-start').val(startStr);
|
||||||
|
$startPicker.datepicker('setDate', startStr);
|
||||||
|
$('#date-range-end').val(endStr);
|
||||||
|
$endPicker.datepicker('setDate', endStr);
|
||||||
|
}
|
||||||
|
if (newState.range) {
|
||||||
|
if (!newState.range.custom) {
|
||||||
|
var newRange = newState.range,
|
||||||
|
$rangeEl = $('li[data-range="' + _.escape(newRange) + '"]');
|
||||||
|
if ($rangeEl.length) {
|
||||||
|
$rangeSelector.children('li.selected').removeClass('selected');
|
||||||
|
$rangeEl.addClass('selected');
|
||||||
|
} else {
|
||||||
|
$rangeSelector.children('li.selected').removeClass('selected');
|
||||||
|
$('li[data-range="custom"]').addClass('selected');
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
var $endPicker = $("#end-date-picker").datepicker({
|
$rangeSelector.children('li.selected').removeClass('selected');
|
||||||
maxDate: 0,
|
$('[data-range="custom"]').addClass('selected');
|
||||||
minDate: minDate,
|
}
|
||||||
dateFormat: 'yy-mm-dd',
|
populateCustomRange();
|
||||||
onSelect: function(dateText) {
|
}
|
||||||
$("#date-range-end").val(dateText);
|
if (newState.group) {
|
||||||
}
|
$groupSelector.children('.selected').removeClass('selected');
|
||||||
});
|
$('li[data-group="' + newState.group + '"]').addClass('selected');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$rangeSelector.click(function(e) {
|
$('#chart-zoomout').click(_pd);
|
||||||
var $target = $(e.target).parent();
|
|
||||||
var newRange = $target.attr("data-range");
|
|
||||||
if (newRange && newRange != "custom") {
|
|
||||||
$target.trigger('changeview', {range: newRange});
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
$groupSelector.on('click', 'a', function(e) {
|
$('#date-range-form').submit(
|
||||||
var $target = $(this).parent(),
|
_pd(function (e) {
|
||||||
newGroup = $target.attr("data-group");
|
var start = Date.iso($('#date-range-start').val()),
|
||||||
|
end = Date.iso($('#date-range-end').val()),
|
||||||
$(this).trigger('changeview', { group: newGroup });
|
newRange = {
|
||||||
e.preventDefault();
|
custom: true,
|
||||||
});
|
start: Date.iso(start),
|
||||||
|
end: Date.iso(end),
|
||||||
// set controls when `changeview` is detected.
|
};
|
||||||
$(window).on('changeview', function(e, newState) {
|
$rangeSelector.trigger('changeview', { range: newRange });
|
||||||
if (!newState) return;
|
$customModal.hider();
|
||||||
function populateCustomRange() {
|
}),
|
||||||
var nRange = normalizeRange(newState.range),
|
);
|
||||||
startStr = nRange.start.iso(),
|
|
||||||
endStr = nRange.end.iso();
|
|
||||||
|
|
||||||
// Trim nRange.end by one day if custom range.
|
|
||||||
if (newState.range.custom) {
|
|
||||||
nRange.end = new Date(nRange.end.getTime() - msDay);
|
|
||||||
endStr = nRange.end.iso();
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#date-range-start").val(startStr);
|
|
||||||
$startPicker.datepicker("setDate", startStr);
|
|
||||||
$("#date-range-end").val(endStr);
|
|
||||||
$endPicker.datepicker("setDate", endStr);
|
|
||||||
}
|
|
||||||
if (newState.range) {
|
|
||||||
if (!newState.range.custom) {
|
|
||||||
var newRange = newState.range,
|
|
||||||
$rangeEl = $('li[data-range="' + _.escape(newRange) + '"]');
|
|
||||||
if ($rangeEl.length) {
|
|
||||||
$rangeSelector.children("li.selected")
|
|
||||||
.removeClass("selected");
|
|
||||||
$rangeEl.addClass("selected");
|
|
||||||
} else {
|
|
||||||
$rangeSelector.children("li.selected")
|
|
||||||
.removeClass("selected");
|
|
||||||
$('li[data-range="custom"]').addClass("selected");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$rangeSelector.children("li.selected").removeClass("selected");
|
|
||||||
$('[data-range="custom"]').addClass("selected");
|
|
||||||
}
|
|
||||||
populateCustomRange();
|
|
||||||
}
|
|
||||||
if (newState.group) {
|
|
||||||
$groupSelector.children('.selected').removeClass('selected');
|
|
||||||
$('li[data-group="' + newState.group + '"]').addClass('selected');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#chart-zoomout").click(_pd);
|
|
||||||
|
|
||||||
$("#date-range-form").submit(_pd(function(e) {
|
|
||||||
var start = Date.iso($("#date-range-start").val()),
|
|
||||||
end = Date.iso($("#date-range-end").val()),
|
|
||||||
newRange = {
|
|
||||||
custom: true,
|
|
||||||
start: Date.iso(start),
|
|
||||||
end: Date.iso(end)
|
|
||||||
};
|
|
||||||
$rangeSelector.trigger('changeview', {range: newRange});
|
|
||||||
$customModal.hider();
|
|
||||||
}));
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,304 +1,304 @@
|
||||||
var csv_keys = {
|
var csv_keys = {
|
||||||
downloads: {
|
downloads: {
|
||||||
"count": gettext('Downloads')
|
count: gettext('Downloads'),
|
||||||
},
|
},
|
||||||
usage: {
|
usage: {
|
||||||
"count": gettext('Daily Users')
|
count: gettext('Daily Users'),
|
||||||
},
|
},
|
||||||
collections_created: {
|
collections_created: {
|
||||||
'count': gettext('Collections Created')
|
count: gettext('Collections Created'),
|
||||||
},
|
},
|
||||||
addons_in_use: {
|
addons_in_use: {
|
||||||
'count': gettext('Add-ons in Use')
|
count: gettext('Add-ons in Use'),
|
||||||
},
|
},
|
||||||
addons_created: {
|
addons_created: {
|
||||||
'count': gettext('Add-ons Created')
|
count: gettext('Add-ons Created'),
|
||||||
},
|
},
|
||||||
addons_downloaded: {
|
addons_downloaded: {
|
||||||
'count': gettext('Add-ons Downloaded')
|
count: gettext('Add-ons Downloaded'),
|
||||||
},
|
},
|
||||||
addons_updated: {
|
addons_updated: {
|
||||||
'count': gettext('Add-ons Updated')
|
count: gettext('Add-ons Updated'),
|
||||||
},
|
},
|
||||||
reviews_created: {
|
reviews_created: {
|
||||||
'count': gettext('Reviews Written')
|
count: gettext('Reviews Written'),
|
||||||
},
|
},
|
||||||
users_created: {
|
users_created: {
|
||||||
'count': gettext('User Signups')
|
count: gettext('User Signups'),
|
||||||
},
|
},
|
||||||
subscribers: {
|
subscribers: {
|
||||||
'count': gettext('Subscribers')
|
count: gettext('Subscribers'),
|
||||||
},
|
},
|
||||||
ratings: {
|
ratings: {
|
||||||
'count': gettext('Ratings')
|
count: gettext('Ratings'),
|
||||||
},
|
},
|
||||||
sales: {
|
sales: {
|
||||||
'count': gettext('Sales')
|
count: gettext('Sales'),
|
||||||
},
|
},
|
||||||
installs: {
|
installs: {
|
||||||
'count': gettext('Installs')
|
count: gettext('Installs'),
|
||||||
},
|
},
|
||||||
sources: {
|
sources: {
|
||||||
"null" : gettext('Unknown'),
|
null: gettext('Unknown'),
|
||||||
'api' : gettext('Add-ons Manager'),
|
api: gettext('Add-ons Manager'),
|
||||||
'discovery-promo' : gettext('Add-ons Manager Promo'),
|
'discovery-promo': gettext('Add-ons Manager Promo'),
|
||||||
'discovery-featured' : gettext('Add-ons Manager Featured'),
|
'discovery-featured': gettext('Add-ons Manager Featured'),
|
||||||
'discovery-learnmore' : gettext('Add-ons Manager Learn More'),
|
'discovery-learnmore': gettext('Add-ons Manager Learn More'),
|
||||||
'ss' : gettext('Search Suggestions'),
|
ss: gettext('Search Suggestions'),
|
||||||
'search' : gettext('Search Results'),
|
search: gettext('Search Results'),
|
||||||
'homepagepromo' : gettext('Homepage Promo'),
|
homepagepromo: gettext('Homepage Promo'),
|
||||||
'hp-btn-promo' : gettext('Homepage Promo'),
|
'hp-btn-promo': gettext('Homepage Promo'),
|
||||||
'hp-dl-promo' : gettext('Homepage Promo'),
|
'hp-dl-promo': gettext('Homepage Promo'),
|
||||||
'hp-hc-featured' : gettext('Homepage Featured'),
|
'hp-hc-featured': gettext('Homepage Featured'),
|
||||||
'hp-dl-featured' : gettext('Homepage Featured'),
|
'hp-dl-featured': gettext('Homepage Featured'),
|
||||||
'hp-hc-upandcoming' : gettext('Homepage Up and Coming'),
|
'hp-hc-upandcoming': gettext('Homepage Up and Coming'),
|
||||||
'hp-dl-upandcoming' : gettext('Homepage Up and Coming'),
|
'hp-dl-upandcoming': gettext('Homepage Up and Coming'),
|
||||||
'hp-dl-mostpopular' : gettext('Homepage Most Popular'),
|
'hp-dl-mostpopular': gettext('Homepage Most Popular'),
|
||||||
'dp-btn-primary' : gettext('Detail Page'),
|
'dp-btn-primary': gettext('Detail Page'),
|
||||||
'dp-btn-version' : gettext('Detail Page (bottom)'),
|
'dp-btn-version': gettext('Detail Page (bottom)'),
|
||||||
'addondetail' : gettext('Detail Page'),
|
addondetail: gettext('Detail Page'),
|
||||||
'addon-detail-version' : gettext('Detail Page (bottom)'),
|
'addon-detail-version': gettext('Detail Page (bottom)'),
|
||||||
'dp-btn-devchannel' : gettext('Detail Page (Development Channel)'),
|
'dp-btn-devchannel': gettext('Detail Page (Development Channel)'),
|
||||||
'oftenusedwith' : gettext('Often Used With'),
|
oftenusedwith: gettext('Often Used With'),
|
||||||
'dp-hc-oftenusedwith' : gettext('Often Used With'),
|
'dp-hc-oftenusedwith': gettext('Often Used With'),
|
||||||
'dp-dl-oftenusedwith' : gettext('Often Used With'),
|
'dp-dl-oftenusedwith': gettext('Often Used With'),
|
||||||
'dp-hc-othersby' : gettext('Others By Author'),
|
'dp-hc-othersby': gettext('Others By Author'),
|
||||||
'dp-dl-othersby' : gettext('Others By Author'),
|
'dp-dl-othersby': gettext('Others By Author'),
|
||||||
'dp-hc-dependencies' : gettext('Dependencies'),
|
'dp-hc-dependencies': gettext('Dependencies'),
|
||||||
'dp-dl-dependencies' : gettext('Dependencies'),
|
'dp-dl-dependencies': gettext('Dependencies'),
|
||||||
'dp-hc-upsell' : gettext('Upsell'),
|
'dp-hc-upsell': gettext('Upsell'),
|
||||||
'dp-dl-upsell' : gettext('Upsell'),
|
'dp-dl-upsell': gettext('Upsell'),
|
||||||
'developers' : gettext('Meet the Developer'),
|
developers: gettext('Meet the Developer'),
|
||||||
'userprofile' : gettext('User Profile'),
|
userprofile: gettext('User Profile'),
|
||||||
'version-history' : gettext('Version History'),
|
'version-history': gettext('Version History'),
|
||||||
|
|
||||||
'sharingapi' : gettext('Sharing'),
|
sharingapi: gettext('Sharing'),
|
||||||
'category' : gettext('Category Pages'),
|
category: gettext('Category Pages'),
|
||||||
'collection' : gettext('Collections'),
|
collection: gettext('Collections'),
|
||||||
'cb-hc-featured' : gettext('Category Landing Featured Carousel'),
|
'cb-hc-featured': gettext('Category Landing Featured Carousel'),
|
||||||
'cb-dl-featured' : gettext('Category Landing Featured Carousel'),
|
'cb-dl-featured': gettext('Category Landing Featured Carousel'),
|
||||||
'cb-hc-toprated' : gettext('Category Landing Top Rated'),
|
'cb-hc-toprated': gettext('Category Landing Top Rated'),
|
||||||
'cb-dl-toprated' : gettext('Category Landing Top Rated'),
|
'cb-dl-toprated': gettext('Category Landing Top Rated'),
|
||||||
'cb-hc-mostpopular' : gettext('Category Landing Most Popular'),
|
'cb-hc-mostpopular': gettext('Category Landing Most Popular'),
|
||||||
'cb-dl-mostpopular' : gettext('Category Landing Most Popular'),
|
'cb-dl-mostpopular': gettext('Category Landing Most Popular'),
|
||||||
'cb-hc-recentlyadded' : gettext('Category Landing Recently Added'),
|
'cb-hc-recentlyadded': gettext('Category Landing Recently Added'),
|
||||||
'cb-dl-recentlyadded' : gettext('Category Landing Recently Added'),
|
'cb-dl-recentlyadded': gettext('Category Landing Recently Added'),
|
||||||
'cb-btn-featured' : gettext('Browse Listing Featured Sort'),
|
'cb-btn-featured': gettext('Browse Listing Featured Sort'),
|
||||||
'cb-dl-featured' : gettext('Browse Listing Featured Sort'),
|
'cb-dl-featured': gettext('Browse Listing Featured Sort'),
|
||||||
'cb-btn-users' : gettext('Browse Listing Users Sort'),
|
'cb-btn-users': gettext('Browse Listing Users Sort'),
|
||||||
'cb-dl-users' : gettext('Browse Listing Users Sort'),
|
'cb-dl-users': gettext('Browse Listing Users Sort'),
|
||||||
'cb-btn-rating' : gettext('Browse Listing Rating Sort'),
|
'cb-btn-rating': gettext('Browse Listing Rating Sort'),
|
||||||
'cb-dl-rating' : gettext('Browse Listing Rating Sort'),
|
'cb-dl-rating': gettext('Browse Listing Rating Sort'),
|
||||||
'cb-btn-created' : gettext('Browse Listing Created Sort'),
|
'cb-btn-created': gettext('Browse Listing Created Sort'),
|
||||||
'cb-dl-created' : gettext('Browse Listing Created Sort'),
|
'cb-dl-created': gettext('Browse Listing Created Sort'),
|
||||||
'cb-btn-name' : gettext('Browse Listing Name Sort'),
|
'cb-btn-name': gettext('Browse Listing Name Sort'),
|
||||||
'cb-dl-name' : gettext('Browse Listing Name Sort'),
|
'cb-dl-name': gettext('Browse Listing Name Sort'),
|
||||||
'cb-btn-popular' : gettext('Browse Listing Popular Sort'),
|
'cb-btn-popular': gettext('Browse Listing Popular Sort'),
|
||||||
'cb-dl-popular' : gettext('Browse Listing Popular Sort'),
|
'cb-dl-popular': gettext('Browse Listing Popular Sort'),
|
||||||
'cb-btn-updated' : gettext('Browse Listing Updated Sort'),
|
'cb-btn-updated': gettext('Browse Listing Updated Sort'),
|
||||||
'cb-dl-updated' : gettext('Browse Listing Updated Sort'),
|
'cb-dl-updated': gettext('Browse Listing Updated Sort'),
|
||||||
'cb-btn-hotness' : gettext('Browse Listing Up and Coming Sort'),
|
'cb-btn-hotness': gettext('Browse Listing Up and Coming Sort'),
|
||||||
'cb-dl-hotness' : gettext('Browse Listing Up and Coming Sort')
|
'cb-dl-hotness': gettext('Browse Listing Up and Coming Sort'),
|
||||||
},
|
},
|
||||||
contributions: {
|
contributions: {
|
||||||
"count": gettext('Number of Contributions'),
|
count: gettext('Number of Contributions'),
|
||||||
"total": gettext('Total Amount Contributed'),
|
total: gettext('Total Amount Contributed'),
|
||||||
"average": gettext('Average Contribution')
|
average: gettext('Average Contribution'),
|
||||||
},
|
},
|
||||||
overview: {
|
overview: {
|
||||||
'downloads' : gettext('Downloads'),
|
downloads: gettext('Downloads'),
|
||||||
'updates' : gettext('Daily Users')
|
updates: gettext('Daily Users'),
|
||||||
},
|
},
|
||||||
app_overview: {
|
app_overview: {
|
||||||
'installs': gettext('Installs'),
|
installs: gettext('Installs'),
|
||||||
'sales': gettext('Sales'),
|
sales: gettext('Sales'),
|
||||||
'usage': gettext('Usage')
|
usage: gettext('Usage'),
|
||||||
},
|
},
|
||||||
apps : {
|
apps: {
|
||||||
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' : gettext('Firefox'),
|
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': gettext('Firefox'),
|
||||||
'{86c18b42-e466-45a9-ae7a-9b95ba6f5640}' : gettext('Mozilla'),
|
'{86c18b42-e466-45a9-ae7a-9b95ba6f5640}': gettext('Mozilla'),
|
||||||
'{3550f703-e582-4d05-9a08-453d09bdfdc6}' : gettext('Thunderbird'),
|
'{3550f703-e582-4d05-9a08-453d09bdfdc6}': gettext('Thunderbird'),
|
||||||
'{718e30fb-e89b-41dd-9da7-e25a45638b28}' : gettext('Sunbird'),
|
'{718e30fb-e89b-41dd-9da7-e25a45638b28}': gettext('Sunbird'),
|
||||||
'{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}' : gettext('SeaMonkey'),
|
'{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}': gettext('SeaMonkey'),
|
||||||
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}' : gettext('Fennec'),
|
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}': gettext('Fennec'),
|
||||||
'{aa3c5121-dab2-40e2-81ca-7ea25febc110}' : gettext('Android')
|
'{aa3c5121-dab2-40e2-81ca-7ea25febc110}': gettext('Android'),
|
||||||
},
|
},
|
||||||
chartTitle: {
|
chartTitle: {
|
||||||
"overview" : [
|
overview: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Downloads and Daily Users, last {0} days"),
|
gettext('Downloads and Daily Users, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Downloads and Daily Users from {0} to {1}")
|
gettext('Downloads and Daily Users from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"app_overview" : [
|
app_overview: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Installs and Daily Users, last {0} days"),
|
gettext('Installs and Daily Users, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Installs and Daily Users from {0} to {1}")
|
gettext('Installs and Daily Users from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"downloads" : [
|
downloads: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Downloads, last {0} days"),
|
gettext('Downloads, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Downloads from {0} to {1}")
|
gettext('Downloads from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"usage" : [
|
usage: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Daily Users, last {0} days"),
|
gettext('Daily Users, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Daily Users from {0} to {1}")
|
gettext('Daily Users from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"apps" : [
|
apps: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Applications, last {0} days"),
|
gettext('Applications, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Applications from {0} to {1}")
|
gettext('Applications from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"countries" : [
|
countries: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Countries, last {0} days"),
|
gettext('Countries, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Countries from {0} to {1}")
|
gettext('Countries from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"os" : [
|
os: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Platforms, last {0} days"),
|
gettext('Platforms, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Platforms from {0} to {1}")
|
gettext('Platforms from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"locales" : [
|
locales: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Languages, last {0} days"),
|
gettext('Languages, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Languages from {0} to {1}")
|
gettext('Languages from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"versions" : [
|
versions: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-on Versions, last {0} days"),
|
gettext('Add-on Versions, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-on Versions from {0} to {1}")
|
gettext('Add-on Versions from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"statuses" : [
|
statuses: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-on Status, last {0} days"),
|
gettext('Add-on Status, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-on Status from {0} to {1}")
|
gettext('Add-on Status from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"sources" : [
|
sources: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Download Sources, last {0} days"),
|
gettext('Download Sources, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Download Sources from {0} to {1}")
|
gettext('Download Sources from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"mediums" : [
|
mediums: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Download Mediums, last {0} days"),
|
gettext('Download Mediums, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Download Mediums from {0} to {1}")
|
gettext('Download Mediums from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"contents" : [
|
contents: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Download Contents, last {0} days"),
|
gettext('Download Contents, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Download Contents from {0} to {1}")
|
gettext('Download Contents from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"campaigns" : [
|
campaigns: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Download Campaigns, last {0} days"),
|
gettext('Download Campaigns, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Download Campaigns from {0} to {1}")
|
gettext('Download Campaigns from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"contributions" : [
|
contributions: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Contributions, last {0} days"),
|
gettext('Contributions, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Contributions from {0} to {1}")
|
gettext('Contributions from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"site" : [
|
site: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Site Metrics, last {0} days"),
|
gettext('Site Metrics, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Site Metrics from {0} to {1}")
|
gettext('Site Metrics from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"addons_in_use" : [
|
addons_in_use: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-ons in Use, last {0} days"),
|
gettext('Add-ons in Use, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-ons in Use from {0} to {1}")
|
gettext('Add-ons in Use from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"addons_downloaded" : [
|
addons_downloaded: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-ons Downloaded, last {0} days"),
|
gettext('Add-ons Downloaded, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-ons Downloaded from {0} to {1}")
|
gettext('Add-ons Downloaded from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"addons_created" : [
|
addons_created: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-ons Created, last {0} days"),
|
gettext('Add-ons Created, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-ons Created from {0} to {1}")
|
gettext('Add-ons Created from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"addons_updated" : [
|
addons_updated: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Add-ons Updated, last {0} days"),
|
gettext('Add-ons Updated, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Add-ons Updated from {0} to {1}")
|
gettext('Add-ons Updated from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"reviews_created" : [
|
reviews_created: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Reviews Written, last {0} days"),
|
gettext('Reviews Written, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Reviews Written from {0} to {1}")
|
gettext('Reviews Written from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"users_created" : [
|
users_created: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("User Signups, last {0} days"),
|
gettext('User Signups, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("User Signups from {0} to {1}")
|
gettext('User Signups from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"collections_created" : [
|
collections_created: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Collections Created, last {0} days"),
|
gettext('Collections Created, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Collections Created from {0} to {1}")
|
gettext('Collections Created from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"subscribers" : [
|
subscribers: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Subscribers, last {0} days"),
|
gettext('Subscribers, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Subscribers from {0} to {1}")
|
gettext('Subscribers from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"ratings" : [
|
ratings: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Ratings, last {0} days"),
|
gettext('Ratings, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Ratings from {0} to {1}")
|
gettext('Ratings from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"sales" : [
|
sales: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Sales, last {0} days"),
|
gettext('Sales, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Sales from {0} to {1}")
|
gettext('Sales from {0} to {1}'),
|
||||||
],
|
],
|
||||||
"installs" : [
|
installs: [
|
||||||
// L10n: {0} is an integer.
|
// L10n: {0} is an integer.
|
||||||
gettext("Installs, last {0} days"),
|
gettext('Installs, last {0} days'),
|
||||||
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
// L10n: both {0} and {1} are dates in YYYY-MM-DD format.
|
||||||
gettext("Installs from {0} to {1}")
|
gettext('Installs from {0} to {1}'),
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
aggregateLabel: {
|
aggregateLabel: {
|
||||||
"downloads" : [
|
downloads: [
|
||||||
// L10n: {0} and {1} are integers.
|
// L10n: {0} and {1} are integers.
|
||||||
gettext("<b>{0}</b> in last {1} days"),
|
gettext('<b>{0}</b> in last {1} days'),
|
||||||
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
|
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
|
||||||
gettext("<b>{0}</b> from {1} to {2}"),
|
gettext('<b>{0}</b> from {1} to {2}'),
|
||||||
],
|
],
|
||||||
"usage" : [
|
usage: [
|
||||||
// L10n: {0} and {1} are integers.
|
// L10n: {0} and {1} are integers.
|
||||||
gettext("<b>{0}</b> average in last {1} days"),
|
gettext('<b>{0}</b> average in last {1} days'),
|
||||||
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
|
// L10n: {0} is an integer and {1} and {2} are dates in YYYY-MM-DD format.
|
||||||
gettext("<b>{0}</b> from {1} to {2}"),
|
gettext('<b>{0}</b> from {1} to {2}'),
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,113 +1,117 @@
|
||||||
// date management helpers
|
// date management helpers
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
// utility
|
// utility
|
||||||
function pad2(n) {
|
function pad2(n) {
|
||||||
var str = n.toString();
|
var str = n.toString();
|
||||||
return ('0' + str).substr(-2);
|
return ('0' + str).substr(-2);
|
||||||
}
|
}
|
||||||
var intervalRegex = /(-?\d+)\s*(\w)/,
|
var intervalRegex = /(-?\d+)\s*(\w)/,
|
||||||
// ISO date format is used for internal representations.
|
// ISO date format is used for internal representations.
|
||||||
dateRegex = /(\d{4})[^\d]?(\d{2})[^\d]?(\d{2})/;
|
dateRegex = /(\d{4})[^\d]?(\d{2})[^\d]?(\d{2})/;
|
||||||
|
|
||||||
_.extend(Date.prototype, {
|
_.extend(Date.prototype, {
|
||||||
forward : function(by, unit) {
|
forward: function (by, unit) {
|
||||||
if (typeof by == 'string') {
|
if (typeof by == 'string') {
|
||||||
var match = intervalRegex.exec(by);
|
var match = intervalRegex.exec(by);
|
||||||
by = +match[1];
|
by = +match[1];
|
||||||
unit = match[2];
|
unit = match[2];
|
||||||
}
|
}
|
||||||
unit = unit || 'd';
|
unit = unit || 'd';
|
||||||
switch (unit[0]) {
|
switch (unit[0]) {
|
||||||
case 'h':
|
case 'h':
|
||||||
this.setHours(this.getHours()+by);
|
this.setHours(this.getHours() + by);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
this.setDate(this.getDate()+by);
|
this.setDate(this.getDate() + by);
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
this.setDate(this.getDate()+by*7);
|
this.setDate(this.getDate() + by * 7);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
this.setMonth(this.getMonth()+by);
|
this.setMonth(this.getMonth() + by);
|
||||||
break;
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
this.setFullYear(this.getFullYear()+by);
|
this.setFullYear(this.getFullYear() + by);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
backward : function(by, unit) {
|
backward: function (by, unit) {
|
||||||
if (typeof by == 'string') {
|
if (typeof by == 'string') {
|
||||||
var match = intervalRegex.exec(by);
|
var match = intervalRegex.exec(by);
|
||||||
by = +match[1];
|
by = +match[1];
|
||||||
unit = match[2];
|
unit = match[2];
|
||||||
}
|
}
|
||||||
return this.forward(-by, unit);
|
return this.forward(-by, unit);
|
||||||
},
|
},
|
||||||
pretty : function(del) {
|
pretty: function (del) {
|
||||||
del = del || '';
|
del = del || '';
|
||||||
return [this.getFullYear(), pad2(this.getMonth()+1), pad2(this.getDate())].join(del);
|
return [
|
||||||
},
|
this.getFullYear(),
|
||||||
iso : function() {
|
pad2(this.getMonth() + 1),
|
||||||
return this.pretty('-');
|
pad2(this.getDate()),
|
||||||
},
|
].join(del);
|
||||||
isAfter : function(d) {
|
},
|
||||||
return this.getTime() > d.getTime();
|
iso: function () {
|
||||||
},
|
return this.pretty('-');
|
||||||
isBefore : function(d) {
|
},
|
||||||
return this.getTime() < d.getTime();
|
isAfter: function (d) {
|
||||||
},
|
return this.getTime() > d.getTime();
|
||||||
latter : function(d) {
|
},
|
||||||
return this.isAfter(d) ? this : d;
|
isBefore: function (d) {
|
||||||
},
|
return this.getTime() < d.getTime();
|
||||||
former : function(d) {
|
},
|
||||||
return this.isBefore(d) ? this : d;
|
latter: function (d) {
|
||||||
},
|
return this.isAfter(d) ? this : d;
|
||||||
clone : function() {
|
},
|
||||||
return new Date(this.getTime());
|
former: function (d) {
|
||||||
}
|
return this.isBefore(d) ? this : d;
|
||||||
});
|
},
|
||||||
_.extend(Date, {
|
clone: function () {
|
||||||
ago : function(s) {
|
return new Date(this.getTime());
|
||||||
return (new Date()).backward(s);
|
},
|
||||||
},
|
});
|
||||||
iso : function(s) {
|
_.extend(Date, {
|
||||||
if (s instanceof Date) return s;
|
ago: function (s) {
|
||||||
var d = dateRegex.exec(s);
|
return new Date().backward(s);
|
||||||
if (d) {
|
},
|
||||||
return new Date(d[1],d[2]-1,d[3]);
|
iso: function (s) {
|
||||||
}
|
if (s instanceof Date) return s;
|
||||||
}
|
var d = dateRegex.exec(s);
|
||||||
});
|
if (d) {
|
||||||
_.extend(String, {
|
return new Date(d[1], d[2] - 1, d[3]);
|
||||||
max : function(a,b) {
|
}
|
||||||
return a > b ? a : b;
|
},
|
||||||
},
|
});
|
||||||
min : function(a,b) {
|
_.extend(String, {
|
||||||
return a < b ? a : b;
|
max: function (a, b) {
|
||||||
}
|
return a > b ? a : b;
|
||||||
});
|
},
|
||||||
|
min: function (a, b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
},
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function forEachISODate(range, step, data, iterator, context) {
|
function forEachISODate(range, step, data, iterator, context) {
|
||||||
var d = range.start.clone();
|
var d = range.start.clone();
|
||||||
for (d; d.isBefore(range.end); d.forward(step)) {
|
for (d; d.isBefore(range.end); d.forward(step)) {
|
||||||
var ds = d.iso();
|
var ds = d.iso();
|
||||||
iterator.call(context, data[ds], d, ds);
|
iterator.call(context, data[ds], d, ds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeRange(range) {
|
function normalizeRange(range) {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
if (typeof range == "string") {
|
if (typeof range == 'string') {
|
||||||
ret.start = Date.ago(range);
|
ret.start = Date.ago(range);
|
||||||
ret.end = (new Date());
|
ret.end = new Date();
|
||||||
} else if (typeof range == "object") {
|
} else if (typeof range == 'object') {
|
||||||
ret.start = new Date(range.start);
|
ret.start = new Date(range.start);
|
||||||
ret.end = new Date(range.end);
|
ret.end = new Date(range.end);
|
||||||
} else {
|
} else {
|
||||||
throw "Invalid range values found.";
|
throw 'Invalid range values found.';
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,47 @@
|
||||||
// Web Worker Pool
|
// Web Worker Pool
|
||||||
// size is the max number of arguments
|
// size is the max number of arguments
|
||||||
function WorkerPool(size) {
|
function WorkerPool(size) {
|
||||||
var workers = 0,
|
var workers = 0,
|
||||||
jobs = [];
|
jobs = [];
|
||||||
|
|
||||||
// url: the url of the worker's js
|
// url: the url of the worker's js
|
||||||
// msg: the initial message to pass to the worker
|
// msg: the initial message to pass to the worker
|
||||||
// cb : the callback to recieve messages from postMessage.
|
// cb : the callback to recieve messages from postMessage.
|
||||||
// return true from cb to dismiss the worker and advance the queue.
|
// return true from cb to dismiss the worker and advance the queue.
|
||||||
// ctx: the context for cb.apply
|
// ctx: the context for cb.apply
|
||||||
this.queueJob = function(url, msg, cb, ctx) {
|
this.queueJob = function (url, msg, cb, ctx) {
|
||||||
var job = {
|
var job = {
|
||||||
"url": url,
|
url: url,
|
||||||
"msg": msg,
|
msg: msg,
|
||||||
"cb" : cb,
|
cb: cb,
|
||||||
"ctx": ctx
|
ctx: ctx,
|
||||||
};
|
|
||||||
jobs.push(job);
|
|
||||||
if (workers < size) nextJob();
|
|
||||||
};
|
};
|
||||||
|
jobs.push(job);
|
||||||
|
if (workers < size) nextJob();
|
||||||
|
};
|
||||||
|
|
||||||
function nextJob() {
|
function nextJob() {
|
||||||
if (jobs.length) {
|
if (jobs.length) {
|
||||||
(function() {
|
(function () {
|
||||||
var job = jobs.shift(),
|
var job = jobs.shift(),
|
||||||
worker = new Worker(job.url);
|
worker = new Worker(job.url);
|
||||||
workers++;
|
workers++;
|
||||||
worker.addEventListener('message', function(e) {
|
worker.addEventListener(
|
||||||
if (job.cb.call(job.ctx, e.data, worker)) {
|
'message',
|
||||||
worker.terminate();
|
function (e) {
|
||||||
worker = null;
|
if (job.cb.call(job.ctx, e.data, worker)) {
|
||||||
workers--;
|
worker.terminate();
|
||||||
nextJob();
|
worker = null;
|
||||||
};
|
workers--;
|
||||||
}, false);
|
nextJob();
|
||||||
worker.postMessage(job.msg);
|
}
|
||||||
})();
|
},
|
||||||
}
|
false,
|
||||||
|
);
|
||||||
|
worker.postMessage(job.msg);
|
||||||
|
})();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple Asynchronous Cache
|
// Simple Asynchronous Cache
|
||||||
|
@ -49,44 +53,46 @@ function WorkerPool(size) {
|
||||||
// Takes one parameter:
|
// Takes one parameter:
|
||||||
// * key
|
// * key
|
||||||
function AsyncCache(miss, hash) {
|
function AsyncCache(miss, hash) {
|
||||||
var cache = {},
|
var cache = {},
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
hash = hash || function(key) {
|
hash =
|
||||||
return key.toString();
|
hash ||
|
||||||
|
function (key) {
|
||||||
|
return key.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
// key: the key to lookup in the cache
|
// key: the key to lookup in the cache
|
||||||
// cb : the method to call with the value
|
// cb : the method to call with the value
|
||||||
// Takes one parameter:
|
// Takes one parameter:
|
||||||
// val: the value in the cache for key
|
// val: the value in the cache for key
|
||||||
// ctx: context for cb.call
|
// ctx: context for cb.call
|
||||||
this.get = function(key, cb, ctx) {
|
this.get = function (key, cb, ctx) {
|
||||||
var k = hash(key);
|
var k = hash(key);
|
||||||
if (k in cache) {
|
if (k in cache) {
|
||||||
cb.call(ctx, cache[k]);
|
cb.call(ctx, cache[k]);
|
||||||
} else {
|
} else {
|
||||||
miss.call(ctx, key, function(val) {
|
miss.call(ctx, key, function (val) {
|
||||||
self.set(key, val);
|
self.set(key, val);
|
||||||
self.get(key, cb, ctx);
|
self.get(key, cb, ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// sets value for key in cache
|
// sets value for key in cache
|
||||||
this.set = function(key, val) {
|
this.set = function (key, val) {
|
||||||
cache[hash(key)] = val;
|
cache[hash(key)] = val;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function hashObj(o) {
|
function hashObj(o) {
|
||||||
var hash = [];
|
var hash = [];
|
||||||
for (var i in o) {
|
for (var i in o) {
|
||||||
if (o.hasOwnProperty(i)) {
|
if (o.hasOwnProperty(i)) {
|
||||||
hash.push(o[i].toString());
|
hash.push(o[i].toString());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return hash.join('_');
|
}
|
||||||
|
return hash.join('_');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cfg takes:
|
/* cfg takes:
|
||||||
|
@ -99,27 +105,26 @@ function hashObj(o) {
|
||||||
* ctx: context from which to run all functions
|
* ctx: context from which to run all functions
|
||||||
*/
|
*/
|
||||||
function chunkfor(cfg) {
|
function chunkfor(cfg) {
|
||||||
var position = cfg.start;
|
var position = cfg.start;
|
||||||
|
|
||||||
function nextchunk() {
|
function nextchunk() {
|
||||||
if (position < cfg.end) {
|
if (position < cfg.end) {
|
||||||
|
for (
|
||||||
|
var iterator = position;
|
||||||
|
iterator < position + cfg.chunk_size * cfg.step && iterator < cfg.end;
|
||||||
|
iterator += cfg.step
|
||||||
|
) {
|
||||||
|
cfg.inner.call(cfg.ctx, iterator);
|
||||||
|
}
|
||||||
|
|
||||||
for (var iterator = position;
|
position += cfg.chunk_size * cfg.step;
|
||||||
iterator < position+(cfg.chunk_size*cfg.step) && iterator < cfg.end;
|
|
||||||
iterator += cfg.step) {
|
|
||||||
|
|
||||||
cfg.inner.call(cfg.ctx, iterator);
|
setTimeout(function () {
|
||||||
}
|
nextchunk.call(this);
|
||||||
|
}, 0);
|
||||||
position += cfg.chunk_size * cfg.step;
|
} else {
|
||||||
|
cfg.callback.call(cfg.ctx);
|
||||||
setTimeout( function () {
|
|
||||||
nextchunk.call(this);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
cfg.callback.call(cfg.ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
nextchunk();
|
}
|
||||||
|
nextchunk();
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,55 +1,54 @@
|
||||||
$(function() {
|
$(function () {
|
||||||
if ($('.primary').attr('data-report') != 'overview') return;
|
if ($('.primary').attr('data-report') != 'overview') return;
|
||||||
|
|
||||||
// set up topcharts (defined in topchart.js)
|
// set up topcharts (defined in topchart.js)
|
||||||
$('.toplist').topChart();
|
$('.toplist').topChart();
|
||||||
|
|
||||||
$(window).on("changeview", function(e, view) {
|
$(window).on('changeview', function (e, view) {
|
||||||
$('.two-up').addClass('loading');
|
$('.two-up').addClass('loading');
|
||||||
|
});
|
||||||
|
// Save some requests by waiting until the graph data is ready.
|
||||||
|
$(window).on('dataready', function (e, data) {
|
||||||
|
// return;
|
||||||
|
var view = _.extend({}, data.view, { group: 'all' }),
|
||||||
|
range = normalizeRange(view.range);
|
||||||
|
|
||||||
|
// get aggregates for Daily Users and Downloads for the given time range.
|
||||||
|
$.when(z.StatsManager.getDataRange(view)).then(function (data) {
|
||||||
|
if (data.empty) {
|
||||||
|
$('#downloads-in-range, #users-in-range').text(
|
||||||
|
gettext('No data available.'),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// make all that data pretty.
|
||||||
|
var aggregateRow = data[data.firstIndex].data,
|
||||||
|
totalDownloads = Highcharts.numberFormat(aggregateRow.downloads, 0),
|
||||||
|
totalUsers = Highcharts.numberFormat(aggregateRow.updates, 0),
|
||||||
|
startString = range.start.iso(),
|
||||||
|
endString = range.end.iso(),
|
||||||
|
downloadFormat,
|
||||||
|
userFormat;
|
||||||
|
if (typeof view.range == 'string') {
|
||||||
|
(downloadFormat = csv_keys.aggregateLabel.downloads[0]),
|
||||||
|
(userFormat = csv_keys.aggregateLabel.usage[0]);
|
||||||
|
$('#downloads-in-range').html(
|
||||||
|
format(downloadFormat, totalDownloads, parseInt(view.range, 10)),
|
||||||
|
);
|
||||||
|
$('#users-in-range').html(
|
||||||
|
format(userFormat, totalUsers, parseInt(view.range, 10)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(downloadFormat = csv_keys.aggregateLabel.downloads[1]),
|
||||||
|
(userFormat = csv_keys.aggregateLabel.usage[1]);
|
||||||
|
$('#downloads-in-range').html(
|
||||||
|
format(downloadFormat, totalDownloads, startString, endString),
|
||||||
|
);
|
||||||
|
$('#users-in-range').html(
|
||||||
|
format(userFormat, totalUsers, startString, endString),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('.two-up').removeClass('loading');
|
||||||
});
|
});
|
||||||
// Save some requests by waiting until the graph data is ready.
|
});
|
||||||
$(window).on("dataready", function(e, data) {
|
});
|
||||||
// return;
|
|
||||||
var view = _.extend({}, data.view, {group: 'all'}),
|
|
||||||
range = normalizeRange(view.range);
|
|
||||||
|
|
||||||
// get aggregates for Daily Users and Downloads for the given time range.
|
|
||||||
$.when(z.StatsManager.getDataRange(view)).then(function(data) {
|
|
||||||
if (data.empty) {
|
|
||||||
$("#downloads-in-range, #users-in-range").text(gettext('No data available.'));
|
|
||||||
} else {
|
|
||||||
// make all that data pretty.
|
|
||||||
var aggregateRow = data[data.firstIndex].data,
|
|
||||||
totalDownloads = Highcharts.numberFormat(aggregateRow.downloads, 0),
|
|
||||||
totalUsers = Highcharts.numberFormat(aggregateRow.updates, 0),
|
|
||||||
startString = range.start.iso(),
|
|
||||||
endString = range.end.iso(),
|
|
||||||
downloadFormat,
|
|
||||||
userFormat;
|
|
||||||
if (typeof view.range == 'string') {
|
|
||||||
downloadFormat = csv_keys.aggregateLabel.downloads[0],
|
|
||||||
userFormat = csv_keys.aggregateLabel.usage[0];
|
|
||||||
$("#downloads-in-range").html(format(downloadFormat,
|
|
||||||
totalDownloads,
|
|
||||||
parseInt(view.range, 10)));
|
|
||||||
$("#users-in-range").html(format(userFormat,
|
|
||||||
totalUsers,
|
|
||||||
parseInt(view.range, 10)));
|
|
||||||
} else {
|
|
||||||
downloadFormat = csv_keys.aggregateLabel.downloads[1],
|
|
||||||
userFormat = csv_keys.aggregateLabel.usage[1];
|
|
||||||
$("#downloads-in-range").html(format(downloadFormat,
|
|
||||||
totalDownloads,
|
|
||||||
startString,
|
|
||||||
endString));
|
|
||||||
$("#users-in-range").html(format(userFormat,
|
|
||||||
totalUsers,
|
|
||||||
startString,
|
|
||||||
endString));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
$('.two-up').removeClass('loading');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
|
@ -80,7 +80,7 @@
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
range: ssView.range,
|
range: ssView.range,
|
||||||
group: ssView.group,
|
group: ssView.group,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
@ -107,7 +107,7 @@
|
||||||
var url =
|
var url =
|
||||||
baseURL +
|
baseURL +
|
||||||
[metric, 'day', range.start.pretty(''), range.end.pretty('')].join(
|
[metric, 'day', range.start.pretty(''), range.end.pretty('')].join(
|
||||||
'-'
|
'-',
|
||||||
);
|
);
|
||||||
|
|
||||||
$('#export_data_csv').attr('href', url + '.csv');
|
$('#export_data_csv').attr('href', url + '.csv');
|
||||||
|
|
|
@ -1,117 +1,134 @@
|
||||||
(function($) {
|
(function ($) {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
$.fn.csvTable = function(cfg) {
|
$.fn.csvTable = function (cfg) {
|
||||||
$(this).each(function() {
|
$(this).each(function () {
|
||||||
var $self = $(this),
|
var $self = $(this),
|
||||||
$table = $self.find('table'),
|
$table = $self.find('table'),
|
||||||
$thead = $self.find('thead'),
|
$thead = $self.find('thead'),
|
||||||
$paginator = $self.find('.paginator'),
|
$paginator = $self.find('.paginator'),
|
||||||
pageSize = 14,
|
pageSize = 14,
|
||||||
pages = {},
|
pages = {},
|
||||||
metric = $('.primary').attr('data-report'),
|
metric = $('.primary').attr('data-report'),
|
||||||
currentPage;
|
currentPage;
|
||||||
|
|
||||||
$(document).ready(init);
|
$(document).ready(init);
|
||||||
function init() {
|
function init() {
|
||||||
gotoPage(0);
|
gotoPage(0);
|
||||||
$paginator.on('click', '.next', _pd(function() {
|
$paginator.on(
|
||||||
if ($(this).hasClass('disabled')) return;
|
'click',
|
||||||
gotoPage(currentPage+1);
|
'.next',
|
||||||
}));
|
_pd(function () {
|
||||||
$paginator.on('click', '.prev', _pd(function() {
|
if ($(this).hasClass('disabled')) return;
|
||||||
if ($(this).hasClass('disabled')) return;
|
gotoPage(currentPage + 1);
|
||||||
gotoPage(currentPage-1);
|
}),
|
||||||
}));
|
);
|
||||||
}
|
$paginator.on(
|
||||||
|
'click',
|
||||||
|
'.prev',
|
||||||
|
_pd(function () {
|
||||||
|
if ($(this).hasClass('disabled')) return;
|
||||||
|
gotoPage(currentPage - 1);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function gotoPage(page) {
|
function gotoPage(page) {
|
||||||
if (page < 0) {
|
if (page < 0) {
|
||||||
page = 0;
|
page = 0;
|
||||||
}
|
}
|
||||||
$paginator.find('.prev').toggleClass('disabled', page==0);
|
$paginator.find('.prev').toggleClass('disabled', page == 0);
|
||||||
if (pages[page]) {
|
if (pages[page]) {
|
||||||
showPage(page);
|
showPage(page);
|
||||||
} else {
|
} else {
|
||||||
$self.parent().addClass('loading');
|
$self.parent().addClass('loading');
|
||||||
$.when(getPage(page))
|
$.when(getPage(page)).then(function () {
|
||||||
.then(function() {
|
showPage(page);
|
||||||
showPage(page);
|
$self.parent().removeClass('loading');
|
||||||
$self.parent().removeClass('loading');
|
getPage(page + 1);
|
||||||
getPage(page+1);
|
getPage(page - 1);
|
||||||
getPage(page-1);
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function showPage(page) {
|
function showPage(page) {
|
||||||
var p = pages[page];
|
var p = pages[page];
|
||||||
if (p) {
|
if (p) {
|
||||||
$table.find('tbody').hide();
|
$table.find('tbody').hide();
|
||||||
p.el.show();
|
p.el.show();
|
||||||
$thead.empty().html(p.head);
|
$thead.empty().html(p.head);
|
||||||
}
|
}
|
||||||
currentPage = page;
|
currentPage = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPage(page) {
|
function getPage(page) {
|
||||||
if (pages[page] || page < 0) return;
|
if (pages[page] || page < 0) return;
|
||||||
var $def = $.Deferred(),
|
var $def = $.Deferred(),
|
||||||
range = {
|
range = {
|
||||||
end : Date.ago(pageSize * page + 'days'),
|
end: Date.ago(pageSize * page + 'days'),
|
||||||
start : Date.ago(pageSize * page + pageSize + 'days')
|
start: Date.ago(pageSize * page + pageSize + 'days'),
|
||||||
},
|
},
|
||||||
view = {
|
view = {
|
||||||
metric : metric,
|
metric: metric,
|
||||||
group : 'day',
|
group: 'day',
|
||||||
range : range
|
range: range,
|
||||||
};
|
};
|
||||||
$.when(z.StatsManager.getDataRange(view))
|
$.when(z.StatsManager.getDataRange(view)).then(function (data) {
|
||||||
.then(function(data) {
|
var fields = z.StatsManager.getAvailableFields(view),
|
||||||
var fields = z.StatsManager.getAvailableFields(view),
|
newBody = '<tbody>',
|
||||||
newBody = '<tbody>',
|
newPage = {},
|
||||||
newPage = {},
|
newHead = '<tr><th>' + gettext('Date') + '</th>',
|
||||||
newHead = '<tr><th>' + gettext('Date') + '</th>',
|
row;
|
||||||
row;
|
|
||||||
|
|
||||||
_.each(fields, function(f) {
|
_.each(fields, function (f) {
|
||||||
var id = f.split('|').pop(),
|
var id = f.split('|').pop(),
|
||||||
prettyName = _.escape(z.StatsManager.getPrettyName(metric, id)),
|
prettyName = _.escape(z.StatsManager.getPrettyName(metric, id)),
|
||||||
trimmedPrettyName = (prettyName.length > 32) ? (prettyName.substr(0, 32) + '...') : prettyName;
|
trimmedPrettyName =
|
||||||
newHead += format('<th title="{0}">', prettyName);
|
prettyName.length > 32
|
||||||
newHead += trimmedPrettyName;
|
? prettyName.substr(0, 32) + '...'
|
||||||
newHead += '</th>';
|
: prettyName;
|
||||||
});
|
newHead += format('<th title="{0}">', prettyName);
|
||||||
|
newHead += trimmedPrettyName;
|
||||||
|
newHead += '</th>';
|
||||||
|
});
|
||||||
|
|
||||||
var d = range.end.clone().backward('1 day'),
|
var d = range.end.clone().backward('1 day'),
|
||||||
lastRowDate = range.start.clone().backward('1 day');
|
lastRowDate = range.start.clone().backward('1 day');
|
||||||
for (; lastRowDate.isBefore(d); d.backward('1 day')) {
|
for (; lastRowDate.isBefore(d); d.backward('1 day')) {
|
||||||
row = data[d.iso()] || {};
|
row = data[d.iso()] || {};
|
||||||
newBody += '<tr>';
|
newBody += '<tr>';
|
||||||
newBody += '<th>' + Highcharts.dateFormat('%a, %b %e, %Y', Date.iso(d)) + "</th>";
|
newBody +=
|
||||||
_.each(fields, function(f) {
|
'<th>' +
|
||||||
newBody += '<td>';
|
Highcharts.dateFormat('%a, %b %e, %Y', Date.iso(d)) +
|
||||||
if (metric == 'contributions' && f != 'count') {
|
'</th>';
|
||||||
newBody += '$' + Highcharts.numberFormat(z.StatsManager.getField(row, f), 2);
|
_.each(fields, function (f) {
|
||||||
} else {
|
newBody += '<td>';
|
||||||
newBody += Highcharts.numberFormat(z.StatsManager.getField(row, f),0);
|
if (metric == 'contributions' && f != 'count') {
|
||||||
}
|
newBody +=
|
||||||
newBody += '</td>';
|
'$' +
|
||||||
});
|
Highcharts.numberFormat(z.StatsManager.getField(row, f), 2);
|
||||||
newBody += '</tr>';
|
} else {
|
||||||
}
|
newBody += Highcharts.numberFormat(
|
||||||
newBody += '</tbody>';
|
z.StatsManager.getField(row, f),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
newBody += '</td>';
|
||||||
|
});
|
||||||
|
newBody += '</tr>';
|
||||||
|
}
|
||||||
|
newBody += '</tbody>';
|
||||||
|
|
||||||
newPage.el = $(newBody);
|
newPage.el = $(newBody);
|
||||||
newPage.head = newHead;
|
newPage.head = newHead;
|
||||||
|
|
||||||
$table.append(newPage.el);
|
$table.append(newPage.el);
|
||||||
pages[page] = newPage;
|
pages[page] = newPage;
|
||||||
|
|
||||||
$def.resolve();
|
$def.resolve();
|
||||||
});
|
|
||||||
return $def;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
return $def;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,141 +1,158 @@
|
||||||
(function($) {
|
(function ($) {
|
||||||
// "use strict";
|
// "use strict";
|
||||||
var baseConfig = {
|
var baseConfig = {
|
||||||
chart: {
|
chart: {
|
||||||
backgroundColor: null
|
backgroundColor: null,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: null
|
text: null,
|
||||||
},
|
},
|
||||||
plotArea: {
|
plotArea: {
|
||||||
shadow: null,
|
shadow: null,
|
||||||
borderWidth: null
|
borderWidth: null,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
enabled: false
|
enabled: false,
|
||||||
},
|
},
|
||||||
plotOptions: {
|
plotOptions: {
|
||||||
pie: {
|
pie: {
|
||||||
allowPointSelect: false,
|
allowPointSelect: false,
|
||||||
dataLabels: {
|
dataLabels: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
color: '#333'
|
color: '#333',
|
||||||
},
|
},
|
||||||
animation: false,
|
animation: false,
|
||||||
size:190
|
size: 190,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
credits: {enabled:false},
|
credits: { enabled: false },
|
||||||
legend: {
|
legend: {
|
||||||
enabled:false
|
enabled: false,
|
||||||
},
|
},
|
||||||
series: [{
|
series: [
|
||||||
type: 'pie'
|
{
|
||||||
}]
|
type: 'pie',
|
||||||
};
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
$.fn.topChart = function(cfg) {
|
$.fn.topChart = function (cfg) {
|
||||||
$(this).each(function() {
|
$(this).each(function () {
|
||||||
var $self = $(this),
|
var $self = $(this),
|
||||||
$win = $(window),
|
$win = $(window),
|
||||||
$chart = $self.find('.piechart'),
|
$chart = $self.find('.piechart'),
|
||||||
hChart,
|
hChart,
|
||||||
$table = $self.find('table'),
|
$table = $self.find('table'),
|
||||||
metric = $table.attr('data-metric'),
|
metric = $table.attr('data-metric'),
|
||||||
view = {
|
view = {
|
||||||
'metric': metric,
|
metric: metric,
|
||||||
'group' : 'all'
|
group: 'all',
|
||||||
};
|
};
|
||||||
|
|
||||||
// reload the data when the view's range is modified.
|
// reload the data when the view's range is modified.
|
||||||
$win.on('changeview', function(e, newView) {
|
$win.on('changeview', function (e, newView) {
|
||||||
// we only want to respond to changes in range.
|
// we only want to respond to changes in range.
|
||||||
if (!newView.range) return;
|
if (!newView.range) return;
|
||||||
$self.addClass('loading');
|
$self.addClass('loading');
|
||||||
$self.removeClass('nodata');
|
$self.removeClass('nodata');
|
||||||
_.extend(view, {'range' : normalizeRange(newView.range)});
|
_.extend(view, { range: normalizeRange(newView.range) });
|
||||||
$.when(z.StatsManager.getDataRange(view))
|
$.when(z.StatsManager.getDataRange(view)).then(function (data) {
|
||||||
.then(function(data) {
|
generateRankedList(data, render);
|
||||||
generateRankedList(data, render);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// We take the data (aggregated to one row)
|
|
||||||
function generateRankedList(data, done) {
|
|
||||||
if (data.empty) {
|
|
||||||
$self.removeClass('loading');
|
|
||||||
$self.addClass('nodata');
|
|
||||||
if (hChart && hChart.destroy) hChart.destroy();
|
|
||||||
$table.html('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var totalValue = 0,
|
|
||||||
otherValue = 0;
|
|
||||||
data = data[data.firstIndex].data;
|
|
||||||
if (_.isEmpty(data)) return;
|
|
||||||
// Sum all fields.
|
|
||||||
_.each(data, function(val) {
|
|
||||||
totalValue += val;
|
|
||||||
});
|
|
||||||
// Convert all fields to percentages and prettify names.
|
|
||||||
var rankedList = _.map(data, function(val, key) {
|
|
||||||
var field = key.split("|").slice(-1)[0];
|
|
||||||
return [
|
|
||||||
z.StatsManager.getPrettyName(metric, field),
|
|
||||||
val,
|
|
||||||
(val / totalValue) * 100
|
|
||||||
];
|
|
||||||
});
|
|
||||||
// Sort by value.
|
|
||||||
rankedList = _.sortBy(rankedList, function(a) {
|
|
||||||
return -a[1];
|
|
||||||
});
|
|
||||||
// Calculate the 'Other' percentage
|
|
||||||
for (var i=5; i<rankedList.length; i++) {
|
|
||||||
otherValue += rankedList[i][1];
|
|
||||||
}
|
|
||||||
// Take the top 5 values and append an 'Other' row.
|
|
||||||
rankedList = rankedList.slice(0,5);
|
|
||||||
rankedList.push([
|
|
||||||
gettext('Other'),
|
|
||||||
otherValue,
|
|
||||||
(otherValue / totalValue) * 100
|
|
||||||
]);
|
|
||||||
// Move on with our lives.
|
|
||||||
done(rankedList);
|
|
||||||
}
|
|
||||||
|
|
||||||
var tableRow = template("<tr><td>{0}</td><td title=\"{3}\">{1}</td><td>({2}%)</td></tr>");
|
|
||||||
|
|
||||||
function render(data) {
|
|
||||||
var newBody = "<tbody>";
|
|
||||||
_.each(data, function(row) {
|
|
||||||
var title = row[0];
|
|
||||||
var raw_value = row[1];
|
|
||||||
var value = Highcharts.numberFormat(raw_value, raw_value < 10 ? 1 : 0);
|
|
||||||
var percent = Highcharts.numberFormat(row[2], 0);
|
|
||||||
|
|
||||||
if (percent < 1) {
|
|
||||||
percent = "<1";
|
|
||||||
}
|
|
||||||
|
|
||||||
newBody += tableRow([title, value, percent, raw_value]);
|
|
||||||
});
|
|
||||||
newBody += "</tbody>";
|
|
||||||
$table.html(newBody);
|
|
||||||
|
|
||||||
// set up chart.
|
|
||||||
var newConfig = _.clone(baseConfig),
|
|
||||||
row;
|
|
||||||
newConfig.chart.renderTo = $chart[0];
|
|
||||||
newConfig.series[0].data = _.map(data, function(r) { return r.slice(0,2); });
|
|
||||||
hChart = new Highcharts.Chart(newConfig);
|
|
||||||
for (i = 0; i < data.length; i++) {
|
|
||||||
row = $table.find('tr').eq(i);
|
|
||||||
row.children().eq(0).append($("<b class='seriesdot' style='background:" + hChart.series[0].data[i].color + "'> </b>"));
|
|
||||||
}
|
|
||||||
$self.removeClass('loading');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
|
// We take the data (aggregated to one row)
|
||||||
|
function generateRankedList(data, done) {
|
||||||
|
if (data.empty) {
|
||||||
|
$self.removeClass('loading');
|
||||||
|
$self.addClass('nodata');
|
||||||
|
if (hChart && hChart.destroy) hChart.destroy();
|
||||||
|
$table.html('');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var totalValue = 0,
|
||||||
|
otherValue = 0;
|
||||||
|
data = data[data.firstIndex].data;
|
||||||
|
if (_.isEmpty(data)) return;
|
||||||
|
// Sum all fields.
|
||||||
|
_.each(data, function (val) {
|
||||||
|
totalValue += val;
|
||||||
|
});
|
||||||
|
// Convert all fields to percentages and prettify names.
|
||||||
|
var rankedList = _.map(data, function (val, key) {
|
||||||
|
var field = key.split('|').slice(-1)[0];
|
||||||
|
return [
|
||||||
|
z.StatsManager.getPrettyName(metric, field),
|
||||||
|
val,
|
||||||
|
(val / totalValue) * 100,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
// Sort by value.
|
||||||
|
rankedList = _.sortBy(rankedList, function (a) {
|
||||||
|
return -a[1];
|
||||||
|
});
|
||||||
|
// Calculate the 'Other' percentage
|
||||||
|
for (var i = 5; i < rankedList.length; i++) {
|
||||||
|
otherValue += rankedList[i][1];
|
||||||
|
}
|
||||||
|
// Take the top 5 values and append an 'Other' row.
|
||||||
|
rankedList = rankedList.slice(0, 5);
|
||||||
|
rankedList.push([
|
||||||
|
gettext('Other'),
|
||||||
|
otherValue,
|
||||||
|
(otherValue / totalValue) * 100,
|
||||||
|
]);
|
||||||
|
// Move on with our lives.
|
||||||
|
done(rankedList);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableRow = template(
|
||||||
|
'<tr><td>{0}</td><td title="{3}">{1}</td><td>({2}%)</td></tr>',
|
||||||
|
);
|
||||||
|
|
||||||
|
function render(data) {
|
||||||
|
var newBody = '<tbody>';
|
||||||
|
_.each(data, function (row) {
|
||||||
|
var title = row[0];
|
||||||
|
var raw_value = row[1];
|
||||||
|
var value = Highcharts.numberFormat(
|
||||||
|
raw_value,
|
||||||
|
raw_value < 10 ? 1 : 0,
|
||||||
|
);
|
||||||
|
var percent = Highcharts.numberFormat(row[2], 0);
|
||||||
|
|
||||||
|
if (percent < 1) {
|
||||||
|
percent = '<1';
|
||||||
|
}
|
||||||
|
|
||||||
|
newBody += tableRow([title, value, percent, raw_value]);
|
||||||
|
});
|
||||||
|
newBody += '</tbody>';
|
||||||
|
$table.html(newBody);
|
||||||
|
|
||||||
|
// set up chart.
|
||||||
|
var newConfig = _.clone(baseConfig),
|
||||||
|
row;
|
||||||
|
newConfig.chart.renderTo = $chart[0];
|
||||||
|
newConfig.series[0].data = _.map(data, function (r) {
|
||||||
|
return r.slice(0, 2);
|
||||||
|
});
|
||||||
|
hChart = new Highcharts.Chart(newConfig);
|
||||||
|
for (i = 0; i < data.length; i++) {
|
||||||
|
row = $table.find('tr').eq(i);
|
||||||
|
row
|
||||||
|
.children()
|
||||||
|
.eq(0)
|
||||||
|
.append(
|
||||||
|
$(
|
||||||
|
"<b class='seriesdot' style='background:" +
|
||||||
|
hChart.series[0].data[i].color +
|
||||||
|
"'> </b>",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$self.removeClass('loading');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
$.fn.highlightTerm = function(val) {
|
$.fn.highlightTerm = function (val) {
|
||||||
// If an item starts with `val`, wrap the matched text with boldness.
|
// If an item starts with `val`, wrap the matched text with boldness.
|
||||||
val = val.replace(/[^\w\s]/gi, '');
|
val = val.replace(/[^\w\s]/gi, '');
|
||||||
var pat = new RegExp(val, 'gi');
|
var pat = new RegExp(val, 'gi');
|
||||||
this.each(function() {
|
this.each(function () {
|
||||||
var $this = $(this),
|
var $this = $(this),
|
||||||
txt = $this.html(),
|
txt = $this.html(),
|
||||||
matchedTxt = txt.replace(pat, '<b>$&</b>');
|
matchedTxt = txt.replace(pat, '<b>$&</b>');
|
||||||
if (txt != matchedTxt) {
|
if (txt != matchedTxt) {
|
||||||
$this.html(matchedTxt);
|
$this.html(matchedTxt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -22,205 +22,232 @@ $.fn.highlightTerm = function(val) {
|
||||||
* Optional:
|
* Optional:
|
||||||
* searchType - possible values are 'AMO', 'MKT'
|
* searchType - possible values are 'AMO', 'MKT'
|
||||||
*/
|
*/
|
||||||
$.fn.searchSuggestions = function($results, processCallback, searchType) {
|
$.fn.searchSuggestions = function ($results, processCallback, searchType) {
|
||||||
var $self = this,
|
var $self = this,
|
||||||
$form = $self.closest('form');
|
$form = $self.closest('form');
|
||||||
|
|
||||||
if (!$results.length) {
|
if (!$results.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cat = $results.attr('data-cat');
|
var cat = $results.attr('data-cat');
|
||||||
|
|
||||||
if (searchType == 'AMO') {
|
if (searchType == 'AMO') {
|
||||||
// Some base elements that we don't want to keep creating on the fly.
|
// Some base elements that we don't want to keep creating on the fly.
|
||||||
var msg;
|
var msg;
|
||||||
if (cat == 'themes') {
|
if (cat == 'themes') {
|
||||||
msg = gettext('Search themes for <b>{0}</b>');
|
msg = gettext('Search themes for <b>{0}</b>');
|
||||||
} else if (cat == 'apps') {
|
} else if (cat == 'apps') {
|
||||||
msg = gettext('Search apps for <b>{0}</b>');
|
msg = gettext('Search apps for <b>{0}</b>');
|
||||||
} else {
|
|
||||||
msg = gettext('Search add-ons for <b>{0}</b>');
|
|
||||||
}
|
|
||||||
var base = template('<div class="wrap">' +
|
|
||||||
'<p><a class="sel" href="#"><span>{msg}</span></a></p><ul></ul>' +
|
|
||||||
'</div>');
|
|
||||||
$results.html(base({'msg': msg}));
|
|
||||||
} else if (searchType == 'MKT') {
|
|
||||||
$results.html('<div class="wrap"><ul></ul></div>');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Control keys that shouldn't trigger new requests.
|
|
||||||
var ignoreKeys = [
|
|
||||||
z.keys.SHIFT, z.keys.CONTROL, z.keys.ALT, z.keys.PAUSE,
|
|
||||||
z.keys.CAPS_LOCK, z.keys.ESCAPE, z.keys.ENTER,
|
|
||||||
z.keys.PAGE_UP, z.keys.PAGE_DOWN,
|
|
||||||
z.keys.LEFT, z.keys.UP, z.keys.RIGHT, z.keys.DOWN,
|
|
||||||
z.keys.HOME, z.keys.END,
|
|
||||||
z.keys.COMMAND, z.keys.WINDOWS_RIGHT, z.keys.COMMAND_RIGHT,
|
|
||||||
z.keys.WINDOWS_LEFT_OPERA, z.keys.WINDOWS_RIGHT_OPERA, z.keys.APPLE
|
|
||||||
];
|
|
||||||
|
|
||||||
var gestureKeys = [z.keys.ESCAPE, z.keys.UP, z.keys.DOWN];
|
|
||||||
|
|
||||||
function pageUp() {
|
|
||||||
// Select the first element.
|
|
||||||
$results.find('.sel').removeClass('sel');
|
|
||||||
$results.removeClass('sel');
|
|
||||||
$results.find('a:first').addClass('sel');
|
|
||||||
}
|
|
||||||
function pageDown() {
|
|
||||||
// Select the last element.
|
|
||||||
$results.find('.sel').removeClass('sel');
|
|
||||||
$results.removeClass('sel');
|
|
||||||
$results.find('a:last').addClass('sel');
|
|
||||||
}
|
|
||||||
|
|
||||||
function dismissHandler() {
|
|
||||||
$results.removeClass('visible sel');
|
|
||||||
if (searchType == 'MKT') {
|
|
||||||
$('#site-header').removeClass('suggestions');
|
|
||||||
if (z.capabilities.mobile && $('body.home').length === 0) {
|
|
||||||
z.body.removeClass('show-search');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function gestureHandler(e) {
|
|
||||||
// Bail if the results are hidden or if we have a non-gesture key
|
|
||||||
// or if we have a alt/ctrl/meta/shift keybinding.
|
|
||||||
if (!$results.hasClass('visible') ||
|
|
||||||
$.inArray(e.which, gestureKeys) < 0 ||
|
|
||||||
e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) {
|
|
||||||
$results.trigger('keyIgnored');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
if (e.which == z.keys.ESCAPE) {
|
|
||||||
dismissHandler();
|
|
||||||
} else if (e.which == z.keys.UP || e.which == z.keys.DOWN) {
|
|
||||||
var $sel = $results.find('.sel'),
|
|
||||||
$elems = $results.find('a'),
|
|
||||||
i = $elems.index($sel.get(0));
|
|
||||||
if ($sel.length && i >= 0) {
|
|
||||||
if (e.which == z.keys.UP) {
|
|
||||||
// Clamp the value so it goes to the previous row
|
|
||||||
// but never goes beyond the first row.
|
|
||||||
i = Math.max(0, i - 1);
|
|
||||||
} else {
|
|
||||||
// Clamp the value so it goes to the next row
|
|
||||||
// but never goes beyond the last row.
|
|
||||||
i = Math.min(i + 1, $elems.length - 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
$sel.removeClass('sel');
|
|
||||||
$elems.eq(i).addClass('sel');
|
|
||||||
$results.addClass('sel').trigger('selectedRowUpdate', [i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputHandler(e) {
|
|
||||||
if (e.type === 'paste') {
|
|
||||||
pasting = true;
|
|
||||||
}
|
|
||||||
var val = escape_($self.val());
|
|
||||||
if (val.length < 3) {
|
|
||||||
$results.filter('.visible').removeClass('visible');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required data to send to the callback.
|
|
||||||
var settings = {
|
|
||||||
'$results': $results,
|
|
||||||
'$form': $form,
|
|
||||||
'searchTerm': val
|
|
||||||
};
|
|
||||||
|
|
||||||
// Optional data for callback.
|
|
||||||
if (searchType == 'AMO' || searchType == 'MKT') {
|
|
||||||
settings['category'] = cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((e.type === 'keyup' && typeof e.which === 'undefined') ||
|
|
||||||
$.inArray(e.which, ignoreKeys) >= 0) && !pasting) {
|
|
||||||
$results.trigger('inputIgnored');
|
|
||||||
} else {
|
|
||||||
// XHR call and populate suggestions.
|
|
||||||
processCallback(settings);
|
|
||||||
}
|
|
||||||
pasting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pollVal = 0,
|
|
||||||
pasting = false;
|
|
||||||
|
|
||||||
if (z.capabilities.touch) {
|
|
||||||
$self.focus(function() {
|
|
||||||
// If we've already got a timer, clear it.
|
|
||||||
if (pollVal !== 0) {
|
|
||||||
clearInterval(pollVal);
|
|
||||||
}
|
|
||||||
pollVal = setInterval(function() {
|
|
||||||
gestureHandler($self);
|
|
||||||
inputHandler($self);
|
|
||||||
return;
|
|
||||||
}, 150);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
$self.on('keydown', gestureHandler)
|
msg = gettext('Search add-ons for <b>{0}</b>');
|
||||||
.on('keyup paste', _.throttle(inputHandler, 250));
|
}
|
||||||
|
var base = template(
|
||||||
|
'<div class="wrap">' +
|
||||||
|
'<p><a class="sel" href="#"><span>{msg}</span></a></p><ul></ul>' +
|
||||||
|
'</div>',
|
||||||
|
);
|
||||||
|
$results.html(base({ msg: msg }));
|
||||||
|
} else if (searchType == 'MKT') {
|
||||||
|
$results.html('<div class="wrap"><ul></ul></div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control keys that shouldn't trigger new requests.
|
||||||
|
var ignoreKeys = [
|
||||||
|
z.keys.SHIFT,
|
||||||
|
z.keys.CONTROL,
|
||||||
|
z.keys.ALT,
|
||||||
|
z.keys.PAUSE,
|
||||||
|
z.keys.CAPS_LOCK,
|
||||||
|
z.keys.ESCAPE,
|
||||||
|
z.keys.ENTER,
|
||||||
|
z.keys.PAGE_UP,
|
||||||
|
z.keys.PAGE_DOWN,
|
||||||
|
z.keys.LEFT,
|
||||||
|
z.keys.UP,
|
||||||
|
z.keys.RIGHT,
|
||||||
|
z.keys.DOWN,
|
||||||
|
z.keys.HOME,
|
||||||
|
z.keys.END,
|
||||||
|
z.keys.COMMAND,
|
||||||
|
z.keys.WINDOWS_RIGHT,
|
||||||
|
z.keys.COMMAND_RIGHT,
|
||||||
|
z.keys.WINDOWS_LEFT_OPERA,
|
||||||
|
z.keys.WINDOWS_RIGHT_OPERA,
|
||||||
|
z.keys.APPLE,
|
||||||
|
];
|
||||||
|
|
||||||
|
var gestureKeys = [z.keys.ESCAPE, z.keys.UP, z.keys.DOWN];
|
||||||
|
|
||||||
|
function pageUp() {
|
||||||
|
// Select the first element.
|
||||||
|
$results.find('.sel').removeClass('sel');
|
||||||
|
$results.removeClass('sel');
|
||||||
|
$results.find('a:first').addClass('sel');
|
||||||
|
}
|
||||||
|
function pageDown() {
|
||||||
|
// Select the last element.
|
||||||
|
$results.find('.sel').removeClass('sel');
|
||||||
|
$results.removeClass('sel');
|
||||||
|
$results.find('a:last').addClass('sel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismissHandler() {
|
||||||
|
$results.removeClass('visible sel');
|
||||||
|
if (searchType == 'MKT') {
|
||||||
|
$('#site-header').removeClass('suggestions');
|
||||||
|
if (z.capabilities.mobile && $('body.home').length === 0) {
|
||||||
|
z.body.removeClass('show-search');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gestureHandler(e) {
|
||||||
|
// Bail if the results are hidden or if we have a non-gesture key
|
||||||
|
// or if we have a alt/ctrl/meta/shift keybinding.
|
||||||
|
if (
|
||||||
|
!$results.hasClass('visible') ||
|
||||||
|
$.inArray(e.which, gestureKeys) < 0 ||
|
||||||
|
e.altKey ||
|
||||||
|
e.ctrlKey ||
|
||||||
|
e.metaKey ||
|
||||||
|
e.shiftKey
|
||||||
|
) {
|
||||||
|
$results.trigger('keyIgnored');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
if (e.which == z.keys.ESCAPE) {
|
||||||
|
dismissHandler();
|
||||||
|
} else if (e.which == z.keys.UP || e.which == z.keys.DOWN) {
|
||||||
|
var $sel = $results.find('.sel'),
|
||||||
|
$elems = $results.find('a'),
|
||||||
|
i = $elems.index($sel.get(0));
|
||||||
|
if ($sel.length && i >= 0) {
|
||||||
|
if (e.which == z.keys.UP) {
|
||||||
|
// Clamp the value so it goes to the previous row
|
||||||
|
// but never goes beyond the first row.
|
||||||
|
i = Math.max(0, i - 1);
|
||||||
|
} else {
|
||||||
|
// Clamp the value so it goes to the next row
|
||||||
|
// but never goes beyond the last row.
|
||||||
|
i = Math.min(i + 1, $elems.length - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
$sel.removeClass('sel');
|
||||||
|
$elems.eq(i).addClass('sel');
|
||||||
|
$results.addClass('sel').trigger('selectedRowUpdate', [i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inputHandler(e) {
|
||||||
|
if (e.type === 'paste') {
|
||||||
|
pasting = true;
|
||||||
|
}
|
||||||
|
var val = escape_($self.val());
|
||||||
|
if (val.length < 3) {
|
||||||
|
$results.filter('.visible').removeClass('visible');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearCurrentSuggestions(e) {
|
// Required data to send to the callback.
|
||||||
|
var settings = {
|
||||||
|
$results: $results,
|
||||||
|
$form: $form,
|
||||||
|
searchTerm: val,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Optional data for callback.
|
||||||
|
if (searchType == 'AMO' || searchType == 'MKT') {
|
||||||
|
settings['category'] = cat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
((e.type === 'keyup' && typeof e.which === 'undefined') ||
|
||||||
|
$.inArray(e.which, ignoreKeys) >= 0) &&
|
||||||
|
!pasting
|
||||||
|
) {
|
||||||
|
$results.trigger('inputIgnored');
|
||||||
|
} else {
|
||||||
|
// XHR call and populate suggestions.
|
||||||
|
processCallback(settings);
|
||||||
|
}
|
||||||
|
pasting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollVal = 0,
|
||||||
|
pasting = false;
|
||||||
|
|
||||||
|
if (z.capabilities.touch) {
|
||||||
|
$self.focus(function () {
|
||||||
|
// If we've already got a timer, clear it.
|
||||||
|
if (pollVal !== 0) {
|
||||||
clearInterval(pollVal);
|
clearInterval(pollVal);
|
||||||
// Delay dismissal to allow for click events to happen on
|
}
|
||||||
// results. If we call it immediately, results get hidden
|
pollVal = setInterval(function () {
|
||||||
// before the click events can happen.
|
gestureHandler($self);
|
||||||
_.delay(dismissHandler, 250);
|
inputHandler($self);
|
||||||
$self.trigger('dismissed');
|
return;
|
||||||
|
}, 150);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$self
|
||||||
|
.on('keydown', gestureHandler)
|
||||||
|
.on('keyup paste', _.throttle(inputHandler, 250));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearCurrentSuggestions(e) {
|
||||||
|
clearInterval(pollVal);
|
||||||
|
// Delay dismissal to allow for click events to happen on
|
||||||
|
// results. If we call it immediately, results get hidden
|
||||||
|
// before the click events can happen.
|
||||||
|
_.delay(dismissHandler, 250);
|
||||||
|
$self.trigger('dismissed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$self.blur(clearCurrentSuggestions);
|
||||||
|
$form.submit(function (e) {
|
||||||
|
$self.blur();
|
||||||
|
clearCurrentSuggestions(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
$results.find('.sel').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$form.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
$results
|
||||||
|
.on('mouseenter mouseleave', 'li, p', function () {
|
||||||
|
$results.find('.sel').removeClass('sel');
|
||||||
|
$results.addClass('sel');
|
||||||
|
$(this).find('a').addClass('sel');
|
||||||
|
})
|
||||||
|
.on('click', 'a', function () {
|
||||||
|
clearCurrentSuggestions();
|
||||||
|
$self.val('');
|
||||||
|
});
|
||||||
|
|
||||||
|
$results.on('highlight', function (e, val) {
|
||||||
|
// If an item starts with `val`, wrap the matched text with boldness.
|
||||||
|
$results.find('ul a span').highlightTerm(val);
|
||||||
|
$results.addClass('visible');
|
||||||
|
if (!$results.find('.sel').length) {
|
||||||
|
pageUp();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$self.blur(clearCurrentSuggestions);
|
$results.on('dismiss', clearCurrentSuggestions);
|
||||||
$form.submit(function(e) {
|
|
||||||
$self.blur();
|
|
||||||
clearCurrentSuggestions(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
$results.find('.sel').click(function(e) {
|
$(document).keyup(function (e) {
|
||||||
e.preventDefault();
|
if (fieldFocused(e)) {
|
||||||
$form.submit();
|
return;
|
||||||
});
|
}
|
||||||
|
if (e.which == 83) {
|
||||||
|
$self.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$results.on('mouseenter mouseleave', 'li, p', function() {
|
return this;
|
||||||
$results.find('.sel').removeClass('sel');
|
|
||||||
$results.addClass('sel');
|
|
||||||
$(this).find('a').addClass('sel');
|
|
||||||
}).on('click', 'a', function() {
|
|
||||||
clearCurrentSuggestions();
|
|
||||||
$self.val('');
|
|
||||||
});
|
|
||||||
|
|
||||||
$results.on('highlight', function(e, val) {
|
|
||||||
// If an item starts with `val`, wrap the matched text with boldness.
|
|
||||||
$results.find('ul a span').highlightTerm(val);
|
|
||||||
$results.addClass('visible');
|
|
||||||
if (!$results.find('.sel').length) {
|
|
||||||
pageUp();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$results.on('dismiss', clearCurrentSuggestions);
|
|
||||||
|
|
||||||
$(document).keyup(function(e) {
|
|
||||||
if (fieldFocused(e)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e.which == 83) {
|
|
||||||
$self.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,69 +1,82 @@
|
||||||
$(function() {
|
$(function () {
|
||||||
// When I click on the avatar, append `#id=<id>` to the URL.
|
// When I click on the avatar, append `#id=<id>` to the URL.
|
||||||
$('.user-avatar img').click(_pd(function(e) {
|
$('.user-avatar img').click(
|
||||||
window.location.hash = 'id=' + $('.user-avatar').data('user-id');
|
_pd(function (e) {
|
||||||
e.stopPropagation();
|
window.location.hash = 'id=' + $('.user-avatar').data('user-id');
|
||||||
}));
|
e.stopPropagation();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
$('#report-user-modal').modal('#report-user-abuse', {delegate: '#page'});
|
$('#report-user-modal').modal('#report-user-abuse', { delegate: '#page' });
|
||||||
|
|
||||||
if($('#user_edit').exists()) {
|
if ($('#user_edit').exists()) {
|
||||||
$('.more-all, .more-none').click(_pd(function() {
|
$('.more-all, .more-none').click(
|
||||||
var $this = $(this);
|
_pd(function () {
|
||||||
$this.closest('li').find('input:not([disabled])').prop('checked', $this.hasClass('more-all'));
|
var $this = $(this);
|
||||||
}));
|
$this
|
||||||
|
.closest('li')
|
||||||
|
.find('input:not([disabled])')
|
||||||
|
.prop('checked', $this.hasClass('more-all'));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide change password box
|
||||||
|
$('#acct-password').hide();
|
||||||
|
$('#change-acct-password').click(
|
||||||
|
_pd(function () {
|
||||||
|
$('#acct-password').fadeIn();
|
||||||
|
$('#id_oldpassword').focus();
|
||||||
|
$(this).closest('li').hide();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show password box if there's an error in it.
|
||||||
|
$('#acct-password .errorlist li').exists(function () {
|
||||||
|
$('#acct-password').show();
|
||||||
|
$('#change-acct-password').closest('li').hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Display image inline
|
||||||
|
var $avatar = $('.profile-photo .avatar'),
|
||||||
|
$a = $('<a>', {
|
||||||
|
text: gettext('Use original'),
|
||||||
|
class: 'use-original delete',
|
||||||
|
href: '#',
|
||||||
|
}).hide();
|
||||||
|
|
||||||
|
$avatar.attr('data-original', $avatar.attr('src'));
|
||||||
|
function use_original() {
|
||||||
|
$('.use-original').hide();
|
||||||
|
$('#id_photo').val('');
|
||||||
|
$avatar.attr('src', $avatar.attr('data-original'));
|
||||||
|
}
|
||||||
|
$a.click(_pd(use_original));
|
||||||
|
|
||||||
|
$avatar.closest('li').append($a);
|
||||||
|
$('#id_photo').change(function () {
|
||||||
|
var $li = $(this).closest('li'),
|
||||||
|
file = $(this)[0].files[0],
|
||||||
|
file_name = file.name || file.fileName;
|
||||||
|
$li.find('.errorlist').remove();
|
||||||
|
if (!file_name.match(/\.(jpg|png|jpeg)$/i)) {
|
||||||
|
$ul = $('<ul>', { class: 'errorlist' });
|
||||||
|
$ul.append(
|
||||||
|
$('<li>', { text: gettext('Images must be either PNG or JPG.') }),
|
||||||
|
);
|
||||||
|
$li.append($ul);
|
||||||
|
use_original();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
var img = $(this).objectUrl();
|
||||||
// Hide change password box
|
if (img) {
|
||||||
$('#acct-password').hide();
|
$a.css('display', 'block');
|
||||||
$('#change-acct-password').click(_pd(function() {
|
$avatar.attr('src', img);
|
||||||
$('#acct-password').fadeIn();
|
|
||||||
$('#id_oldpassword').focus();
|
|
||||||
$(this).closest('li').hide();
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Show password box if there's an error in it.
|
|
||||||
$('#acct-password .errorlist li').exists(function() {
|
|
||||||
$('#acct-password').show();
|
|
||||||
$('#change-acct-password').closest('li').hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Display image inline
|
|
||||||
var $avatar = $('.profile-photo .avatar'),
|
|
||||||
$a = $('<a>', {'text': gettext('Use original'), 'class': 'use-original delete', 'href': '#'}).hide();
|
|
||||||
|
|
||||||
$avatar.attr('data-original', $avatar.attr('src'));
|
|
||||||
function use_original() {
|
|
||||||
$('.use-original').hide();
|
|
||||||
$('#id_photo').val("");
|
|
||||||
$avatar.attr('src', $avatar.attr('data-original'));
|
|
||||||
}
|
}
|
||||||
$a.click(_pd(use_original));
|
});
|
||||||
|
|
||||||
$avatar.closest('li').append($a);
|
|
||||||
$('#id_photo').change(function() {
|
|
||||||
var $li = $(this).closest('li'),
|
|
||||||
file = $(this)[0].files[0],
|
|
||||||
file_name = file.name || file.fileName;
|
|
||||||
$li.find('.errorlist').remove();
|
|
||||||
if(!file_name.match(/\.(jpg|png|jpeg)$/i)) {
|
|
||||||
$ul = $('<ul>', {'class': 'errorlist'});
|
|
||||||
$ul.append($('<li>', {'text': gettext('Images must be either PNG or JPG.')}));
|
|
||||||
$li.append($ul);
|
|
||||||
use_original();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var img = $(this).objectUrl();
|
|
||||||
if(img) {
|
|
||||||
$a.css('display', 'block');
|
|
||||||
$avatar.attr('src', img);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Hijack "Admin / Editor Log in" context menuitem.
|
// Hijack "Admin / Editor Log in" context menuitem.
|
||||||
$('#admin-login').click(function() {
|
$('#admin-login').click(function () {
|
||||||
window.location = $(this).attr('data-url');
|
window.location = $(this).attr('data-url');
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,112 +2,119 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
;(function() {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// !! this file assumes only one signup form per page !!
|
// !! this file assumes only one signup form per page !!
|
||||||
|
|
||||||
var newsletterForm = document.getElementById('newsletter_form');
|
var newsletterForm = document.getElementById('newsletter_form');
|
||||||
var newsletterWrapper = document.getElementById('newsletter_wrap');
|
var newsletterWrapper = document.getElementById('newsletter_wrap');
|
||||||
|
|
||||||
// handle errors
|
// handle errors
|
||||||
var errorArray = [];
|
var errorArray = [];
|
||||||
var newsletterErrors = document.getElementById('newsletter_errors');
|
var newsletterErrors = document.getElementById('newsletter_errors');
|
||||||
function newsletterError(e) {
|
function newsletterError(e) {
|
||||||
var errorList = document.createElement('ul');
|
var errorList = document.createElement('ul');
|
||||||
|
|
||||||
if(errorArray.length) {
|
if (errorArray.length) {
|
||||||
for (var i = 0; i < errorArray.length; i++) {
|
for (var i = 0; i < errorArray.length; i++) {
|
||||||
var item = document.createElement('li');
|
var item = document.createElement('li');
|
||||||
item.appendChild(document.createTextNode(errorArray[i]));
|
item.appendChild(document.createTextNode(errorArray[i]));
|
||||||
errorList.appendChild(item);
|
errorList.appendChild(item);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// no error messages, forward to server for better troubleshooting
|
||||||
|
newsletterForm.setAttribute('data-skip-xhr', true);
|
||||||
|
newsletterForm.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
newsletterErrors.appendChild(errorList);
|
||||||
|
newsletterErrors.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// show sucess message
|
||||||
|
function newsletterThanks() {
|
||||||
|
var thanks = document.getElementById('newsletter_thanks');
|
||||||
|
|
||||||
|
// show thanks message
|
||||||
|
thanks.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// XHR subscribe; handle errors; display thanks message on success.
|
||||||
|
function newsletterSubscribe(evt) {
|
||||||
|
var skipXHR = newsletterForm.getAttribute('data-skip-xhr');
|
||||||
|
if (skipXHR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
evt.preventDefault();
|
||||||
|
evt.stopPropagation();
|
||||||
|
|
||||||
|
// new submission, clear old errors
|
||||||
|
errorArray = [];
|
||||||
|
newsletterErrors.style.display = 'none';
|
||||||
|
while (newsletterErrors.firstChild) {
|
||||||
|
newsletterErrors.removeChild(newsletterErrors.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fmt = document.getElementById('fmt').value;
|
||||||
|
var email = document.getElementById('email').value;
|
||||||
|
var newsletter = document.getElementById('newsletters').value;
|
||||||
|
var privacy = document.querySelector('input[name="privacy"]:checked')
|
||||||
|
? '&privacy=true'
|
||||||
|
: '';
|
||||||
|
var params =
|
||||||
|
'email=' +
|
||||||
|
encodeURIComponent(email) +
|
||||||
|
'&newsletters=' +
|
||||||
|
newsletter +
|
||||||
|
privacy +
|
||||||
|
'&fmt=' +
|
||||||
|
fmt +
|
||||||
|
'&source_url=' +
|
||||||
|
encodeURIComponent(document.location.href);
|
||||||
|
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
|
xhr.onload = function (r) {
|
||||||
|
if (r.target.status >= 200 && r.target.status < 300) {
|
||||||
|
// response is null if handled by service worker
|
||||||
|
if (response === null) {
|
||||||
|
newsletterError(new Error());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var response = r.target.response;
|
||||||
|
if (response.success === true) {
|
||||||
|
newsletterForm.style.display = 'none';
|
||||||
|
newsletterThanks();
|
||||||
} else {
|
} else {
|
||||||
// no error messages, forward to server for better troubleshooting
|
if (response.errors) {
|
||||||
newsletterForm.setAttribute('data-skip-xhr', true);
|
for (var i = 0; i < response.errors.length; i++) {
|
||||||
newsletterForm.submit();
|
errorArray.push(response.errors[i]);
|
||||||
}
|
|
||||||
|
|
||||||
newsletterErrors.appendChild(errorList);
|
|
||||||
newsletterErrors.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
// show sucess message
|
|
||||||
function newsletterThanks() {
|
|
||||||
var thanks = document.getElementById('newsletter_thanks');
|
|
||||||
|
|
||||||
// show thanks message
|
|
||||||
thanks.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
// XHR subscribe; handle errors; display thanks message on success.
|
|
||||||
function newsletterSubscribe(evt) {
|
|
||||||
var skipXHR = newsletterForm.getAttribute('data-skip-xhr');
|
|
||||||
if (skipXHR) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
evt.preventDefault();
|
|
||||||
evt.stopPropagation();
|
|
||||||
|
|
||||||
// new submission, clear old errors
|
|
||||||
errorArray = [];
|
|
||||||
newsletterErrors.style.display = 'none';
|
|
||||||
while (newsletterErrors.firstChild) {
|
|
||||||
newsletterErrors.removeChild(newsletterErrors.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fmt = document.getElementById('fmt').value;
|
|
||||||
var email = document.getElementById('email').value;
|
|
||||||
var newsletter = document.getElementById('newsletters').value;
|
|
||||||
var privacy = document.querySelector('input[name="privacy"]:checked') ? '&privacy=true' : '';
|
|
||||||
var params = 'email=' + encodeURIComponent(email) +
|
|
||||||
'&newsletters=' + newsletter +
|
|
||||||
privacy +
|
|
||||||
'&fmt=' + fmt +
|
|
||||||
'&source_url=' + encodeURIComponent(document.location.href);
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
xhr.onload = function(r) {
|
|
||||||
if (r.target.status >= 200 && r.target.status < 300) {
|
|
||||||
// response is null if handled by service worker
|
|
||||||
if(response === null) {
|
|
||||||
newsletterError(new Error());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var response = r.target.response;
|
|
||||||
if (response.success === true) {
|
|
||||||
newsletterForm.style.display = 'none';
|
|
||||||
newsletterThanks();
|
|
||||||
} else {
|
|
||||||
if(response.errors) {
|
|
||||||
for (var i = 0; i < response.errors.length; i++) {
|
|
||||||
errorArray.push(response.errors[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newsletterError(new Error());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newsletterError(new Error());
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
newsletterError(new Error());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newsletterError(new Error());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
xhr.onerror = function(e) {
|
xhr.onerror = function (e) {
|
||||||
newsletterError(e);
|
newsletterError(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
var url = newsletterForm.getAttribute('action');
|
var url = newsletterForm.getAttribute('action');
|
||||||
|
|
||||||
xhr.open('POST', url, true);
|
xhr.open('POST', url, true);
|
||||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
|
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
xhr.timeout = 5000;
|
xhr.timeout = 5000;
|
||||||
xhr.ontimeout = newsletterError;
|
xhr.ontimeout = newsletterError;
|
||||||
xhr.responseType = 'json';
|
xhr.responseType = 'json';
|
||||||
xhr.send(params);
|
xhr.send(params);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
newsletterForm.addEventListener('submit', newsletterSubscribe, false);
|
newsletterForm.addEventListener('submit', newsletterSubscribe, false);
|
||||||
})();
|
})();
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -6,21 +6,25 @@
|
||||||
* >>> format('{x}', {x: 1})
|
* >>> format('{x}', {x: 1})
|
||||||
* "1"
|
* "1"
|
||||||
*/
|
*/
|
||||||
var format = (function() {
|
var format = (function () {
|
||||||
var re = /\{([^}]+)\}/g;
|
var re = /\{([^}]+)\}/g;
|
||||||
return function(s, args) {
|
return function (s, args) {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
throw "Format string is empty!";
|
throw 'Format string is empty!';
|
||||||
}
|
}
|
||||||
if (!args) return;
|
if (!args) return;
|
||||||
if (!(args instanceof Array || args instanceof Object))
|
if (!(args instanceof Array || args instanceof Object))
|
||||||
args = Array.prototype.slice.call(arguments, 1);
|
args = Array.prototype.slice.call(arguments, 1);
|
||||||
return s.replace(re, function(_, match){ return args[match]; });
|
return s.replace(re, function (_, match) {
|
||||||
};
|
return args[match];
|
||||||
|
});
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
function template(s) {
|
function template(s) {
|
||||||
if (!s) {
|
if (!s) {
|
||||||
throw "Template string is empty!";
|
throw 'Template string is empty!';
|
||||||
}
|
}
|
||||||
return function(args) { return format(s, args); };
|
return function (args) {
|
||||||
|
return format(s, args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,106 +1,120 @@
|
||||||
/**
|
/**
|
||||||
* hoverIntent is similar to jQuery's built-in "hover" function except that
|
* hoverIntent is similar to jQuery's built-in "hover" function except that
|
||||||
* instead of firing the onMouseOver event immediately, hoverIntent checks
|
* instead of firing the onMouseOver event immediately, hoverIntent checks
|
||||||
* to see if the user's mouse has slowed down (beneath the sensitivity
|
* to see if the user's mouse has slowed down (beneath the sensitivity
|
||||||
* threshold) before firing the onMouseOver event.
|
* threshold) before firing the onMouseOver event.
|
||||||
*
|
*
|
||||||
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
|
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
|
||||||
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
|
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
|
||||||
*
|
*
|
||||||
* hoverIntent is currently available for use in all personal or commercial
|
* hoverIntent is currently available for use in all personal or commercial
|
||||||
* projects under both MIT and GPL licenses. This means that you can choose
|
* projects under both MIT and GPL licenses. This means that you can choose
|
||||||
* the license that best suits your project, and use it accordingly.
|
* the license that best suits your project, and use it accordingly.
|
||||||
*
|
*
|
||||||
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
|
* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions
|
||||||
* $("ul li").hoverIntent( showNav , hideNav );
|
* $("ul li").hoverIntent( showNav , hideNav );
|
||||||
*
|
*
|
||||||
* // advanced usage receives configuration object only
|
* // advanced usage receives configuration object only
|
||||||
* $("ul li").hoverIntent({
|
* $("ul li").hoverIntent({
|
||||||
* sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
|
* sensitivity: 7, // number = sensitivity threshold (must be 1 or higher)
|
||||||
* interval: 100, // number = milliseconds of polling interval
|
* interval: 100, // number = milliseconds of polling interval
|
||||||
* over: showNav, // function = onMouseOver callback (required)
|
* over: showNav, // function = onMouseOver callback (required)
|
||||||
* timeout: 0, // number = milliseconds delay before onMouseOut function call
|
* timeout: 0, // number = milliseconds delay before onMouseOut function call
|
||||||
* out: hideNav // function = onMouseOut callback (required)
|
* out: hideNav // function = onMouseOut callback (required)
|
||||||
* });
|
* });
|
||||||
*
|
*
|
||||||
* @param f onMouseOver function || An object with configuration options
|
* @param f onMouseOver function || An object with configuration options
|
||||||
* @param g onMouseOut function || Nothing (use configuration options object)
|
* @param g onMouseOut function || Nothing (use configuration options object)
|
||||||
* @author Brian Cherne brian(at)cherne(dot)net
|
* @author Brian Cherne brian(at)cherne(dot)net
|
||||||
*/
|
*/
|
||||||
(function($) {
|
(function ($) {
|
||||||
$.fn.hoverIntent = function(f,g) {
|
$.fn.hoverIntent = function (f, g) {
|
||||||
// default configuration options
|
// default configuration options
|
||||||
var cfg = {
|
var cfg = {
|
||||||
sensitivity: 7,
|
sensitivity: 7,
|
||||||
interval: 100,
|
interval: 100,
|
||||||
timeout: 0
|
timeout: 0,
|
||||||
};
|
};
|
||||||
// override configuration options with user supplied object
|
// override configuration options with user supplied object
|
||||||
cfg = $.extend(cfg, g ? { over: f, out: g } : f );
|
cfg = $.extend(cfg, g ? { over: f, out: g } : f);
|
||||||
|
|
||||||
// instantiate variables
|
// instantiate variables
|
||||||
// cX, cY = current X and Y position of mouse, updated by mousemove event
|
// cX, cY = current X and Y position of mouse, updated by mousemove event
|
||||||
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
|
// pX, pY = previous X and Y position of mouse, set by mouseover and polling interval
|
||||||
var cX, cY, pX, pY;
|
var cX, cY, pX, pY;
|
||||||
|
|
||||||
// A private function for getting mouse position
|
// A private function for getting mouse position
|
||||||
var track = function(ev) {
|
var track = function (ev) {
|
||||||
cX = ev.pageX;
|
cX = ev.pageX;
|
||||||
cY = ev.pageY;
|
cY = ev.pageY;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A private function for comparing current and previous mouse position
|
// A private function for comparing current and previous mouse position
|
||||||
var compare = function(ev,ob) {
|
var compare = function (ev, ob) {
|
||||||
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
||||||
// compare mouse positions to see if they've crossed the threshold
|
// compare mouse positions to see if they've crossed the threshold
|
||||||
if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) {
|
if (Math.abs(pX - cX) + Math.abs(pY - cY) < cfg.sensitivity) {
|
||||||
$(ob).off("mousemove",track);
|
$(ob).off('mousemove', track);
|
||||||
// set hoverIntent state to true (so mouseOut can be called)
|
// set hoverIntent state to true (so mouseOut can be called)
|
||||||
ob.hoverIntent_s = 1;
|
ob.hoverIntent_s = 1;
|
||||||
return cfg.over.apply(ob,[ev]);
|
return cfg.over.apply(ob, [ev]);
|
||||||
} else {
|
} else {
|
||||||
// set previous coordinates for next time
|
// set previous coordinates for next time
|
||||||
pX = cX; pY = cY;
|
pX = cX;
|
||||||
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
|
pY = cY;
|
||||||
ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval );
|
// use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs)
|
||||||
}
|
ob.hoverIntent_t = setTimeout(function () {
|
||||||
};
|
compare(ev, ob);
|
||||||
|
}, cfg.interval);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A private function for delaying the mouseOut function
|
// A private function for delaying the mouseOut function
|
||||||
var delay = function(ev,ob) {
|
var delay = function (ev, ob) {
|
||||||
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
||||||
ob.hoverIntent_s = 0;
|
ob.hoverIntent_s = 0;
|
||||||
return cfg.out.apply(ob,[ev]);
|
return cfg.out.apply(ob, [ev]);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A private function for handling mouse 'hovering'
|
// A private function for handling mouse 'hovering'
|
||||||
var handleHover = function(e) {
|
var handleHover = function (e) {
|
||||||
// copy objects to be passed into t (required for event object to be passed in IE)
|
// copy objects to be passed into t (required for event object to be passed in IE)
|
||||||
var ev = jQuery.extend({},e);
|
var ev = jQuery.extend({}, e);
|
||||||
var ob = this;
|
var ob = this;
|
||||||
|
|
||||||
// cancel hoverIntent timer if it exists
|
// cancel hoverIntent timer if it exists
|
||||||
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
|
if (ob.hoverIntent_t) {
|
||||||
|
ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t);
|
||||||
|
}
|
||||||
|
|
||||||
// if e.type == "mouseenter"
|
// if e.type == "mouseenter"
|
||||||
if (e.type == "mouseenter") {
|
if (e.type == 'mouseenter') {
|
||||||
// set "previous" X and Y position based on initial entry point
|
// set "previous" X and Y position based on initial entry point
|
||||||
pX = ev.pageX; pY = ev.pageY;
|
pX = ev.pageX;
|
||||||
// update "current" X and Y position based on mousemove
|
pY = ev.pageY;
|
||||||
$(ob).on("mousemove",track);
|
// update "current" X and Y position based on mousemove
|
||||||
// start polling interval (self-calling timeout) to compare mouse coordinates over time
|
$(ob).on('mousemove', track);
|
||||||
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
|
// start polling interval (self-calling timeout) to compare mouse coordinates over time
|
||||||
|
if (ob.hoverIntent_s != 1) {
|
||||||
|
ob.hoverIntent_t = setTimeout(function () {
|
||||||
|
compare(ev, ob);
|
||||||
|
}, cfg.interval);
|
||||||
|
}
|
||||||
|
|
||||||
// else e.type == "mouseleave"
|
// else e.type == "mouseleave"
|
||||||
} else {
|
} else {
|
||||||
// unbind expensive mousemove event
|
// unbind expensive mousemove event
|
||||||
$(ob).off("mousemove",track);
|
$(ob).off('mousemove', track);
|
||||||
// if hoverIntent state is true, then call the mouseOut function after the specified delay
|
// if hoverIntent state is true, then call the mouseOut function after the specified delay
|
||||||
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
|
if (ob.hoverIntent_s == 1) {
|
||||||
}
|
ob.hoverIntent_t = setTimeout(function () {
|
||||||
};
|
delay(ev, ob);
|
||||||
|
}, cfg.timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// bind the function to the two event listeners
|
// bind the function to the two event listeners
|
||||||
return this.on('mouseenter', handleHover).on('mouseleave', handleHover);
|
return this.on('mouseenter', handleHover).on('mouseleave', handleHover);
|
||||||
};
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -1,111 +1,116 @@
|
||||||
/* jQuery Zoombox: https://github.com/technicolorenvy/jquery-zoombox */
|
/* jQuery Zoombox: https://github.com/technicolorenvy/jquery-zoombox */
|
||||||
(function($) {
|
(function ($) {
|
||||||
$.fn.zoomBox = function(opts) {
|
$.fn.zoomBox = function (opts) {
|
||||||
var box, boxX, boxY, boxW, boxH;
|
var box, boxX, boxY, boxW, boxH;
|
||||||
var img, imgW, imgH;
|
var img, imgW, imgH;
|
||||||
|
|
||||||
opts = $.extend({
|
opts = $.extend(
|
||||||
interval: 400,
|
{
|
||||||
sensitivity: 100000,
|
interval: 400,
|
||||||
zoomSpeed: 200,
|
sensitivity: 100000,
|
||||||
zoomMargin: 10
|
zoomSpeed: 200,
|
||||||
}, opts || { });
|
zoomMargin: 10,
|
||||||
|
},
|
||||||
|
opts || {},
|
||||||
|
);
|
||||||
|
|
||||||
this.each(function() {
|
this.each(function () {
|
||||||
initMetrics(this);
|
initMetrics(this);
|
||||||
$(img).css( imageCenter() );
|
$(img).css(imageCenter());
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.hoverIntent( $.extend({
|
return this.hoverIntent(
|
||||||
over: onMouseEnter,
|
$.extend(
|
||||||
out: onMouseLeave
|
{
|
||||||
}, opts));
|
over: onMouseEnter,
|
||||||
|
out: onMouseLeave,
|
||||||
|
},
|
||||||
|
opts,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
function initMetrics(elem) {
|
||||||
|
var jimg = $('img', elem);
|
||||||
|
jimg.data('origWidth', jimg.attr('width'));
|
||||||
|
jimg.data('origHeight', jimg.attr('height'));
|
||||||
|
updateMetrics(elem);
|
||||||
|
}
|
||||||
|
|
||||||
function initMetrics(elem)
|
function updateMetrics(elem) {
|
||||||
{
|
var jbox = $(elem);
|
||||||
var jimg = $('img', elem);
|
var jimg = $('img', elem);
|
||||||
jimg.data('origWidth', jimg.attr('width'));
|
|
||||||
jimg.data('origHeight', jimg.attr('height'));
|
|
||||||
updateMetrics(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateMetrics(elem)
|
box = elem;
|
||||||
{
|
boxX = jbox.offset().left;
|
||||||
var jbox = $(elem);
|
boxY = jbox.offset().top;
|
||||||
var jimg = $('img', elem);
|
boxW = jbox.width();
|
||||||
|
boxH = jbox.height();
|
||||||
|
|
||||||
box = elem;
|
img = jimg.get(0);
|
||||||
boxX = jbox.offset().left;
|
imgW = jimg.data('origWidth');
|
||||||
boxY = jbox.offset().top;
|
imgH = jimg.data('origHeight');
|
||||||
boxW = jbox.width();
|
}
|
||||||
boxH = jbox.height();
|
|
||||||
|
|
||||||
img = jimg.get(0);
|
function onMouseEnter(e) {
|
||||||
imgW = jimg.data('origWidth');
|
updateMetrics(e.currentTarget);
|
||||||
imgH = jimg.data('origHeight');
|
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseEnter(e)
|
$(img).stop(false, true);
|
||||||
{
|
|
||||||
updateMetrics(e.currentTarget);
|
|
||||||
|
|
||||||
$(img).stop(false, true);
|
$(img).animate(
|
||||||
|
imageZoom(e.pageX, e.pageY),
|
||||||
|
opts.zoomSpeed,
|
||||||
|
'linear',
|
||||||
|
function () {
|
||||||
|
$(box).on('mousemove', onMouseMove);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$(img).animate(imageZoom(e.pageX, e.pageY), opts.zoomSpeed, 'linear', function() {
|
function onMouseMove(e) {
|
||||||
$(box).on('mousemove', onMouseMove);
|
$(img).css(imageZoom(e.pageX, e.pageY));
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function onMouseMove(e)
|
function onMouseLeave(e) {
|
||||||
{
|
$(img).stop(false, true);
|
||||||
$(img).css( imageZoom(e.pageX, e.pageY) );
|
$(box).off('mousemove', onMouseMove);
|
||||||
}
|
$(img).animate(imageCenter(), opts.zoomSpeed, 'linear');
|
||||||
|
box = img = null;
|
||||||
|
}
|
||||||
|
|
||||||
function onMouseLeave(e)
|
function imageCenter() {
|
||||||
{
|
var sx = (boxW - 2 * opts.zoomMargin) / imgW;
|
||||||
$(img).stop(false, true);
|
var sy = (boxH - 2 * opts.zoomMargin) / imgH;
|
||||||
$(box).off('mousemove', onMouseMove);
|
var scale = Math.min(sx, sy);
|
||||||
$(img).animate(imageCenter(), opts.zoomSpeed, 'linear');
|
|
||||||
box = img = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function imageCenter()
|
return {
|
||||||
{
|
/* line below patched to always pin images to the top right -
|
||||||
var sx = (boxW - 2*opts.zoomMargin) / imgW;
|
|
||||||
var sy = (boxH - 2*opts.zoomMargin) / imgH;
|
|
||||||
var scale = Math.min(sx, sy);
|
|
||||||
|
|
||||||
return {
|
|
||||||
/* line below patched to always pin images to the top right -
|
|
||||||
helps when images don't fill the div width for static themes.*/
|
helps when images don't fill the div width for static themes.*/
|
||||||
'left': (boxW - scale*imgW) - opts.zoomMargin,
|
left: boxW - scale * imgW - opts.zoomMargin,
|
||||||
'top': (boxH - scale*imgH) / 2,
|
top: (boxH - scale * imgH) / 2,
|
||||||
'width': scale*imgW,
|
width: scale * imgW,
|
||||||
'height': scale*imgH
|
height: scale * imgH,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function imageZoom(x, y)
|
function imageZoom(x, y) {
|
||||||
{
|
x = x - boxX;
|
||||||
x = x - boxX;
|
y = y - boxY;
|
||||||
y = y - boxY;
|
|
||||||
|
|
||||||
var sx = boxW / imgW;
|
var sx = boxW / imgW;
|
||||||
var sy = boxH / imgH;
|
var sy = boxH / imgH;
|
||||||
|
|
||||||
var x2 = boxW/2 - x * imgW / boxW;
|
var x2 = boxW / 2 - (x * imgW) / boxW;
|
||||||
var y2 = boxH/2 - y * imgH / boxH;
|
var y2 = boxH / 2 - (y * imgH) / boxH;
|
||||||
|
|
||||||
x2 = Math.max( Math.min(x2, 0), boxW-imgW );
|
x2 = Math.max(Math.min(x2, 0), boxW - imgW);
|
||||||
y2 = Math.max( Math.min(y2, 0), boxH-imgH );
|
y2 = Math.max(Math.min(y2, 0), boxH - imgH);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'left': x2,
|
left: x2,
|
||||||
'top': y2,
|
top: y2,
|
||||||
'width': imgW,
|
width: imgW,
|
||||||
'height': imgH
|
height: imgH,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -2,17 +2,23 @@
|
||||||
// to the provided string if a translation is missing.
|
// to the provided string if a translation is missing.
|
||||||
// TODO: Remove this once we upgrade to a version of Django that contains
|
// TODO: Remove this once we upgrade to a version of Django that contains
|
||||||
// the fix for https://code.djangoproject.com/ticket/27418
|
// the fix for https://code.djangoproject.com/ticket/27418
|
||||||
if (window.ngettext && django && django.ngettext && django.catalog && django.pluralidx) {
|
if (
|
||||||
|
window.ngettext &&
|
||||||
|
django &&
|
||||||
|
django.ngettext &&
|
||||||
|
django.catalog &&
|
||||||
|
django.pluralidx
|
||||||
|
) {
|
||||||
django.ngettext = function (singular, plural, count) {
|
django.ngettext = function (singular, plural, count) {
|
||||||
var value = django.catalog[singular];
|
var value = django.catalog[singular];
|
||||||
var translation;
|
var translation;
|
||||||
if (typeof(value) !== 'undefined') {
|
if (typeof value !== 'undefined') {
|
||||||
translation = value[django.pluralidx(count)];
|
translation = value[django.pluralidx(count)];
|
||||||
if (translation && translation.length > 0) {
|
if (translation && translation.length > 0) {
|
||||||
return translation;
|
return translation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (count == 1) ? singular : plural;
|
return count == 1 ? singular : plural;
|
||||||
};
|
};
|
||||||
window.ngettext = django.ngettext;
|
window.ngettext = django.ngettext;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
; /* This is just to fix jquery.pjax.js breaking our hokey concatenation and
|
/* This is just to fix jquery.pjax.js breaking our hokey concatenation and
|
||||||
* minification process.
|
* minification process.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,78 +14,85 @@
|
||||||
* @license
|
* @license
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
;(function()
|
(function () {
|
||||||
{
|
// CommonJS
|
||||||
// CommonJS
|
SyntaxHighlighter =
|
||||||
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
|
SyntaxHighlighter ||
|
||||||
|
(typeof require !== 'undefined'
|
||||||
|
? require('shCore').SyntaxHighlighter
|
||||||
|
: null);
|
||||||
|
|
||||||
function Brush()
|
function Brush() {
|
||||||
{
|
function getKeywordsCSS(str) {
|
||||||
function getKeywordsCSS(str)
|
return (
|
||||||
{
|
'\\b([a-z_]|)' +
|
||||||
return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b';
|
str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') +
|
||||||
};
|
'(?=:)\\b'
|
||||||
|
);
|
||||||
function getValuesCSS(str)
|
}
|
||||||
{
|
|
||||||
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
|
|
||||||
};
|
|
||||||
|
|
||||||
var keywords = 'ascent azimuth background-attachment background-color background-image background-position ' +
|
function getValuesCSS(str) {
|
||||||
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
|
return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + ':\\b';
|
||||||
'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
|
}
|
||||||
'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
|
|
||||||
'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
|
|
||||||
'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
|
|
||||||
'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
|
|
||||||
'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
|
|
||||||
'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
|
|
||||||
'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
|
|
||||||
'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
|
|
||||||
'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
|
|
||||||
'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
|
|
||||||
'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
|
|
||||||
|
|
||||||
var values = 'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
|
var keywords =
|
||||||
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
|
'ascent azimuth background-attachment background-color background-image background-position ' +
|
||||||
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+
|
'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
|
||||||
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
|
'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
|
||||||
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
|
'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
|
||||||
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
|
'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
|
||||||
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
|
'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
|
||||||
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
|
'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
|
||||||
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
|
'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
|
||||||
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
|
'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
|
||||||
'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+
|
'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
|
||||||
'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+
|
'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
|
||||||
'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+
|
'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
|
||||||
'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
|
'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
|
||||||
|
'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
|
||||||
|
|
||||||
var fonts = '[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
|
var values =
|
||||||
|
'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder ' +
|
||||||
this.regexList = [
|
'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed ' +
|
||||||
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments
|
'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double ' +
|
||||||
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings
|
'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia ' +
|
||||||
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings
|
'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic ' +
|
||||||
{ regex: /\#[a-fA-F0-9]{3,6}/g, css: 'value' }, // html colors
|
'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha ' +
|
||||||
{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g, css: 'value' }, // sizes
|
'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower ' +
|
||||||
{ regex: /!important/g, css: 'color3' }, // !important
|
'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset ' +
|
||||||
{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'), css: 'keyword' }, // keywords
|
'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side ' +
|
||||||
{ regex: new RegExp(getValuesCSS(values), 'g'), css: 'value' }, // values
|
'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow ' +
|
||||||
{ regex: new RegExp(this.getKeywords(fonts), 'g'), css: 'color1' } // fonts
|
'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize ' +
|
||||||
];
|
'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal ' +
|
||||||
|
'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin ' +
|
||||||
|
'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
|
||||||
|
|
||||||
this.forHtmlScript({
|
var fonts =
|
||||||
left: /(<|<)\s*style.*?(>|>)/gi,
|
'[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
|
||||||
right: /(<|<)\/\s*style\s*(>|>)/gi
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
this.regexList = [
|
||||||
Brush.aliases = ['css'];
|
{ regex: SyntaxHighlighter.regexLib.multiLineCComments, css: 'comments' }, // multiline comments
|
||||||
|
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // double quoted strings
|
||||||
|
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // single quoted strings
|
||||||
|
{ regex: /\#[a-fA-F0-9]{3,6}/g, css: 'value' }, // html colors
|
||||||
|
{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g, css: 'value' }, // sizes
|
||||||
|
{ regex: /!important/g, css: 'color3' }, // !important
|
||||||
|
{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'), css: 'keyword' }, // keywords
|
||||||
|
{ regex: new RegExp(getValuesCSS(values), 'g'), css: 'value' }, // values
|
||||||
|
{ regex: new RegExp(this.getKeywords(fonts), 'g'), css: 'color1' }, // fonts
|
||||||
|
];
|
||||||
|
|
||||||
SyntaxHighlighter.brushes.CSS = Brush;
|
this.forHtmlScript({
|
||||||
|
left: /(<|<)\s*style.*?(>|>)/gi,
|
||||||
|
right: /(<|<)\/\s*style\s*(>|>)/gi,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// CommonJS
|
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
Brush.aliases = ['css'];
|
||||||
|
|
||||||
|
SyntaxHighlighter.brushes.CSS = Brush;
|
||||||
|
|
||||||
|
// CommonJS
|
||||||
|
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,39 +14,42 @@
|
||||||
* @license
|
* @license
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
;(function()
|
(function () {
|
||||||
{
|
// CommonJS
|
||||||
// CommonJS
|
SyntaxHighlighter =
|
||||||
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
|
SyntaxHighlighter ||
|
||||||
|
(typeof require !== 'undefined'
|
||||||
|
? require('shCore').SyntaxHighlighter
|
||||||
|
: null);
|
||||||
|
|
||||||
function Brush()
|
function Brush() {
|
||||||
{
|
var keywords =
|
||||||
var keywords = 'break case catch class continue ' +
|
'break case catch class continue ' +
|
||||||
'default delete do else enum export extends false ' +
|
'default delete do else enum export extends false ' +
|
||||||
'for function if implements import in instanceof ' +
|
'for function if implements import in instanceof ' +
|
||||||
'interface let new null package private protected ' +
|
'interface let new null package private protected ' +
|
||||||
'static return super switch ' +
|
'static return super switch ' +
|
||||||
'this throw true try typeof var while with yield';
|
'this throw true try typeof var while with yield';
|
||||||
|
|
||||||
var r = SyntaxHighlighter.regexLib;
|
var r = SyntaxHighlighter.regexLib;
|
||||||
|
|
||||||
this.regexList = [
|
|
||||||
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
|
||||||
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
|
||||||
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
|
||||||
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
|
||||||
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
|
||||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
|
|
||||||
];
|
|
||||||
|
|
||||||
this.forHtmlScript(r.scriptScriptTags);
|
|
||||||
};
|
|
||||||
|
|
||||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
this.regexList = [
|
||||||
Brush.aliases = ['js', 'jscript', 'javascript', 'json'];
|
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
||||||
|
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
||||||
|
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
||||||
|
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
||||||
|
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
||||||
|
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, // keywords
|
||||||
|
];
|
||||||
|
|
||||||
SyntaxHighlighter.brushes.JScript = Brush;
|
this.forHtmlScript(r.scriptScriptTags);
|
||||||
|
}
|
||||||
|
|
||||||
// CommonJS
|
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
Brush.aliases = ['js', 'jscript', 'javascript', 'json'];
|
||||||
|
|
||||||
|
SyntaxHighlighter.brushes.JScript = Brush;
|
||||||
|
|
||||||
|
// CommonJS
|
||||||
|
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,44 +14,50 @@
|
||||||
* @license
|
* @license
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
;(function()
|
(function () {
|
||||||
{
|
// CommonJS
|
||||||
// CommonJS
|
SyntaxHighlighter =
|
||||||
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
|
SyntaxHighlighter ||
|
||||||
|
(typeof require !== 'undefined'
|
||||||
|
? require('shCore').SyntaxHighlighter
|
||||||
|
: null);
|
||||||
|
|
||||||
function Brush()
|
function Brush() {
|
||||||
{
|
var keywords =
|
||||||
var keywords = 'abstract assert boolean break byte case catch char class const ' +
|
'abstract assert boolean break byte case catch char class const ' +
|
||||||
'continue default do double else enum extends ' +
|
'continue default do double else enum extends ' +
|
||||||
'false final finally float for goto if implements import ' +
|
'false final finally float for goto if implements import ' +
|
||||||
'instanceof int interface long native new null ' +
|
'instanceof int interface long native new null ' +
|
||||||
'package private protected public return ' +
|
'package private protected public return ' +
|
||||||
'short static strictfp super switch synchronized this throw throws true ' +
|
'short static strictfp super switch synchronized this throw throws true ' +
|
||||||
'transient try void volatile while';
|
'transient try void volatile while';
|
||||||
|
|
||||||
this.regexList = [
|
this.regexList = [
|
||||||
{ regex: SyntaxHighlighter.regexLib.singleLineCComments, css: 'comments' }, // one line comments
|
{
|
||||||
{ regex: /\/\*([^\*][\s\S]*?)?\*\//gm, css: 'comments' }, // multiline comments
|
regex: SyntaxHighlighter.regexLib.singleLineCComments,
|
||||||
{ regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments
|
css: 'comments',
|
||||||
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings
|
}, // one line comments
|
||||||
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings
|
{ regex: /\/\*([^\*][\s\S]*?)?\*\//gm, css: 'comments' }, // multiline comments
|
||||||
{ regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers
|
{ regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm, css: 'preprocessor' }, // documentation comments
|
||||||
{ regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno
|
{ regex: SyntaxHighlighter.regexLib.doubleQuotedString, css: 'string' }, // strings
|
||||||
{ regex: /\@interface\b/g, css: 'color2' }, // @interface keyword
|
{ regex: SyntaxHighlighter.regexLib.singleQuotedString, css: 'string' }, // strings
|
||||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // java keyword
|
{ regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi, css: 'value' }, // numbers
|
||||||
];
|
{ regex: /(?!\@interface\b)\@[\$\w]+\b/g, css: 'color1' }, // annotation @anno
|
||||||
|
{ regex: /\@interface\b/g, css: 'color2' }, // @interface keyword
|
||||||
|
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' }, // java keyword
|
||||||
|
];
|
||||||
|
|
||||||
this.forHtmlScript({
|
this.forHtmlScript({
|
||||||
left : /(<|<)%[@!=]?/g,
|
left: /(<|<)%[@!=]?/g,
|
||||||
right : /%(>|>)/g
|
right: /%(>|>)/g,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||||
Brush.aliases = ['java'];
|
Brush.aliases = ['java'];
|
||||||
|
|
||||||
SyntaxHighlighter.brushes.Java = Brush;
|
SyntaxHighlighter.brushes.Java = Brush;
|
||||||
|
|
||||||
// CommonJS
|
// CommonJS
|
||||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,20 +14,21 @@
|
||||||
* @license
|
* @license
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
;(function()
|
(function () {
|
||||||
{
|
// CommonJS
|
||||||
// CommonJS
|
SyntaxHighlighter =
|
||||||
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
|
SyntaxHighlighter ||
|
||||||
|
(typeof require !== 'undefined'
|
||||||
|
? require('shCore').SyntaxHighlighter
|
||||||
|
: null);
|
||||||
|
|
||||||
function Brush()
|
function Brush() {}
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||||
Brush.aliases = ['text', 'plain'];
|
Brush.aliases = ['text', 'plain'];
|
||||||
|
|
||||||
SyntaxHighlighter.brushes.Plain = Brush;
|
SyntaxHighlighter.brushes.Plain = Brush;
|
||||||
|
|
||||||
// CommonJS
|
// CommonJS
|
||||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -14,58 +14,90 @@
|
||||||
* @license
|
* @license
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
;(function()
|
(function () {
|
||||||
{
|
// CommonJS
|
||||||
// CommonJS
|
SyntaxHighlighter =
|
||||||
SyntaxHighlighter = SyntaxHighlighter || (typeof require !== 'undefined'? require('shCore').SyntaxHighlighter : null);
|
SyntaxHighlighter ||
|
||||||
|
(typeof require !== 'undefined'
|
||||||
|
? require('shCore').SyntaxHighlighter
|
||||||
|
: null);
|
||||||
|
|
||||||
function Brush()
|
function Brush() {
|
||||||
{
|
function process(match, regexInfo) {
|
||||||
function process(match, regexInfo)
|
var constructor = SyntaxHighlighter.Match,
|
||||||
{
|
code = match[0],
|
||||||
var constructor = SyntaxHighlighter.Match,
|
tag = XRegExp.exec(
|
||||||
code = match[0],
|
code,
|
||||||
tag = XRegExp.exec(code, XRegExp('(<|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg')),
|
XRegExp('(<|<)[\\s\\/\\?!]*(?<name>[:\\w-\\.]+)', 'xg'),
|
||||||
result = []
|
),
|
||||||
;
|
result = [];
|
||||||
|
if (match.attributes != null) {
|
||||||
|
var attributes,
|
||||||
|
pos = 0,
|
||||||
|
regex = XRegExp(
|
||||||
|
'(?<name> [\\w:.-]+)' +
|
||||||
|
'\\s*=\\s*' +
|
||||||
|
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
||||||
|
'xg',
|
||||||
|
);
|
||||||
|
|
||||||
if (match.attributes != null)
|
while ((attributes = XRegExp.exec(code, regex, pos)) != null) {
|
||||||
{
|
result.push(
|
||||||
var attributes,
|
new constructor(
|
||||||
pos = 0,
|
attributes.name,
|
||||||
regex = XRegExp('(?<name> [\\w:.-]+)' +
|
match.index + attributes.index,
|
||||||
'\\s*=\\s*' +
|
'color1',
|
||||||
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
),
|
||||||
'xg');
|
);
|
||||||
|
result.push(
|
||||||
|
new constructor(
|
||||||
|
attributes.value,
|
||||||
|
match.index +
|
||||||
|
attributes.index +
|
||||||
|
attributes[0].indexOf(attributes.value),
|
||||||
|
'string',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
pos = attributes.index + attributes[0].length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while ((attributes = XRegExp.exec(code, regex, pos)) != null)
|
if (tag != null)
|
||||||
{
|
result.push(
|
||||||
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
|
new constructor(
|
||||||
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
|
tag.name,
|
||||||
pos = attributes.index + attributes[0].length;
|
match.index + tag[0].indexOf(tag.name),
|
||||||
}
|
'keyword',
|
||||||
}
|
),
|
||||||
|
);
|
||||||
|
|
||||||
if (tag != null)
|
return result;
|
||||||
result.push(
|
}
|
||||||
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
this.regexList = [
|
||||||
}
|
{
|
||||||
|
regex: XRegExp(
|
||||||
|
'(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)',
|
||||||
|
'gm',
|
||||||
|
),
|
||||||
|
css: 'color2',
|
||||||
|
}, // <![ ... [ ... ]]>
|
||||||
|
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||||
|
{
|
||||||
|
regex: XRegExp(
|
||||||
|
'(<|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)',
|
||||||
|
'sg',
|
||||||
|
),
|
||||||
|
func: process,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
this.regexList = [
|
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||||
{ regex: XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
|
||||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
|
||||||
{ regex: XRegExp('(<|<)[\\s\\/\\?!]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
SyntaxHighlighter.brushes.Xml = Brush;
|
||||||
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html', 'plist'];
|
|
||||||
|
|
||||||
SyntaxHighlighter.brushes.Xml = Brush;
|
// CommonJS
|
||||||
|
typeof exports != 'undefined' ? (exports.Brush = Brush) : null;
|
||||||
// CommonJS
|
|
||||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
|
||||||
})();
|
})();
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -15,143 +15,121 @@
|
||||||
* Dual licensed under the MIT and GPL licenses.
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
*/
|
*/
|
||||||
var dp = {
|
var dp = {
|
||||||
SyntaxHighlighter : {}
|
SyntaxHighlighter: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
dp.SyntaxHighlighter = {
|
dp.SyntaxHighlighter = {
|
||||||
parseParams: function(
|
parseParams: function (
|
||||||
input,
|
input,
|
||||||
showGutter,
|
showGutter,
|
||||||
showControls,
|
showControls,
|
||||||
collapseAll,
|
collapseAll,
|
||||||
firstLine,
|
firstLine,
|
||||||
showColumns
|
showColumns,
|
||||||
)
|
) {
|
||||||
{
|
function getValue(list, name) {
|
||||||
function getValue(list, name)
|
var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'),
|
||||||
{
|
match = null;
|
||||||
var regex = XRegExp('^' + name + '\\[(?<value>\\w+)\\]$', 'gi'),
|
for (var i = 0; i < list.length; i++)
|
||||||
match = null
|
if ((match = XRegExp.exec(list[i], regex)) != null) return match.value;
|
||||||
;
|
|
||||||
|
|
||||||
for (var i = 0; i < list.length; i++)
|
|
||||||
if ((match = XRegExp.exec(list[i], regex)) != null)
|
|
||||||
return match.value;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
function defaultValue(value, def)
|
|
||||||
{
|
|
||||||
return value != null ? value : def;
|
|
||||||
};
|
|
||||||
|
|
||||||
function asString(value)
|
|
||||||
{
|
|
||||||
return value != null ? value.toString() : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var parts = input.split(':'),
|
return null;
|
||||||
brushName = parts[0],
|
}
|
||||||
options = {},
|
|
||||||
straight = { 'true' : true }
|
|
||||||
reverse = { 'true' : false },
|
|
||||||
result = null,
|
|
||||||
defaults = SyntaxHighlighter.defaults
|
|
||||||
;
|
|
||||||
|
|
||||||
for (var i in parts)
|
|
||||||
options[parts[i]] = 'true';
|
|
||||||
|
|
||||||
showGutter = asString(defaultValue(showGutter, defaults.gutter));
|
function defaultValue(value, def) {
|
||||||
showControls = asString(defaultValue(showControls, defaults.toolbar));
|
return value != null ? value : def;
|
||||||
collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
|
}
|
||||||
showColumns = asString(defaultValue(showColumns, defaults.ruler));
|
|
||||||
firstLine = asString(defaultValue(firstLine, defaults['first-line']));
|
|
||||||
|
|
||||||
return {
|
function asString(value) {
|
||||||
brush : brushName,
|
return value != null ? value.toString() : null;
|
||||||
gutter : defaultValue(reverse[options.nogutter], showGutter),
|
}
|
||||||
toolbar : defaultValue(reverse[options.nocontrols], showControls),
|
|
||||||
collapse : defaultValue(straight[options.collapse], collapseAll),
|
|
||||||
// ruler : defaultValue(straight[options.showcolumns], showColumns),
|
|
||||||
'first-line' : defaultValue(getValue(parts, 'firstline'), firstLine)
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
HighlightAll: function(
|
|
||||||
name,
|
|
||||||
showGutter /* optional */,
|
|
||||||
showControls /* optional */,
|
|
||||||
collapseAll /* optional */,
|
|
||||||
firstLine /* optional */,
|
|
||||||
showColumns /* optional */
|
|
||||||
)
|
|
||||||
{
|
|
||||||
function findValue()
|
|
||||||
{
|
|
||||||
var a = arguments;
|
|
||||||
|
|
||||||
for (var i = 0; i < a.length; i++)
|
|
||||||
{
|
|
||||||
if (a[i] === null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (typeof(a[i]) == 'string' && a[i] != '')
|
|
||||||
return a[i] + '';
|
|
||||||
|
|
||||||
if (typeof(a[i]) == 'object' && a[i].value != '')
|
|
||||||
return a[i].value + '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
function findTagsByName(list, name, tagName)
|
var parts = input.split(':'),
|
||||||
{
|
brushName = parts[0],
|
||||||
var tags = document.getElementsByTagName(tagName);
|
options = {},
|
||||||
|
straight = { true: true };
|
||||||
for (var i = 0; i < tags.length; i++)
|
(reverse = { true: false }),
|
||||||
if (tags[i].getAttribute('name') == name)
|
(result = null),
|
||||||
list.push(tags[i]);
|
(defaults = SyntaxHighlighter.defaults);
|
||||||
}
|
|
||||||
|
|
||||||
var elements = [],
|
|
||||||
highlighter = null,
|
|
||||||
registered = {},
|
|
||||||
propertyName = 'innerHTML'
|
|
||||||
;
|
|
||||||
|
|
||||||
// for some reason IE doesn't find <pre/> by name, however it does see them just fine by tag name...
|
|
||||||
findTagsByName(elements, name, 'pre');
|
|
||||||
findTagsByName(elements, name, 'textarea');
|
|
||||||
|
|
||||||
if (elements.length === 0)
|
for (var i in parts) options[parts[i]] = 'true';
|
||||||
return;
|
|
||||||
|
|
||||||
for (var i = 0; i < elements.length; i++)
|
|
||||||
{
|
|
||||||
var element = elements[i],
|
|
||||||
params = findValue(
|
|
||||||
element.attributes['class'], element.className,
|
|
||||||
element.attributes['language'], element.language
|
|
||||||
),
|
|
||||||
language = ''
|
|
||||||
;
|
|
||||||
|
|
||||||
if (params === null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
params = dp.SyntaxHighlighter.parseParams(
|
showGutter = asString(defaultValue(showGutter, defaults.gutter));
|
||||||
params,
|
showControls = asString(defaultValue(showControls, defaults.toolbar));
|
||||||
showGutter,
|
collapseAll = asString(defaultValue(collapseAll, defaults.collapse));
|
||||||
showControls,
|
showColumns = asString(defaultValue(showColumns, defaults.ruler));
|
||||||
collapseAll,
|
firstLine = asString(defaultValue(firstLine, defaults['first-line']));
|
||||||
firstLine,
|
|
||||||
showColumns
|
|
||||||
);
|
|
||||||
|
|
||||||
SyntaxHighlighter.highlight(params, element);
|
return {
|
||||||
}
|
brush: brushName,
|
||||||
}
|
gutter: defaultValue(reverse[options.nogutter], showGutter),
|
||||||
|
toolbar: defaultValue(reverse[options.nocontrols], showControls),
|
||||||
|
collapse: defaultValue(straight[options.collapse], collapseAll),
|
||||||
|
// ruler : defaultValue(straight[options.showcolumns], showColumns),
|
||||||
|
'first-line': defaultValue(getValue(parts, 'firstline'), firstLine),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
HighlightAll: function (
|
||||||
|
name,
|
||||||
|
showGutter /* optional */,
|
||||||
|
showControls /* optional */,
|
||||||
|
collapseAll /* optional */,
|
||||||
|
firstLine /* optional */,
|
||||||
|
showColumns /* optional */,
|
||||||
|
) {
|
||||||
|
function findValue() {
|
||||||
|
var a = arguments;
|
||||||
|
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
if (a[i] === null) continue;
|
||||||
|
|
||||||
|
if (typeof a[i] == 'string' && a[i] != '') return a[i] + '';
|
||||||
|
|
||||||
|
if (typeof a[i] == 'object' && a[i].value != '') return a[i].value + '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTagsByName(list, name, tagName) {
|
||||||
|
var tags = document.getElementsByTagName(tagName);
|
||||||
|
|
||||||
|
for (var i = 0; i < tags.length; i++)
|
||||||
|
if (tags[i].getAttribute('name') == name) list.push(tags[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var elements = [],
|
||||||
|
highlighter = null,
|
||||||
|
registered = {},
|
||||||
|
propertyName = 'innerHTML';
|
||||||
|
// for some reason IE doesn't find <pre/> by name, however it does see them just fine by tag name...
|
||||||
|
findTagsByName(elements, name, 'pre');
|
||||||
|
findTagsByName(elements, name, 'textarea');
|
||||||
|
|
||||||
|
if (elements.length === 0) return;
|
||||||
|
|
||||||
|
for (var i = 0; i < elements.length; i++) {
|
||||||
|
var element = elements[i],
|
||||||
|
params = findValue(
|
||||||
|
element.attributes['class'],
|
||||||
|
element.className,
|
||||||
|
element.attributes['language'],
|
||||||
|
element.language,
|
||||||
|
),
|
||||||
|
language = '';
|
||||||
|
if (params === null) continue;
|
||||||
|
|
||||||
|
params = dp.SyntaxHighlighter.parseParams(
|
||||||
|
params,
|
||||||
|
showGutter,
|
||||||
|
showControls,
|
||||||
|
collapseAll,
|
||||||
|
firstLine,
|
||||||
|
showColumns,
|
||||||
|
);
|
||||||
|
|
||||||
|
SyntaxHighlighter.highlight(params, element);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,72 +1,79 @@
|
||||||
var truncate = (function() {
|
var truncate = (function () {
|
||||||
function text(node, trim) {
|
function text(node, trim) {
|
||||||
var cn = node.childNodes;
|
var cn = node.childNodes;
|
||||||
var t='';
|
var t = '';
|
||||||
if (cn.length) {
|
if (cn.length) {
|
||||||
for (var i=0; i<cn.length; i++) {
|
for (var i = 0; i < cn.length; i++) {
|
||||||
t += text(cn[i]);
|
t += text(cn[i]);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
t = _.escape(node.textContent);
|
||||||
|
}
|
||||||
|
if (trim) {
|
||||||
|
return t.replace(/^\s+|\s+$/g, '');
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncate(el, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
if (!opts.dir || opts.dir != 'v') return this;
|
||||||
|
var showTitle = opts.showTitle || false;
|
||||||
|
var dir = (opts.dir && opts.dir[0]) || 'h';
|
||||||
|
var scrollProp = dir == 'h' ? 'scrollWidth' : 'scrollHeight';
|
||||||
|
var offsetProp = dir == 'h' ? 'offsetWidth' : 'offsetHeight';
|
||||||
|
var truncText = opts.truncText || '…';
|
||||||
|
var trim = opts.trim || false;
|
||||||
|
var textEl = opts.textEl || el;
|
||||||
|
var split = [' ', ''],
|
||||||
|
counter,
|
||||||
|
success;
|
||||||
|
var txt, cutoff, delim;
|
||||||
|
var oldtext = textEl.getAttribute('data-oldtext') || text(textEl, trim);
|
||||||
|
textEl.setAttribute('data-oldtext', oldtext);
|
||||||
|
for (var i = 0; i < split.length; i++) {
|
||||||
|
delim = split[i];
|
||||||
|
txt = oldtext.split(delim);
|
||||||
|
cutoff = txt.length;
|
||||||
|
success = false;
|
||||||
|
if (textEl.getAttribute('data-oldtext')) {
|
||||||
|
textEl.innerHTML = oldtext;
|
||||||
|
}
|
||||||
|
if (el[scrollProp] - el[offsetProp] < 1) {
|
||||||
|
el.removeAttribute('data-truncated', null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
var chunk = Math.ceil(txt.length / 2),
|
||||||
|
oc = 0,
|
||||||
|
wid;
|
||||||
|
for (counter = 0; counter < 15; counter++) {
|
||||||
|
textEl.innerHTML = txt.slice(0, cutoff).join(delim) + truncText;
|
||||||
|
wid = el[scrollProp] - el[offsetProp];
|
||||||
|
if (cutoff < 1) {
|
||||||
|
break;
|
||||||
|
} else if (wid < 2 && chunk == oc) {
|
||||||
|
if (
|
||||||
|
dir === 'h' ||
|
||||||
|
(delim === '' && el.scrollWidth < el.offsetWidth)
|
||||||
|
) {
|
||||||
|
success = true;
|
||||||
|
el.setAttribute('data-truncated', true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (wid > 1) {
|
||||||
|
cutoff -= chunk;
|
||||||
} else {
|
} else {
|
||||||
t = _.escape(node.textContent);
|
cutoff += chunk;
|
||||||
}
|
}
|
||||||
if (trim) {
|
oc = chunk;
|
||||||
return t.replace(/^\s+|\s+$/g, "");
|
chunk = Math.ceil(chunk / 2);
|
||||||
}
|
}
|
||||||
return t;
|
if (success) break;
|
||||||
}
|
}
|
||||||
|
if (showTitle && oldtext != text(textEl, trim)) {
|
||||||
function truncate(el, opts) {
|
textEl.setAttribute('title', oldtext);
|
||||||
opts = opts || {};
|
|
||||||
if (!opts.dir || opts.dir != 'v') return this;
|
|
||||||
var showTitle = opts.showTitle || false;
|
|
||||||
var dir = (opts.dir && opts.dir[0]) || 'h';
|
|
||||||
var scrollProp = dir == "h" ? "scrollWidth" : "scrollHeight";
|
|
||||||
var offsetProp = dir == "h" ? "offsetWidth" : "offsetHeight";
|
|
||||||
var truncText = opts.truncText || "…";
|
|
||||||
var trim = opts.trim || false;
|
|
||||||
var textEl = opts.textEl || el;
|
|
||||||
var split = [" ",""], counter, success;
|
|
||||||
var txt, cutoff, delim;
|
|
||||||
var oldtext = textEl.getAttribute("data-oldtext") || text(textEl, trim);
|
|
||||||
textEl.setAttribute("data-oldtext", oldtext);
|
|
||||||
for (var i=0; i<split.length; i++) {
|
|
||||||
delim = split[i];
|
|
||||||
txt = oldtext.split(delim);
|
|
||||||
cutoff = txt.length;
|
|
||||||
success = false;
|
|
||||||
if (textEl.getAttribute("data-oldtext")) {
|
|
||||||
textEl.innerHTML = oldtext;
|
|
||||||
}
|
|
||||||
if ((el[scrollProp] - el[offsetProp]) < 1) {
|
|
||||||
el.removeAttribute("data-truncated", null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var chunk = Math.ceil(txt.length/2), oc=0, wid;
|
|
||||||
for (counter = 0; counter < 15; counter++) {
|
|
||||||
textEl.innerHTML = txt.slice(0,cutoff).join(delim) + truncText;
|
|
||||||
wid = (el[scrollProp] - el[offsetProp]);
|
|
||||||
if (cutoff < 1) {
|
|
||||||
break;
|
|
||||||
} else if (wid < 2 && chunk == oc) {
|
|
||||||
if (dir === 'h' || (delim === '' && el.scrollWidth < el.offsetWidth)) {
|
|
||||||
success = true;
|
|
||||||
el.setAttribute("data-truncated", true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (wid > 1) {
|
|
||||||
cutoff -= chunk;
|
|
||||||
} else {
|
|
||||||
cutoff += chunk;
|
|
||||||
}
|
|
||||||
oc = chunk;
|
|
||||||
chunk = Math.ceil(chunk/2);
|
|
||||||
}
|
|
||||||
if (success) break;
|
|
||||||
}
|
|
||||||
if (showTitle && oldtext != text(textEl, trim)) {
|
|
||||||
textEl.setAttribute("title", oldtext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return truncate;
|
return truncate;
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -13,79 +13,95 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
|
// Offering a Custom Alias suport - More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias
|
||||||
(function($) {
|
(function ($) {
|
||||||
|
/**
|
||||||
|
* $ is an alias to jQuery object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
$.fn.lightBox = function (settings) {
|
||||||
|
// Settings to configure the jQuery lightBox plugin how you like
|
||||||
|
settings = jQuery.extend(
|
||||||
|
{
|
||||||
|
// Configuration related to overlay
|
||||||
|
overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
|
||||||
|
overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
|
||||||
|
// Configuration related to images
|
||||||
|
imageLoading: 'images/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon
|
||||||
|
imageBtnPrev: 'images/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image
|
||||||
|
imageBtnNext: 'images/lightbox-btn-next.gif', // (string) Path and the name of the next button image
|
||||||
|
imageBtnClose: 'images/lightbox-btn-close.gif', // (string) Path and the name of the close btn
|
||||||
|
imageBlank: 'images/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel)
|
||||||
|
// Configuration related to container image box
|
||||||
|
containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
|
||||||
|
containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
|
||||||
|
// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
|
||||||
|
txtImage: 'Image', // (string) Specify text "Image"
|
||||||
|
txtOf: 'of', // (string) Specify text "of"
|
||||||
|
// Configuration related to keyboard navigation
|
||||||
|
keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
|
||||||
|
keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image
|
||||||
|
keyToNext: 'n', // (string) (n = next) Letter to show the next image.
|
||||||
|
// Don´t alter these variables in any way
|
||||||
|
imageArray: [],
|
||||||
|
activeImage: 0,
|
||||||
|
},
|
||||||
|
settings,
|
||||||
|
);
|
||||||
|
// Caching the jQuery object with all elements matched
|
||||||
|
var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
|
||||||
/**
|
/**
|
||||||
* $ is an alias to jQuery object
|
* Initializing the plugin calling the start function
|
||||||
*
|
*
|
||||||
|
* @return boolean false
|
||||||
*/
|
*/
|
||||||
$.fn.lightBox = function(settings) {
|
function _initialize() {
|
||||||
// Settings to configure the jQuery lightBox plugin how you like
|
_start(this, jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
|
||||||
settings = jQuery.extend({
|
return false; // Avoid the browser following the link
|
||||||
// Configuration related to overlay
|
}
|
||||||
overlayBgColor: '#000', // (string) Background color to overlay; inform a hexadecimal value like: #RRGGBB. Where RR, GG, and BB are the hexadecimal values for the red, green, and blue values of the color.
|
/**
|
||||||
overlayOpacity: 0.8, // (integer) Opacity value to overlay; inform: 0.X. Where X are number from 0 to 9
|
* Start the jQuery lightBox plugin
|
||||||
// Configuration related to images
|
*
|
||||||
imageLoading: 'images/lightbox-ico-loading.gif', // (string) Path and the name of the loading icon
|
* @param object objClicked The object (link) whick the user have clicked
|
||||||
imageBtnPrev: 'images/lightbox-btn-prev.gif', // (string) Path and the name of the prev button image
|
* @param object jQueryMatchedObj The jQuery object with all elements matched
|
||||||
imageBtnNext: 'images/lightbox-btn-next.gif', // (string) Path and the name of the next button image
|
*/
|
||||||
imageBtnClose: 'images/lightbox-btn-close.gif', // (string) Path and the name of the close btn
|
function _start(objClicked, jQueryMatchedObj) {
|
||||||
imageBlank: 'images/lightbox-blank.gif', // (string) Path and the name of a blank image (one pixel)
|
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
|
||||||
// Configuration related to container image box
|
$('embed, object, select').css({ visibility: 'hidden' });
|
||||||
containerBorderSize: 10, // (integer) If you adjust the padding in the CSS for the container, #lightbox-container-image-box, you will need to update this value
|
// Call the function to create the markup structure; style some elements; assign events in some elements.
|
||||||
containerResizeSpeed: 400, // (integer) Specify the resize duration of container image. These number are miliseconds. 400 is default.
|
_set_interface();
|
||||||
// Configuration related to texts in caption. For example: Image 2 of 8. You can alter either "Image" and "of" texts.
|
// Unset total images in imageArray
|
||||||
txtImage: 'Image', // (string) Specify text "Image"
|
settings.imageArray.length = 0;
|
||||||
txtOf: 'of', // (string) Specify text "of"
|
// Unset image active information
|
||||||
// Configuration related to keyboard navigation
|
settings.activeImage = 0;
|
||||||
keyToClose: 'c', // (string) (c = close) Letter to close the jQuery lightBox interface. Beyond this letter, the letter X and the SCAPE key is used to.
|
// We have an image set? Or just an image? Let´s see it.
|
||||||
keyToPrev: 'p', // (string) (p = previous) Letter to show the previous image
|
if (jQueryMatchedObj.length == 1) {
|
||||||
keyToNext: 'n', // (string) (n = next) Letter to show the next image.
|
settings.imageArray.push(
|
||||||
// Don´t alter these variables in any way
|
new Array(
|
||||||
imageArray: [],
|
objClicked.getAttribute('href'),
|
||||||
activeImage: 0
|
objClicked.getAttribute('title'),
|
||||||
},settings);
|
),
|
||||||
// Caching the jQuery object with all elements matched
|
);
|
||||||
var jQueryMatchedObj = this; // This, in this context, refer to jQuery object
|
} else {
|
||||||
/**
|
// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
|
||||||
* Initializing the plugin calling the start function
|
for (var i = 0; i < jQueryMatchedObj.length; i++) {
|
||||||
*
|
settings.imageArray.push(
|
||||||
* @return boolean false
|
new Array(
|
||||||
*/
|
jQueryMatchedObj[i].getAttribute('href'),
|
||||||
function _initialize() {
|
jQueryMatchedObj[i].getAttribute('title'),
|
||||||
_start(this,jQueryMatchedObj); // This, in this context, refer to object (link) which the user have clicked
|
),
|
||||||
return false; // Avoid the browser following the link
|
);
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* Start the jQuery lightBox plugin
|
while (
|
||||||
*
|
settings.imageArray[settings.activeImage][0] !=
|
||||||
* @param object objClicked The object (link) whick the user have clicked
|
objClicked.getAttribute('href')
|
||||||
* @param object jQueryMatchedObj The jQuery object with all elements matched
|
) {
|
||||||
*/
|
settings.activeImage++;
|
||||||
function _start(objClicked,jQueryMatchedObj) {
|
}
|
||||||
// Hime some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
|
// Call the function that prepares image exibition
|
||||||
$('embed, object, select').css({ 'visibility' : 'hidden' });
|
_set_image_to_view();
|
||||||
// Call the function to create the markup structure; style some elements; assign events in some elements.
|
}
|
||||||
_set_interface();
|
/**
|
||||||
// Unset total images in imageArray
|
|
||||||
settings.imageArray.length = 0;
|
|
||||||
// Unset image active information
|
|
||||||
settings.activeImage = 0;
|
|
||||||
// We have an image set? Or just an image? Let´s see it.
|
|
||||||
if ( jQueryMatchedObj.length == 1 ) {
|
|
||||||
settings.imageArray.push(new Array(objClicked.getAttribute('href'),objClicked.getAttribute('title')));
|
|
||||||
} else {
|
|
||||||
// Add an Array (as many as we have), with href and title atributes, inside the Array that storage the images references
|
|
||||||
for ( var i = 0; i < jQueryMatchedObj.length; i++ ) {
|
|
||||||
settings.imageArray.push(new Array(jQueryMatchedObj[i].getAttribute('href'),jQueryMatchedObj[i].getAttribute('title')));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( settings.imageArray[settings.activeImage][0] != objClicked.getAttribute('href') ) {
|
|
||||||
settings.activeImage++;
|
|
||||||
}
|
|
||||||
// Call the function that prepares image exibition
|
|
||||||
_set_image_to_view();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Create the jQuery lightBox plugin interface
|
* Create the jQuery lightBox plugin interface
|
||||||
*
|
*
|
||||||
* The HTML markup will be like that:
|
* The HTML markup will be like that:
|
||||||
|
@ -121,326 +137,414 @@
|
||||||
</div>
|
</div>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function _set_interface() {
|
function _set_interface() {
|
||||||
// Apply the HTML markup into body tag
|
// Apply the HTML markup into body tag
|
||||||
$('body').append('<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + settings.imageLoading + '"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><span class="sr-only">' + gettext('close') + '</span></a></div></div></div></div>');
|
$('body').append(
|
||||||
// Get page sizes
|
'<div id="jquery-overlay"></div><div id="jquery-lightbox"><div id="lightbox-container-image-box"><div id="lightbox-container-image"><img id="lightbox-image"><div style="" id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' +
|
||||||
var arrPageSizes = ___getPageSize();
|
settings.imageLoading +
|
||||||
// Style overlay and show it
|
'"></a></div></div></div><div id="lightbox-container-image-data-box"><div id="lightbox-container-image-data"><div id="lightbox-image-details"><span id="lightbox-image-details-caption"></span><span id="lightbox-image-details-currentNumber"></span></div><div id="lightbox-secNav"><a href="#" id="lightbox-secNav-btnClose"><span class="sr-only">' +
|
||||||
$('#jquery-overlay').css({
|
gettext('close') +
|
||||||
backgroundColor: settings.overlayBgColor,
|
'</span></a></div></div></div></div>',
|
||||||
opacity: settings.overlayOpacity,
|
);
|
||||||
width: arrPageSizes[0],
|
// Get page sizes
|
||||||
height: arrPageSizes[1]
|
var arrPageSizes = ___getPageSize();
|
||||||
}).fadeIn();
|
// Style overlay and show it
|
||||||
// Get page scroll
|
$('#jquery-overlay')
|
||||||
var arrPageScroll = ___getPageScroll();
|
.css({
|
||||||
// Calculate top and left offset for the jquery-lightbox div object and show it
|
backgroundColor: settings.overlayBgColor,
|
||||||
$('#jquery-lightbox').css({
|
opacity: settings.overlayOpacity,
|
||||||
top: arrPageScroll[1] + (arrPageSizes[3] / 10),
|
width: arrPageSizes[0],
|
||||||
left: arrPageScroll[0]
|
height: arrPageSizes[1],
|
||||||
}).show();
|
})
|
||||||
// Assigning click events in elements to close overlay
|
.fadeIn();
|
||||||
$('#jquery-overlay,#jquery-lightbox').click(function() {
|
// Get page scroll
|
||||||
_finish();
|
var arrPageScroll = ___getPageScroll();
|
||||||
});
|
// Calculate top and left offset for the jquery-lightbox div object and show it
|
||||||
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
|
$('#jquery-lightbox')
|
||||||
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function() {
|
.css({
|
||||||
_finish();
|
top: arrPageScroll[1] + arrPageSizes[3] / 10,
|
||||||
return false;
|
left: arrPageScroll[0],
|
||||||
});
|
})
|
||||||
// If window was resized, calculate the new overlay dimensions
|
.show();
|
||||||
$(window).resize(function() {
|
// Assigning click events in elements to close overlay
|
||||||
// Get page sizes
|
$('#jquery-overlay,#jquery-lightbox').click(function () {
|
||||||
var arrPageSizes = ___getPageSize();
|
_finish();
|
||||||
// Style overlay and show it
|
});
|
||||||
$('#jquery-overlay').css({
|
// Assign the _finish function to lightbox-loading-link and lightbox-secNav-btnClose objects
|
||||||
width: arrPageSizes[0],
|
$('#lightbox-loading-link,#lightbox-secNav-btnClose').click(function () {
|
||||||
height: arrPageSizes[1]
|
_finish();
|
||||||
});
|
return false;
|
||||||
// Get page scroll
|
});
|
||||||
var arrPageScroll = ___getPageScroll();
|
// If window was resized, calculate the new overlay dimensions
|
||||||
// Calculate top and left offset for the jquery-lightbox div object and show it
|
$(window).resize(function () {
|
||||||
$('#jquery-lightbox').css({
|
// Get page sizes
|
||||||
top: arrPageScroll[1] + (arrPageSizes[3] / 10),
|
var arrPageSizes = ___getPageSize();
|
||||||
left: arrPageScroll[0]
|
// Style overlay and show it
|
||||||
});
|
$('#jquery-overlay').css({
|
||||||
});
|
width: arrPageSizes[0],
|
||||||
|
height: arrPageSizes[1],
|
||||||
|
});
|
||||||
|
// Get page scroll
|
||||||
|
var arrPageScroll = ___getPageScroll();
|
||||||
|
// Calculate top and left offset for the jquery-lightbox div object and show it
|
||||||
|
$('#jquery-lightbox').css({
|
||||||
|
top: arrPageScroll[1] + arrPageSizes[3] / 10,
|
||||||
|
left: arrPageScroll[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Prepares image exibition; doing a image´s preloader to calculate it´s size
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function _set_image_to_view() {
|
||||||
|
// show the loading
|
||||||
|
// Show the loading
|
||||||
|
$('#lightbox-loading').show();
|
||||||
|
// Hide some elements
|
||||||
|
$(
|
||||||
|
'#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber',
|
||||||
|
).hide();
|
||||||
|
// Image preload process
|
||||||
|
var objImagePreloader = new Image();
|
||||||
|
objImagePreloader.onload = function () {
|
||||||
|
$('#lightbox-image').attr(
|
||||||
|
'src',
|
||||||
|
settings.imageArray[settings.activeImage][0],
|
||||||
|
);
|
||||||
|
// Perfomance an effect in the image container resizing it
|
||||||
|
_resize_container_image_box(
|
||||||
|
objImagePreloader.width,
|
||||||
|
objImagePreloader.height,
|
||||||
|
);
|
||||||
|
// clear onLoad, IE behaves irratically with animated gifs otherwise
|
||||||
|
objImagePreloader.onload = function () {};
|
||||||
|
};
|
||||||
|
objImagePreloader.src = settings.imageArray[settings.activeImage][0];
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Perfomance an effect in the image container resizing it
|
||||||
|
*
|
||||||
|
* @param integer intImageWidth The image´s width that will be showed
|
||||||
|
* @param integer intImageHeight The image´s height that will be showed
|
||||||
|
*/
|
||||||
|
function _resize_container_image_box(intImageWidth, intImageHeight) {
|
||||||
|
// Get current width and height
|
||||||
|
var intCurrentWidth = $('#lightbox-container-image-box').width();
|
||||||
|
var intCurrentHeight = $('#lightbox-container-image-box').height();
|
||||||
|
// Get the width and height of the selected image plus the padding
|
||||||
|
var intWidth = intImageWidth + settings.containerBorderSize * 2; // Plus the image´s width and the left and right padding value
|
||||||
|
var intHeight = intImageHeight + settings.containerBorderSize * 2; // Plus the image´s height and the left and right padding value
|
||||||
|
// Diferences
|
||||||
|
var intDiffW = intCurrentWidth - intWidth;
|
||||||
|
var intDiffH = intCurrentHeight - intHeight;
|
||||||
|
// Perfomance the effect
|
||||||
|
$('#lightbox-container-image-box').animate(
|
||||||
|
{ width: intWidth, height: intHeight },
|
||||||
|
settings.containerResizeSpeed,
|
||||||
|
function () {
|
||||||
|
_show_image();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (intDiffW == 0 && intDiffH == 0) {
|
||||||
|
if ($.browser.msie) {
|
||||||
|
___pause(250);
|
||||||
|
} else {
|
||||||
|
___pause(100);
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* Prepares image exibition; doing a image´s preloader to calculate it´s size
|
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({
|
||||||
*
|
height: intImageHeight + settings.containerBorderSize * 2,
|
||||||
*/
|
});
|
||||||
function _set_image_to_view() { // show the loading
|
$('#lightbox-container-image-data-box').css({ width: intImageWidth });
|
||||||
// Show the loading
|
}
|
||||||
$('#lightbox-loading').show();
|
/**
|
||||||
// Hide some elements
|
* Show the prepared image
|
||||||
$('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-container-image-data-box,#lightbox-image-details-currentNumber').hide();
|
*
|
||||||
// Image preload process
|
*/
|
||||||
var objImagePreloader = new Image();
|
function _show_image() {
|
||||||
objImagePreloader.onload = function() {
|
$('#lightbox-loading').hide();
|
||||||
$('#lightbox-image').attr('src',settings.imageArray[settings.activeImage][0]);
|
$('#lightbox-image').fadeIn(function () {
|
||||||
// Perfomance an effect in the image container resizing it
|
_show_image_data();
|
||||||
_resize_container_image_box(objImagePreloader.width,objImagePreloader.height);
|
_set_navigation();
|
||||||
// clear onLoad, IE behaves irratically with animated gifs otherwise
|
});
|
||||||
objImagePreloader.onload=function(){};
|
_preload_neighbor_images();
|
||||||
}
|
}
|
||||||
objImagePreloader.src = settings.imageArray[settings.activeImage][0];
|
/**
|
||||||
};
|
* Show the image information
|
||||||
/**
|
*
|
||||||
* Perfomance an effect in the image container resizing it
|
*/
|
||||||
*
|
function _show_image_data() {
|
||||||
* @param integer intImageWidth The image´s width that will be showed
|
$('#lightbox-container-image-data-box').slideDown('fast');
|
||||||
* @param integer intImageHeight The image´s height that will be showed
|
$('#lightbox-image-details-caption').hide();
|
||||||
*/
|
if (settings.imageArray[settings.activeImage][1]) {
|
||||||
function _resize_container_image_box(intImageWidth,intImageHeight) {
|
$('#lightbox-image-details-caption')
|
||||||
// Get current width and height
|
.text(settings.imageArray[settings.activeImage][1])
|
||||||
var intCurrentWidth = $('#lightbox-container-image-box').width();
|
.show();
|
||||||
var intCurrentHeight = $('#lightbox-container-image-box').height();
|
}
|
||||||
// Get the width and height of the selected image plus the padding
|
// If we have a image set, display 'Image X of X'
|
||||||
var intWidth = (intImageWidth + (settings.containerBorderSize * 2)); // Plus the image´s width and the left and right padding value
|
if (settings.imageArray.length > 1) {
|
||||||
var intHeight = (intImageHeight + (settings.containerBorderSize * 2)); // Plus the image´s height and the left and right padding value
|
$('#lightbox-image-details-currentNumber')
|
||||||
// Diferences
|
.html(
|
||||||
var intDiffW = intCurrentWidth - intWidth;
|
settings.txtImage +
|
||||||
var intDiffH = intCurrentHeight - intHeight;
|
' ' +
|
||||||
// Perfomance the effect
|
(settings.activeImage + 1) +
|
||||||
$('#lightbox-container-image-box').animate({ width: intWidth, height: intHeight },settings.containerResizeSpeed,function() { _show_image(); });
|
' ' +
|
||||||
if ( ( intDiffW == 0 ) && ( intDiffH == 0 ) ) {
|
settings.txtOf +
|
||||||
if ( $.browser.msie ) {
|
' ' +
|
||||||
___pause(250);
|
settings.imageArray.length,
|
||||||
} else {
|
)
|
||||||
___pause(100);
|
.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ height: intImageHeight + (settings.containerBorderSize * 2) });
|
/**
|
||||||
$('#lightbox-container-image-data-box').css({ width: intImageWidth });
|
* Display the button navigations
|
||||||
};
|
*
|
||||||
/**
|
*/
|
||||||
* Show the prepared image
|
function _set_navigation() {
|
||||||
*
|
$('#lightbox-nav').show();
|
||||||
*/
|
|
||||||
function _show_image() {
|
|
||||||
$('#lightbox-loading').hide();
|
|
||||||
$('#lightbox-image').fadeIn(function() {
|
|
||||||
_show_image_data();
|
|
||||||
_set_navigation();
|
|
||||||
});
|
|
||||||
_preload_neighbor_images();
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* Show the image information
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function _show_image_data() {
|
|
||||||
$('#lightbox-container-image-data-box').slideDown('fast');
|
|
||||||
$('#lightbox-image-details-caption').hide();
|
|
||||||
if ( settings.imageArray[settings.activeImage][1] ) {
|
|
||||||
$('#lightbox-image-details-caption').text(settings.imageArray[settings.activeImage][1]).show();
|
|
||||||
}
|
|
||||||
// If we have a image set, display 'Image X of X'
|
|
||||||
if ( settings.imageArray.length > 1 ) {
|
|
||||||
$('#lightbox-image-details-currentNumber').html(settings.txtImage + ' ' + ( settings.activeImage + 1 ) + ' ' + settings.txtOf + ' ' + settings.imageArray.length).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Display the button navigations
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function _set_navigation() {
|
|
||||||
$('#lightbox-nav').show();
|
|
||||||
|
|
||||||
// Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just.
|
// Instead to define this configuration in CSS file, we define here. And it´s need to IE. Just.
|
||||||
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
|
$('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css({
|
||||||
|
background: 'transparent url(' + settings.imageBlank + ') no-repeat',
|
||||||
|
});
|
||||||
|
|
||||||
// Show the prev button, if not the first image in set
|
// Show the prev button, if not the first image in set
|
||||||
if ( settings.activeImage != 0 ) {
|
if (settings.activeImage != 0) {
|
||||||
// Show the images button for Next buttons
|
// Show the images button for Next buttons
|
||||||
$('#lightbox-nav-btnPrev').off().hover(function() {
|
$('#lightbox-nav-btnPrev')
|
||||||
$(this).css({ 'background' : 'url(' + settings.imageBtnPrev + ') left 45% no-repeat' });
|
.off()
|
||||||
},function() {
|
.hover(
|
||||||
$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
|
function () {
|
||||||
}).show().on('click', function() {
|
$(this).css({
|
||||||
settings.activeImage = settings.activeImage - 1;
|
background:
|
||||||
_set_image_to_view();
|
'url(' + settings.imageBtnPrev + ') left 45% no-repeat',
|
||||||
return false;
|
});
|
||||||
});
|
},
|
||||||
}
|
function () {
|
||||||
|
$(this).css({
|
||||||
|
background:
|
||||||
|
'transparent url(' + settings.imageBlank + ') no-repeat',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
.on('click', function () {
|
||||||
|
settings.activeImage = settings.activeImage - 1;
|
||||||
|
_set_image_to_view();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Show the next button, if not the last image in set
|
// Show the next button, if not the last image in set
|
||||||
if ( settings.activeImage != ( settings.imageArray.length -1 ) ) {
|
if (settings.activeImage != settings.imageArray.length - 1) {
|
||||||
// Show the images button for Next buttons
|
// Show the images button for Next buttons
|
||||||
$('#lightbox-nav-btnNext').off().hover(function() {
|
$('#lightbox-nav-btnNext')
|
||||||
$(this).css({ 'background' : 'url(' + settings.imageBtnNext + ') right 45% no-repeat' });
|
.off()
|
||||||
},function() {
|
.hover(
|
||||||
$(this).css({ 'background' : 'transparent url(' + settings.imageBlank + ') no-repeat' });
|
function () {
|
||||||
}).show().on('click', function() {
|
$(this).css({
|
||||||
settings.activeImage = settings.activeImage + 1;
|
background:
|
||||||
_set_image_to_view();
|
'url(' + settings.imageBtnNext + ') right 45% no-repeat',
|
||||||
return false;
|
});
|
||||||
});
|
},
|
||||||
}
|
function () {
|
||||||
// Enable keyboard navigation
|
$(this).css({
|
||||||
_enable_keyboard_navigation();
|
background:
|
||||||
|
'transparent url(' + settings.imageBlank + ') no-repeat',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
.on('click', function () {
|
||||||
|
settings.activeImage = settings.activeImage + 1;
|
||||||
|
_set_image_to_view();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Enable keyboard navigation
|
||||||
|
_enable_keyboard_navigation();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Enable a support to keyboard navigation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function _enable_keyboard_navigation() {
|
||||||
|
$(document).keydown(function (objEvent) {
|
||||||
|
_keyboard_action(objEvent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Disable the support to keyboard navigation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function _disable_keyboard_navigation() {
|
||||||
|
$(document).off();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Perform the keyboard actions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function _keyboard_action(objEvent) {
|
||||||
|
// To ie
|
||||||
|
if (objEvent == null) {
|
||||||
|
keycode = event.keyCode;
|
||||||
|
escapeKey = 27;
|
||||||
|
// To Mozilla
|
||||||
|
} else {
|
||||||
|
keycode = objEvent.keyCode;
|
||||||
|
escapeKey = objEvent.DOM_VK_ESCAPE;
|
||||||
|
}
|
||||||
|
// Get the key in lower case form
|
||||||
|
key = String.fromCharCode(keycode).toLowerCase();
|
||||||
|
// Verify the keys to close the ligthBox
|
||||||
|
if (key == settings.keyToClose || key == 'x' || keycode == escapeKey) {
|
||||||
|
_finish();
|
||||||
|
}
|
||||||
|
// Verify the key to show the previous image
|
||||||
|
if (key == settings.keyToPrev || keycode == 37) {
|
||||||
|
// If we´re not showing the first image, call the previous
|
||||||
|
if (settings.activeImage != 0) {
|
||||||
|
settings.activeImage = settings.activeImage - 1;
|
||||||
|
_set_image_to_view();
|
||||||
|
_disable_keyboard_navigation();
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* Enable a support to keyboard navigation
|
// Verify the key to show the next image
|
||||||
*
|
if (key == settings.keyToNext || keycode == 39) {
|
||||||
*/
|
// If we´re not showing the last image, call the next
|
||||||
function _enable_keyboard_navigation() {
|
if (settings.activeImage != settings.imageArray.length - 1) {
|
||||||
$(document).keydown(function(objEvent) {
|
settings.activeImage = settings.activeImage + 1;
|
||||||
_keyboard_action(objEvent);
|
_set_image_to_view();
|
||||||
});
|
_disable_keyboard_navigation();
|
||||||
}
|
}
|
||||||
/**
|
}
|
||||||
* Disable the support to keyboard navigation
|
}
|
||||||
*
|
/**
|
||||||
*/
|
* Preload prev and next images being showed
|
||||||
function _disable_keyboard_navigation() {
|
*
|
||||||
$(document).off();
|
*/
|
||||||
}
|
function _preload_neighbor_images() {
|
||||||
/**
|
if (settings.imageArray.length - 1 > settings.activeImage) {
|
||||||
* Perform the keyboard actions
|
objNext = new Image();
|
||||||
*
|
objNext.src = settings.imageArray[settings.activeImage + 1][0];
|
||||||
*/
|
}
|
||||||
function _keyboard_action(objEvent) {
|
if (settings.activeImage > 0) {
|
||||||
// To ie
|
objPrev = new Image();
|
||||||
if ( objEvent == null ) {
|
objPrev.src = settings.imageArray[settings.activeImage - 1][0];
|
||||||
keycode = event.keyCode;
|
}
|
||||||
escapeKey = 27;
|
}
|
||||||
// To Mozilla
|
/**
|
||||||
} else {
|
* Remove jQuery lightBox plugin HTML markup
|
||||||
keycode = objEvent.keyCode;
|
*
|
||||||
escapeKey = objEvent.DOM_VK_ESCAPE;
|
*/
|
||||||
}
|
function _finish() {
|
||||||
// Get the key in lower case form
|
$('#jquery-lightbox').remove();
|
||||||
key = String.fromCharCode(keycode).toLowerCase();
|
$('#jquery-overlay').fadeOut(function () {
|
||||||
// Verify the keys to close the ligthBox
|
$('#jquery-overlay').remove();
|
||||||
if ( ( key == settings.keyToClose ) || ( key == 'x' ) || ( keycode == escapeKey ) ) {
|
});
|
||||||
_finish();
|
// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
|
||||||
}
|
$('embed, object, select').css({ visibility: 'visible' });
|
||||||
// Verify the key to show the previous image
|
}
|
||||||
if ( ( key == settings.keyToPrev ) || ( keycode == 37 ) ) {
|
/**
|
||||||
// If we´re not showing the first image, call the previous
|
|
||||||
if ( settings.activeImage != 0 ) {
|
|
||||||
settings.activeImage = settings.activeImage - 1;
|
|
||||||
_set_image_to_view();
|
|
||||||
_disable_keyboard_navigation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Verify the key to show the next image
|
|
||||||
if ( ( key == settings.keyToNext ) || ( keycode == 39 ) ) {
|
|
||||||
// If we´re not showing the last image, call the next
|
|
||||||
if ( settings.activeImage != ( settings.imageArray.length - 1 ) ) {
|
|
||||||
settings.activeImage = settings.activeImage + 1;
|
|
||||||
_set_image_to_view();
|
|
||||||
_disable_keyboard_navigation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Preload prev and next images being showed
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function _preload_neighbor_images() {
|
|
||||||
if ( (settings.imageArray.length -1) > settings.activeImage ) {
|
|
||||||
objNext = new Image();
|
|
||||||
objNext.src = settings.imageArray[settings.activeImage + 1][0];
|
|
||||||
}
|
|
||||||
if ( settings.activeImage > 0 ) {
|
|
||||||
objPrev = new Image();
|
|
||||||
objPrev.src = settings.imageArray[settings.activeImage -1][0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Remove jQuery lightBox plugin HTML markup
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function _finish() {
|
|
||||||
$('#jquery-lightbox').remove();
|
|
||||||
$('#jquery-overlay').fadeOut(function() { $('#jquery-overlay').remove(); });
|
|
||||||
// Show some elements to avoid conflict with overlay in IE. These elements appear above the overlay.
|
|
||||||
$('embed, object, select').css({ 'visibility' : 'visible' });
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
/ THIRD FUNCTION
|
/ THIRD FUNCTION
|
||||||
* getPageSize() by quirksmode.com
|
* getPageSize() by quirksmode.com
|
||||||
*
|
*
|
||||||
* @return Array Return an array with page width, height and window width, height
|
* @return Array Return an array with page width, height and window width, height
|
||||||
*/
|
*/
|
||||||
function ___getPageSize() {
|
function ___getPageSize() {
|
||||||
var xScroll, yScroll;
|
var xScroll, yScroll;
|
||||||
if (window.innerHeight && window.scrollMaxY) {
|
if (window.innerHeight && window.scrollMaxY) {
|
||||||
xScroll = window.innerWidth + window.scrollMaxX;
|
xScroll = window.innerWidth + window.scrollMaxX;
|
||||||
yScroll = window.innerHeight + window.scrollMaxY;
|
yScroll = window.innerHeight + window.scrollMaxY;
|
||||||
} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
|
} else if (document.body.scrollHeight > document.body.offsetHeight) {
|
||||||
xScroll = document.body.scrollWidth;
|
// all but Explorer Mac
|
||||||
yScroll = document.body.scrollHeight;
|
xScroll = document.body.scrollWidth;
|
||||||
} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
|
yScroll = document.body.scrollHeight;
|
||||||
xScroll = document.body.offsetWidth;
|
} else {
|
||||||
yScroll = document.body.offsetHeight;
|
// Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
|
||||||
}
|
xScroll = document.body.offsetWidth;
|
||||||
var windowWidth, windowHeight;
|
yScroll = document.body.offsetHeight;
|
||||||
if (self.innerHeight) { // all except Explorer
|
}
|
||||||
if(document.documentElement.clientWidth){
|
var windowWidth, windowHeight;
|
||||||
windowWidth = document.documentElement.clientWidth;
|
if (self.innerHeight) {
|
||||||
} else {
|
// all except Explorer
|
||||||
windowWidth = self.innerWidth;
|
if (document.documentElement.clientWidth) {
|
||||||
}
|
windowWidth = document.documentElement.clientWidth;
|
||||||
windowHeight = self.innerHeight;
|
} else {
|
||||||
} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
|
windowWidth = self.innerWidth;
|
||||||
windowWidth = document.documentElement.clientWidth;
|
}
|
||||||
windowHeight = document.documentElement.clientHeight;
|
windowHeight = self.innerHeight;
|
||||||
} else if (document.body) { // other Explorers
|
} else if (
|
||||||
windowWidth = document.body.clientWidth;
|
document.documentElement &&
|
||||||
windowHeight = document.body.clientHeight;
|
document.documentElement.clientHeight
|
||||||
}
|
) {
|
||||||
// for small pages with total height less then height of the viewport
|
// Explorer 6 Strict Mode
|
||||||
if(yScroll < windowHeight){
|
windowWidth = document.documentElement.clientWidth;
|
||||||
pageHeight = windowHeight;
|
windowHeight = document.documentElement.clientHeight;
|
||||||
} else {
|
} else if (document.body) {
|
||||||
pageHeight = yScroll;
|
// other Explorers
|
||||||
}
|
windowWidth = document.body.clientWidth;
|
||||||
// for small pages with total width less then width of the viewport
|
windowHeight = document.body.clientHeight;
|
||||||
if(xScroll < windowWidth){
|
}
|
||||||
pageWidth = xScroll;
|
// for small pages with total height less then height of the viewport
|
||||||
} else {
|
if (yScroll < windowHeight) {
|
||||||
pageWidth = windowWidth;
|
pageHeight = windowHeight;
|
||||||
}
|
} else {
|
||||||
arrayPageSize = new Array(pageWidth,pageHeight,windowWidth,windowHeight)
|
pageHeight = yScroll;
|
||||||
return arrayPageSize;
|
}
|
||||||
};
|
// for small pages with total width less then width of the viewport
|
||||||
/**
|
if (xScroll < windowWidth) {
|
||||||
|
pageWidth = xScroll;
|
||||||
|
} else {
|
||||||
|
pageWidth = windowWidth;
|
||||||
|
}
|
||||||
|
arrayPageSize = new Array(
|
||||||
|
pageWidth,
|
||||||
|
pageHeight,
|
||||||
|
windowWidth,
|
||||||
|
windowHeight,
|
||||||
|
);
|
||||||
|
return arrayPageSize;
|
||||||
|
}
|
||||||
|
/**
|
||||||
/ THIRD FUNCTION
|
/ THIRD FUNCTION
|
||||||
* getPageScroll() by quirksmode.com
|
* getPageScroll() by quirksmode.com
|
||||||
*
|
*
|
||||||
* @return Array Return an array with x,y page scroll values.
|
* @return Array Return an array with x,y page scroll values.
|
||||||
*/
|
*/
|
||||||
function ___getPageScroll() {
|
function ___getPageScroll() {
|
||||||
var xScroll, yScroll;
|
var xScroll, yScroll;
|
||||||
if (self.pageYOffset) {
|
if (self.pageYOffset) {
|
||||||
yScroll = self.pageYOffset;
|
yScroll = self.pageYOffset;
|
||||||
xScroll = self.pageXOffset;
|
xScroll = self.pageXOffset;
|
||||||
} else if (document.documentElement && document.documentElement.scrollTop) { // Explorer 6 Strict
|
} else if (
|
||||||
yScroll = document.documentElement.scrollTop;
|
document.documentElement &&
|
||||||
xScroll = document.documentElement.scrollLeft;
|
document.documentElement.scrollTop
|
||||||
} else if (document.body) {// all other Explorers
|
) {
|
||||||
yScroll = document.body.scrollTop;
|
// Explorer 6 Strict
|
||||||
xScroll = document.body.scrollLeft;
|
yScroll = document.documentElement.scrollTop;
|
||||||
}
|
xScroll = document.documentElement.scrollLeft;
|
||||||
arrayPageScroll = new Array(xScroll,yScroll)
|
} else if (document.body) {
|
||||||
return arrayPageScroll;
|
// all other Explorers
|
||||||
};
|
yScroll = document.body.scrollTop;
|
||||||
/**
|
xScroll = document.body.scrollLeft;
|
||||||
* Stop the code execution from a escified time in milisecond
|
}
|
||||||
*
|
arrayPageScroll = new Array(xScroll, yScroll);
|
||||||
*/
|
return arrayPageScroll;
|
||||||
function ___pause(ms) {
|
}
|
||||||
var date = new Date();
|
/**
|
||||||
curDate = null;
|
* Stop the code execution from a escified time in milisecond
|
||||||
do { var curDate = new Date(); }
|
*
|
||||||
while ( curDate - date < ms);
|
*/
|
||||||
};
|
function ___pause(ms) {
|
||||||
// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
|
var date = new Date();
|
||||||
return this.off('click').click(_initialize);
|
curDate = null;
|
||||||
};
|
do {
|
||||||
|
var curDate = new Date();
|
||||||
|
} while (curDate - date < ms);
|
||||||
|
}
|
||||||
|
// Return the jQuery object for chaining. The unbind method is used to avoid click conflict when the plugin is called more than once
|
||||||
|
return this.off('click').click(_initialize);
|
||||||
|
};
|
||||||
})(jQuery); // Call and execute the function immediately passing the jQuery object
|
})(jQuery); // Call and execute the function immediately passing the jQuery object
|
||||||
|
|
|
@ -35,7 +35,7 @@ const showOrHideDefinition = (select) => {
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// This function changes the visibility of the `name` field in the "change
|
// This function changes the visibility of the `name` field in the "change
|
||||||
// form". We only want to show this field when a scanner is selected and this
|
// form". We only want to show this field when a scanner is selected and this
|
||||||
|
@ -47,7 +47,7 @@ const showOrHideName = (select) => {
|
||||||
const isNone = selectedScannerIsNone(select);
|
const isNone = selectedScannerIsNone(select);
|
||||||
const isYara = selectedScannerIsYara(select);
|
const isYara = selectedScannerIsYara(select);
|
||||||
|
|
||||||
name.style.display = (isNone || isYara) ? 'none' : 'block';
|
name.style.display = isNone || isYara ? 'none' : 'block';
|
||||||
|
|
||||||
if (isYara) {
|
if (isYara) {
|
||||||
const nameInput = document.querySelector('#id_name');
|
const nameInput = document.querySelector('#id_name');
|
||||||
|
@ -56,7 +56,7 @@ const showOrHideName = (select) => {
|
||||||
nameInput.value = DEFAULT_RULE_NAME;
|
nameInput.value = DEFAULT_RULE_NAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const scannerSelect = document.querySelector('#id_scanner');
|
const scannerSelect = document.querySelector('#id_scanner');
|
||||||
const definitionTextarea = document.querySelector('#id_definition');
|
const definitionTextarea = document.querySelector('#id_definition');
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
/** Addons Display page */
|
/** Addons Display page */
|
||||||
|
|
||||||
/* general initialization */
|
/* general initialization */
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
if ($('#addon.primary').length == 0) return;
|
||||||
|
|
||||||
if ($('#addon.primary').length == 0) return;
|
var lb_baseurl = z.static_url + 'img/jquery-lightbox/';
|
||||||
|
$("a[rel='jquery-lightbox']").lightBox({
|
||||||
|
overlayOpacity: 0.6,
|
||||||
|
imageBlank: lb_baseurl + 'lightbox-blank.gif',
|
||||||
|
imageLoading: lb_baseurl + 'lightbox-ico-loading.gif',
|
||||||
|
imageBtnClose: lb_baseurl + 'close.png',
|
||||||
|
imageBtnPrev: lb_baseurl + 'goleft.png',
|
||||||
|
imageBtnNext: lb_baseurl + 'goright.png',
|
||||||
|
containerResizeSpeed: 350,
|
||||||
|
});
|
||||||
|
|
||||||
var lb_baseurl = z.static_url+'img/jquery-lightbox/';
|
var etiquette_box = $('#addons-display-review-etiquette').hide();
|
||||||
$("a[rel='jquery-lightbox']").lightBox({
|
$('#short-review').focus(function () {
|
||||||
overlayOpacity: 0.6,
|
etiquette_box.show('fast');
|
||||||
imageBlank: lb_baseurl+"lightbox-blank.gif",
|
});
|
||||||
imageLoading: lb_baseurl+"lightbox-ico-loading.gif",
|
|
||||||
imageBtnClose: lb_baseurl+"close.png",
|
|
||||||
imageBtnPrev: lb_baseurl+"goleft.png",
|
|
||||||
imageBtnNext: lb_baseurl+"goright.png",
|
|
||||||
containerResizeSpeed: 350
|
|
||||||
});
|
|
||||||
|
|
||||||
var etiquette_box = $("#addons-display-review-etiquette").hide();
|
|
||||||
$("#short-review").focus(function() { etiquette_box.show("fast"); } );
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,55 +1,62 @@
|
||||||
(function(){
|
(function () {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
// Add sphinx-like links to headings with ids.
|
// Add sphinx-like links to headings with ids.
|
||||||
$(function(){
|
$(function () {
|
||||||
var html = '<a class="headerlink" href="#{0}">¶</a>';
|
var html = '<a class="headerlink" href="#{0}">¶</a>';
|
||||||
$(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function() {
|
$(':-moz-any(h1,h2,h3,h4,h5,h6)[id]').each(function () {
|
||||||
console.log(format(html, $(this).attr('id')));
|
console.log(format(html, $(this).attr('id')));
|
||||||
$(this).append(format(html, $(this).attr('id')));
|
$(this).append(format(html, $(this).attr('id')));
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('input.searchbar').each(function () {
|
||||||
|
var $form = $(this).closest('form');
|
||||||
|
$(this).autocomplete({
|
||||||
|
minLength: 3,
|
||||||
|
width: 300,
|
||||||
|
source: function (request, response) {
|
||||||
|
$.getJSON(
|
||||||
|
$form.attr('data-search-url') + '?' + $form.serialize(),
|
||||||
|
response,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
focus: function (event, ui) {
|
||||||
|
$(this).val(ui.item.label);
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
select: function (event, ui) {
|
||||||
|
window.location = format($form.attr('action'), { id: ui.item.value });
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
$form.on(
|
||||||
|
'submit',
|
||||||
|
_pd(function () {
|
||||||
|
// Prevent just submitting the form because that takes you
|
||||||
|
// to your page. TODO: do something clever with this.
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function() {
|
// Recalculate Hash
|
||||||
$('input.searchbar').each(function() {
|
$('.recalc').click(
|
||||||
var $form = $(this).closest('form');
|
_pd(function () {
|
||||||
$(this).autocomplete({
|
var $this = $(this);
|
||||||
minLength: 3,
|
$this.html('Recalcing…');
|
||||||
width: 300,
|
$.post($this.attr('href'), function (d) {
|
||||||
source: function(request, response) {
|
if (d.success) {
|
||||||
$.getJSON($form.attr('data-search-url') + '?' + $form.serialize(),
|
$this.text('Done!');
|
||||||
response);
|
} else {
|
||||||
},
|
$this.text('Error :(');
|
||||||
focus: function(event, ui) {
|
}
|
||||||
$(this).val(ui.item.label);
|
setTimeout(function () {
|
||||||
event.preventDefault();
|
$this.text('Recalc Hash');
|
||||||
},
|
}, 2000);
|
||||||
select: function(event, ui) {
|
|
||||||
window.location = format($form.attr('action'), {id: ui.item.value});
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$form.on('submit', _pd(function() {
|
|
||||||
// Prevent just submitting the form because that takes you
|
|
||||||
// to your page. TODO: do something clever with this.
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
}),
|
||||||
// Recalculate Hash
|
);
|
||||||
$('.recalc').click(_pd(function() {
|
});
|
||||||
var $this = $(this);
|
$('#id_start, #id_end').datepicker({ dateFormat: 'yy-mm-dd' });
|
||||||
$this.html('Recalcing…');
|
|
||||||
$.post($this.attr('href'), function(d) {
|
|
||||||
if(d.success) {
|
|
||||||
$this.text('Done!');
|
|
||||||
} else {
|
|
||||||
$this.text('Error :(');
|
|
||||||
}
|
|
||||||
setTimeout(function() {
|
|
||||||
$this.text('Recalc Hash');
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
$('#id_start, #id_end').datepicker({ dateFormat: 'yy-mm-dd' });
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,102 +1,135 @@
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
function incTotalForms() {
|
function incTotalForms() {
|
||||||
var $totalForms = $('#id_form-TOTAL_FORMS'),
|
var $totalForms = $('#id_form-TOTAL_FORMS'),
|
||||||
num = parseInt($totalForms.val()) + 1;
|
num = parseInt($totalForms.val()) + 1;
|
||||||
$totalForms.val(num);
|
$totalForms.val(num);
|
||||||
return num;
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate cells with current collections.
|
||||||
|
$('#features td.collection').each(function () {
|
||||||
|
var $td = $(this),
|
||||||
|
cid = $td.attr('data-collection'),
|
||||||
|
$input = $td.find('.collection-ac');
|
||||||
|
if (!cid) {
|
||||||
|
$td.removeClass('loading');
|
||||||
|
$input.show();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
$.post(
|
||||||
|
document.body.getAttribute('data-featured-collection-url'),
|
||||||
|
{ collection: cid },
|
||||||
|
function (data) {
|
||||||
|
$td.removeClass('loading');
|
||||||
|
$input.hide();
|
||||||
|
$td.find('.current-collection').html(data).show();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Populate cells with current collections.
|
$('#features').on('change', '.app select', function () {
|
||||||
$('#features td.collection').each(function() {
|
// Update application id and toggle disabled attr on autocomplete field.
|
||||||
var $td = $(this),
|
var $this = $(this),
|
||||||
cid = $td.attr('data-collection'),
|
$tr = $this.closest('tr'),
|
||||||
$input = $td.find('.collection-ac');
|
val = $this.val();
|
||||||
if (!cid) {
|
$tr.attr('data-app', val);
|
||||||
$td.removeClass('loading');
|
$tr.find('.collection-ac').prop('disabled', !val);
|
||||||
$input.show();
|
});
|
||||||
return;
|
$('#features').on(
|
||||||
}
|
'click',
|
||||||
$.post(document.body.getAttribute('data-featured-collection-url'),
|
'.remove',
|
||||||
{'collection': cid}, function(data) {
|
_pd(function () {
|
||||||
$td.removeClass('loading');
|
$(this).closest('tr').hide();
|
||||||
$input.hide();
|
$(this).closest('td').find('input').prop('checked', true);
|
||||||
$td.find('.current-collection').html(data).show();
|
}),
|
||||||
});
|
);
|
||||||
});
|
$('#features')
|
||||||
|
.on(
|
||||||
$('#features').on('change', '.app select', function() {
|
'click',
|
||||||
// Update application id and toggle disabled attr on autocomplete field.
|
'.replace',
|
||||||
var $this = $(this),
|
_pd(function () {
|
||||||
$tr = $this.closest('tr'),
|
|
||||||
val = $this.val();
|
|
||||||
$tr.attr('data-app', val);
|
|
||||||
$tr.find('.collection-ac').prop('disabled', !val);
|
|
||||||
});
|
|
||||||
$('#features').on('click', '.remove', _pd(function() {
|
|
||||||
$(this).closest('tr').hide();
|
|
||||||
$(this).closest('td').find('input').prop('checked', true);
|
|
||||||
}));
|
|
||||||
$('#features').on('click', '.replace', _pd(function() {
|
|
||||||
var $td = $(this).closest('td');
|
var $td = $(this).closest('td');
|
||||||
$td.find('.collection-ac').show();
|
$td.find('.collection-ac').show();
|
||||||
$td.find('input[type=hidden]').val('');
|
$td.find('input[type=hidden]').val('');
|
||||||
$(this).parent().html('');
|
$(this).parent().html('');
|
||||||
})).on('collectionAdd', '.collection-ac', function() {
|
}),
|
||||||
// Autocomplete for collection add form.
|
)
|
||||||
var $input = $(this),
|
.on('collectionAdd', '.collection-ac', function () {
|
||||||
$tr = $input.closest('tr'),
|
// Autocomplete for collection add form.
|
||||||
$td = $input.closest('td'),
|
var $input = $(this),
|
||||||
$select = $tr.find('.collection-select');
|
$tr = $input.closest('tr'),
|
||||||
function selectCollection() {
|
$td = $input.closest('td'),
|
||||||
var item = JSON.parse($input.attr('data-item'));
|
$select = $tr.find('.collection-select');
|
||||||
if (item) {
|
function selectCollection() {
|
||||||
$td.find('.errorlist').remove();
|
var item = JSON.parse($input.attr('data-item'));
|
||||||
var current = template(
|
if (item) {
|
||||||
'<a href="{url}" ' +
|
$td.find('.errorlist').remove();
|
||||||
'class="collectionitem {is_personas}">{name} ({slug})</a>' +
|
var current = template(
|
||||||
'<a href="#" class="replace">Replace with another collection</a>'
|
'<a href="{url}" ' +
|
||||||
);
|
'class="collectionitem {is_personas}">{name} ({slug})</a>' +
|
||||||
$td.find('.current-collection').show().html(current({
|
'<a href="#" class="replace">Replace with another collection</a>',
|
||||||
url: item.url,
|
);
|
||||||
is_personas: item.all_personas ? 'personas-collection' : '',
|
$td
|
||||||
name: _.escape(item.name),
|
.find('.current-collection')
|
||||||
slug: _.escape(item.slug),
|
.show()
|
||||||
}));
|
.html(
|
||||||
$td.find('input[type=hidden]').val(item.id);
|
current({
|
||||||
$td.attr('data-collection', item.id);
|
url: item.url,
|
||||||
}
|
is_personas: item.all_personas ? 'personas-collection' : '',
|
||||||
$input.val('');
|
name: _.escape(item.name),
|
||||||
$input.hide();
|
slug: _.escape(item.slug),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
$td.find('input[type=hidden]').val(item.id);
|
||||||
|
$td.attr('data-collection', item.id);
|
||||||
}
|
}
|
||||||
$input.autocomplete({
|
$input.val('');
|
||||||
minLength: 3,
|
$input.hide();
|
||||||
width: 300,
|
}
|
||||||
source: function(request, response) {
|
$input
|
||||||
$.getJSON(document.body.getAttribute('data-collections-url'),
|
.autocomplete({
|
||||||
{'app': $input.closest('tr').attr('data-app'),
|
minLength: 3,
|
||||||
'q': request.term}, response);
|
width: 300,
|
||||||
},
|
source: function (request, response) {
|
||||||
focus: function(event, ui) {
|
$.getJSON(
|
||||||
$input.val(ui.item.name);
|
document.body.getAttribute('data-collections-url'),
|
||||||
return false;
|
{ app: $input.closest('tr').attr('data-app'), q: request.term },
|
||||||
},
|
response,
|
||||||
select: function(event, ui) {
|
);
|
||||||
$input.val(ui.item.name).attr('data-item', JSON.stringify(ui.item));
|
},
|
||||||
selectCollection();
|
focus: function (event, ui) {
|
||||||
return false;
|
$input.val(ui.item.name);
|
||||||
}
|
return false;
|
||||||
}).data('ui-autocomplete')._renderItem = function(ul, item) {
|
},
|
||||||
var html = format('<a>{0} ({1})<b>ID: {2}</b></a>', [_.escape(item.name), _.escape(item.slug), item.id]);
|
select: function (event, ui) {
|
||||||
return $('<li>').data('item.autocomplete', item).append(html).appendTo(ul);
|
$input.val(ui.item.name).attr('data-item', JSON.stringify(ui.item));
|
||||||
};
|
selectCollection();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.data('ui-autocomplete')._renderItem = function (ul, item) {
|
||||||
|
var html = format('<a>{0} ({1})<b>ID: {2}</b></a>', [
|
||||||
|
_.escape(item.name),
|
||||||
|
_.escape(item.slug),
|
||||||
|
item.id,
|
||||||
|
]);
|
||||||
|
return $('<li>')
|
||||||
|
.data('item.autocomplete', item)
|
||||||
|
.append(html)
|
||||||
|
.appendTo(ul);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#features .collection-ac').trigger('collectionAdd');
|
$('#features .collection-ac').trigger('collectionAdd');
|
||||||
|
|
||||||
$('#add').click(_pd(function() {
|
$('#add').click(
|
||||||
var formId = incTotalForms() - 1,
|
_pd(function () {
|
||||||
emptyForm = $('tfoot').html().replace(/__prefix__/g, formId);
|
var formId = incTotalForms() - 1,
|
||||||
$('tbody').append(emptyForm);
|
emptyForm = $('tfoot')
|
||||||
$('tbody tr:last-child .collection-ac').trigger('collectionAdd');
|
.html()
|
||||||
}));
|
.replace(/__prefix__/g, formId);
|
||||||
|
$('tbody').append(emptyForm);
|
||||||
|
$('tbody tr:last-child .collection-ac').trigger('collectionAdd');
|
||||||
|
}),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,171 +1,193 @@
|
||||||
(function() {
|
(function () {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
$(function() {
|
$(function () {
|
||||||
if ($('#admin-validation').length) {
|
if ($('#admin-validation').length) {
|
||||||
initAdminValidation($('#admin-validation'));
|
initAdminValidation($('#admin-validation'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function initAdminValidation(doc) {
|
||||||
function initAdminValidation(doc) {
|
|
||||||
var $elem = $('#id_application', doc),
|
var $elem = $('#id_application', doc),
|
||||||
statInterval,
|
statInterval,
|
||||||
incompleteJobs = {};
|
incompleteJobs = {};
|
||||||
|
|
||||||
$elem.change(function(e) {
|
$elem.change(function (e) {
|
||||||
var maxVer = $('#id_curr_max_version, #id_target_version', doc),
|
var maxVer = $('#id_curr_max_version, #id_target_version', doc),
|
||||||
sel = $(e.target),
|
sel = $(e.target),
|
||||||
appId = $('option:selected', sel).val();
|
appId = $('option:selected', sel).val();
|
||||||
|
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
$('option', maxVer).remove();
|
$('option', maxVer).remove();
|
||||||
maxVer.append(format('<option value="{0}">{1}</option>',
|
maxVer.append(
|
||||||
['', gettext('Select an application first')]));
|
format('<option value="{0}">{1}</option>', [
|
||||||
return;
|
'',
|
||||||
}
|
gettext('Select an application first'),
|
||||||
$.post(sel.attr('data-url'), {'application': appId}, function(d) {
|
]),
|
||||||
$('option', maxVer).remove();
|
);
|
||||||
$.each(d.choices, function(i, ch) {
|
return;
|
||||||
maxVer.append(format('<option value="{0}">{1}</option>',
|
}
|
||||||
[ch[0], ch[1]]));
|
$.post(sel.attr('data-url'), { application: appId }, function (d) {
|
||||||
});
|
$('option', maxVer).remove();
|
||||||
|
$.each(d.choices, function (i, ch) {
|
||||||
|
maxVer.append(
|
||||||
|
format('<option value="{0}">{1}</option>', [ch[0], ch[1]]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($elem.children('option:selected').val() &&
|
if (
|
||||||
!$('#id_curr_max_version option:selected, ' +
|
$elem.children('option:selected').val() &&
|
||||||
'#id_target_version option:selected', doc).val()) {
|
!$(
|
||||||
// If an app is selected when page loads and it's not a form post.
|
'#id_curr_max_version option:selected, ' +
|
||||||
$elem.trigger('change');
|
'#id_target_version option:selected',
|
||||||
|
doc,
|
||||||
|
).val()
|
||||||
|
) {
|
||||||
|
// If an app is selected when page loads and it's not a form post.
|
||||||
|
$elem.trigger('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
var $popup = $('#notify').popup('td a.v-popup', {
|
var $popup = $('#notify').popup('td a.v-popup', {
|
||||||
width: '600px',
|
width: '600px',
|
||||||
callback: function(obj) {
|
callback: function (obj) {
|
||||||
var $ct = $(obj.click_target),
|
var $ct = $(obj.click_target),
|
||||||
msg = '',
|
msg = '',
|
||||||
// L10n: {0} is the number of add-ons, {1} is a version like 4.0
|
// L10n: {0} is the number of add-ons, {1} is a version like 4.0
|
||||||
msg = ngettext('Set {0} add-on to a max version of {1} and email the author.',
|
msg =
|
||||||
'Set {0} add-ons to a max version of {1} and email the authors.',
|
ngettext(
|
||||||
$ct.attr('data-job-count-passing')) + ' ' +
|
'Set {0} add-on to a max version of {1} and email the author.',
|
||||||
ngettext('Email author of {2} add-on which failed validation.',
|
'Set {0} add-ons to a max version of {1} and email the authors.',
|
||||||
'Email authors of {2} add-ons which failed validation.',
|
$ct.attr('data-job-count-passing'),
|
||||||
$ct.attr('data-job-count-failing'));
|
) +
|
||||||
|
' ' +
|
||||||
|
ngettext(
|
||||||
|
'Email author of {2} add-on which failed validation.',
|
||||||
|
'Email authors of {2} add-ons which failed validation.',
|
||||||
|
$ct.attr('data-job-count-failing'),
|
||||||
|
);
|
||||||
|
|
||||||
msg = format(msg, [$ct.attr('data-job-count-passing'), $ct.attr('data-job-version'),
|
msg = format(msg, [
|
||||||
$ct.attr('data-job-count-failing')]);
|
$ct.attr('data-job-count-passing'),
|
||||||
$(this).find('p.error').text(''); // clear any existing errors.
|
$ct.attr('data-job-version'),
|
||||||
$(this).find('p').eq(0).text(msg);
|
$ct.attr('data-job-count-failing'),
|
||||||
$(this).children('form').attr('action', $ct.attr('data-job-url'));
|
]);
|
||||||
return { pointTo: $ct };
|
$(this).find('p.error').text(''); // clear any existing errors.
|
||||||
}
|
$(this).find('p').eq(0).text(msg);
|
||||||
|
$(this).children('form').attr('action', $ct.attr('data-job-url'));
|
||||||
|
return { pointTo: $ct };
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#notify form').submit(function(e) {
|
$('#notify form').submit(function (e) {
|
||||||
var $form = $(this);
|
var $form = $(this);
|
||||||
if ($form.attr('data-valid') != 'valid') {
|
if ($form.attr('data-valid') != 'valid') {
|
||||||
$.post($form.attr('data-url'), $(this).serialize(), function(json) {
|
$.post($form.attr('data-url'), $(this).serialize(), function (json) {
|
||||||
if (json && json.valid) {
|
if (json && json.valid) {
|
||||||
$form.attr('data-valid', 'valid').submit();
|
$form.attr('data-valid', 'valid').submit();
|
||||||
} else {
|
} else {
|
||||||
$form.find('p.error').text(json.error).show();
|
$form.find('p.error').text(json.error).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('#notify form span.cancel a').click(_pd(function() {
|
$('#notify form span.cancel a').click(
|
||||||
|
_pd(function () {
|
||||||
$popup.hideMe();
|
$popup.hideMe();
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
function startStats() {
|
function startStats() {
|
||||||
var incompleteJobIds = [],
|
var incompleteJobIds = [],
|
||||||
checkStatus;
|
checkStatus;
|
||||||
$('tr.job-result').each(function(i, el) {
|
$('tr.job-result').each(function (i, el) {
|
||||||
var $el = $(el),
|
var $el = $(el),
|
||||||
$td = $el.children('td.tests-finished'),
|
$td = $el.children('td.tests-finished'),
|
||||||
isComplete = parseInt($el.attr('data-is-complete'), 10),
|
isComplete = parseInt($el.attr('data-is-complete'), 10),
|
||||||
jobId = parseInt($el.attr('data-job-id'), 10);
|
jobId = parseInt($el.attr('data-job-id'), 10);
|
||||||
if (!isComplete) {
|
if (!isComplete) {
|
||||||
incompleteJobIds.push(jobId);
|
incompleteJobIds.push(jobId);
|
||||||
incompleteJobs[jobId] = $td;
|
incompleteJobs[jobId] = $td;
|
||||||
createProgressBar($td);
|
createProgressBar($td);
|
||||||
}
|
|
||||||
});
|
|
||||||
if (incompleteJobIds.length) {
|
|
||||||
var checkStatus = function() {
|
|
||||||
$('#admin-validation').trigger('checkstats', [incompleteJobIds]);
|
|
||||||
};
|
|
||||||
checkStatus();
|
|
||||||
statInterval = setInterval(checkStatus, 3000);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
if (incompleteJobIds.length) {
|
||||||
|
var checkStatus = function () {
|
||||||
|
$('#admin-validation').trigger('checkstats', [incompleteJobIds]);
|
||||||
|
};
|
||||||
|
checkStatus();
|
||||||
|
statInterval = setInterval(checkStatus, 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startStats();
|
startStats();
|
||||||
|
|
||||||
$('td').on('receivestats', function(ev, stats) {
|
$('td').on('receivestats', function (ev, stats) {
|
||||||
var $el = $(this),
|
var $el = $(this),
|
||||||
$tr = $el.parent(),
|
$tr = $el.parent(),
|
||||||
complete = stats.percent_complete;
|
complete = stats.percent_complete;
|
||||||
$tr.children('td.tested').text(stats.total);
|
$tr.children('td.tested').text(stats.total);
|
||||||
$tr.children('td.failing').text(stats.failing);
|
$tr.children('td.failing').text(stats.failing);
|
||||||
$tr.children('td.passing').text(stats.passing);
|
$tr.children('td.passing').text(stats.passing);
|
||||||
$tr.children('td.exceptions').text(stats.errors);
|
$tr.children('td.exceptions').text(stats.errors);
|
||||||
$('.job-status-bar div', $el).animate({'width': complete + '%'},
|
$('.job-status-bar div', $el).animate(
|
||||||
{duration: 500});
|
{ width: complete + '%' },
|
||||||
if (stats.completed_timestamp != '') {
|
{ duration: 500 },
|
||||||
delete incompleteJobs[stats.job_id];
|
);
|
||||||
$('.job-status-bar', $el).remove();
|
if (stats.completed_timestamp != '') {
|
||||||
$el.text(stats.completed_timestamp);
|
delete incompleteJobs[stats.job_id];
|
||||||
jobCompleted();
|
$('.job-status-bar', $el).remove();
|
||||||
}
|
$el.text(stats.completed_timestamp);
|
||||||
|
jobCompleted();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#admin-validation').on('checkstats', function(ev, job_ids) {
|
$('#admin-validation').on('checkstats', function (ev, job_ids) {
|
||||||
$.ajax({type: 'POST',
|
$.ajax({
|
||||||
url: $(this).attr('data-status-url'),
|
type: 'POST',
|
||||||
data: {job_ids: JSON.stringify(job_ids)},
|
url: $(this).attr('data-status-url'),
|
||||||
cache: false,
|
data: { job_ids: JSON.stringify(job_ids) },
|
||||||
success: function(data) {
|
cache: false,
|
||||||
$.each(data, function(jobId, stats) {
|
success: function (data) {
|
||||||
if (incompleteJobs[jobId]) {
|
$.each(data, function (jobId, stats) {
|
||||||
incompleteJobs[jobId].trigger('receivestats', [stats]);
|
if (incompleteJobs[jobId]) {
|
||||||
} else {
|
incompleteJobs[jobId].trigger('receivestats', [stats]);
|
||||||
if (typeof console !== 'undefined')
|
} else {
|
||||||
console.log('checkstats: Job ID does not exist: ' + jobId);
|
if (typeof console !== 'undefined')
|
||||||
}
|
console.log('checkstats: Job ID does not exist: ' + jobId);
|
||||||
});
|
}
|
||||||
},
|
});
|
||||||
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
},
|
||||||
if (typeof console !== 'undefined')
|
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
||||||
console.log('error: ' + textStatus);
|
if (typeof console !== 'undefined')
|
||||||
},
|
console.log('error: ' + textStatus);
|
||||||
dataType: 'json'
|
},
|
||||||
});
|
dataType: 'json',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function createProgressBar($el) {
|
function createProgressBar($el) {
|
||||||
var bar = {};
|
var bar = {};
|
||||||
bar.progress_outside = $('<div>', {'class': 'job-status-bar'});
|
bar.progress_outside = $('<div>', { class: 'job-status-bar' });
|
||||||
bar.progress_inside = $('<div>').css('width', 0);
|
bar.progress_inside = $('<div>').css('width', 0);
|
||||||
bar.progress_outside.append(bar.progress_inside);
|
bar.progress_outside.append(bar.progress_inside);
|
||||||
$el.append(bar.progress_outside);
|
$el.append(bar.progress_outside);
|
||||||
bar.progress_outside.show();
|
bar.progress_outside.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function jobCompleted() {
|
function jobCompleted() {
|
||||||
var allDone = true;
|
var allDone = true;
|
||||||
$.each(incompleteJobs, function(jobId, el) {
|
$.each(incompleteJobs, function (jobId, el) {
|
||||||
allDone = false;
|
allDone = false;
|
||||||
});
|
});
|
||||||
if (allDone) {
|
if (allDone) {
|
||||||
clearInterval(statInterval);
|
clearInterval(statInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -9,7 +9,7 @@ function isDoNotTrackEnabled() {
|
||||||
if (dnt === '1') {
|
if (dnt === '1') {
|
||||||
window.console &&
|
window.console &&
|
||||||
console.info(
|
console.info(
|
||||||
'[TRACKING]: Do Not Track Enabled; Google Analytics will not be loaded.'
|
'[TRACKING]: Do Not Track Enabled; Google Analytics will not be loaded.',
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ if (isDoNotTrackEnabled() === false) {
|
||||||
document,
|
document,
|
||||||
'script',
|
'script',
|
||||||
'https://www.google-analytics.com/analytics.js',
|
'https://www.google-analytics.com/analytics.js',
|
||||||
'ga'
|
'ga',
|
||||||
);
|
);
|
||||||
|
|
||||||
ga('create', 'UA-36116321-7', 'auto');
|
ga('create', 'UA-36116321-7', 'auto');
|
||||||
|
|
|
@ -1,163 +1,159 @@
|
||||||
/* Browser Utilities
|
/* Browser Utilities
|
||||||
* Based on amo2009/addons.js
|
* Based on amo2009/addons.js
|
||||||
**/
|
**/
|
||||||
|
|
||||||
function BrowserUtils() {
|
function BrowserUtils() {
|
||||||
"use strict";
|
'use strict';
|
||||||
|
|
||||||
var userAgentStrings = {
|
var userAgentStrings = {
|
||||||
'firefox' : /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/,
|
firefox: /^Mozilla.*(Firefox|Minefield|Namoroka|Shiretoko|GranParadiso|BonEcho|Iceweasel|Fennec|MozillaDeveloperPreview)\/([^\s]*).*$/,
|
||||||
'seamonkey': /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/,
|
seamonkey: /^Mozilla.*(SeaMonkey|Iceape)\/([^\s]*).*$/,
|
||||||
'mobile': /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/,
|
mobile: /^Mozilla.*(Fennec|Mobile)\/([^\s]*)$/,
|
||||||
'thunderbird': /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/
|
thunderbird: /^Mozilla.*(Thunderbird|Shredder|Lanikai)\/([^\s*]*).*$/,
|
||||||
},
|
},
|
||||||
osStrings = {
|
osStrings = {
|
||||||
'windows': /Windows/,
|
windows: /Windows/,
|
||||||
'mac': /Mac/,
|
mac: /Mac/,
|
||||||
'linux': /Linux|BSD/,
|
linux: /Linux|BSD/,
|
||||||
'android': /Android/,
|
android: /Android/,
|
||||||
};
|
|
||||||
|
|
||||||
// browser detection
|
|
||||||
var browser = {},
|
|
||||||
browserVersion = '',
|
|
||||||
pattern, match, i,
|
|
||||||
badBrowser = true;
|
|
||||||
for (i in userAgentStrings) {
|
|
||||||
if (userAgentStrings.hasOwnProperty(i)) {
|
|
||||||
pattern = userAgentStrings[i];
|
|
||||||
match = pattern.exec(navigator.userAgent);
|
|
||||||
browser[i] = !!(match && match.length === 3);
|
|
||||||
if (browser[i]) {
|
|
||||||
browserVersion = escape_(match[2]);
|
|
||||||
badBrowser = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seamonkey looks like Firefox but Firefox doesn't look like Seamonkey.
|
|
||||||
// If both are true, set Firefox to false.
|
|
||||||
if (browser.firefox && browser.seamonkey) {
|
|
||||||
browser.firefox = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var os = {},
|
|
||||||
platform = "";
|
|
||||||
for (i in osStrings) {
|
|
||||||
if (osStrings.hasOwnProperty(i)) {
|
|
||||||
pattern = osStrings[i];
|
|
||||||
os[i] = pattern.test(navigator.userAgent);
|
|
||||||
if (os[i]) {
|
|
||||||
platform = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!platform) {
|
|
||||||
os['other'] = !platform;
|
|
||||||
platform = "other";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"browser": browser,
|
|
||||||
"browserVersion": browserVersion,
|
|
||||||
"badBrowser": badBrowser,
|
|
||||||
"os": os,
|
|
||||||
"platform": platform,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// browser detection
|
||||||
|
var browser = {},
|
||||||
|
browserVersion = '',
|
||||||
|
pattern,
|
||||||
|
match,
|
||||||
|
i,
|
||||||
|
badBrowser = true;
|
||||||
|
for (i in userAgentStrings) {
|
||||||
|
if (userAgentStrings.hasOwnProperty(i)) {
|
||||||
|
pattern = userAgentStrings[i];
|
||||||
|
match = pattern.exec(navigator.userAgent);
|
||||||
|
browser[i] = !!(match && match.length === 3);
|
||||||
|
if (browser[i]) {
|
||||||
|
browserVersion = escape_(match[2]);
|
||||||
|
badBrowser = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seamonkey looks like Firefox but Firefox doesn't look like Seamonkey.
|
||||||
|
// If both are true, set Firefox to false.
|
||||||
|
if (browser.firefox && browser.seamonkey) {
|
||||||
|
browser.firefox = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var os = {},
|
||||||
|
platform = '';
|
||||||
|
for (i in osStrings) {
|
||||||
|
if (osStrings.hasOwnProperty(i)) {
|
||||||
|
pattern = osStrings[i];
|
||||||
|
os[i] = pattern.test(navigator.userAgent);
|
||||||
|
if (os[i]) {
|
||||||
|
platform = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!platform) {
|
||||||
|
os['other'] = !platform;
|
||||||
|
platform = 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
browser: browser,
|
||||||
|
browserVersion: browserVersion,
|
||||||
|
badBrowser: badBrowser,
|
||||||
|
os: os,
|
||||||
|
platform: platform,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var VersionCompare = {
|
var VersionCompare = {
|
||||||
/**
|
/**
|
||||||
* Mozilla-style version numbers comparison in Javascript
|
* Mozilla-style version numbers comparison in Javascript
|
||||||
* (JS-translated version of PHP versioncompare component)
|
* (JS-translated version of PHP versioncompare component)
|
||||||
* @return -1: a<b, 0: a==b, 1: a>b
|
* @return -1: a<b, 0: a==b, 1: a>b
|
||||||
*/
|
*/
|
||||||
compareVersions: function(a,b) {
|
compareVersions: function (a, b) {
|
||||||
var al = a.split('.'),
|
var al = a.split('.'),
|
||||||
bl = b.split('.'),
|
bl = b.split('.'),
|
||||||
ap, bp, r, i;
|
ap,
|
||||||
for (i=0; i<al.length || i<bl.length; i++) {
|
bp,
|
||||||
ap = (i<al.length ? al[i] : null);
|
r,
|
||||||
bp = (i<bl.length ? bl[i] : null);
|
i;
|
||||||
r = this.compareVersionParts(ap,bp);
|
for (i = 0; i < al.length || i < bl.length; i++) {
|
||||||
if (r !== 0)
|
ap = i < al.length ? al[i] : null;
|
||||||
return r;
|
bp = i < bl.length ? bl[i] : null;
|
||||||
}
|
r = this.compareVersionParts(ap, bp);
|
||||||
return 0;
|
if (r !== 0) return r;
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function: compare a single version part
|
|
||||||
*/
|
|
||||||
compareVersionParts: function(ap,bp) {
|
|
||||||
var avp = this.parseVersionPart(ap),
|
|
||||||
bvp = this.parseVersionPart(bp),
|
|
||||||
r = this.cmp(avp['numA'],bvp['numA']);
|
|
||||||
if (r) return r;
|
|
||||||
r = this.strcmp(avp['strB'],bvp['strB']);
|
|
||||||
if (r) return r;
|
|
||||||
r = this.cmp(avp['numC'],bvp['numC']);
|
|
||||||
if (r) return r;
|
|
||||||
return this.strcmp(avp['extraD'],bvp['extraD']);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function: parse a version part
|
|
||||||
*/
|
|
||||||
parseVersionPart: function(p) {
|
|
||||||
if (p == '*') {
|
|
||||||
return {
|
|
||||||
'numA' : Number.MAX_VALUE,
|
|
||||||
'strB' : '',
|
|
||||||
'numC' : 0,
|
|
||||||
'extraD' : ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var pattern = /^([-\d]*)([^-\d]*)([-\d]*)(.*)$/,
|
|
||||||
m = pattern.exec(p),
|
|
||||||
r = {
|
|
||||||
'numA' : parseInt(m[1], 10),
|
|
||||||
'strB' : m[2],
|
|
||||||
'numC' : parseInt(m[3], 10),
|
|
||||||
'extraD' : m[4]
|
|
||||||
};
|
|
||||||
if (r['strB'] == '+') {
|
|
||||||
r['numA']++;
|
|
||||||
r['strB'] = 'pre';
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function: compare numeric version parts
|
|
||||||
*/
|
|
||||||
cmp: function(an,bn) {
|
|
||||||
if (isNaN(an)) an = 0;
|
|
||||||
if (isNaN(bn)) bn = 0;
|
|
||||||
if (an < bn)
|
|
||||||
return -1;
|
|
||||||
if (an > bn)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper function: compare string version parts
|
|
||||||
*/
|
|
||||||
strcmp: function(as,bs) {
|
|
||||||
if (as == bs)
|
|
||||||
return 0;
|
|
||||||
// any string comes *before* the empty string
|
|
||||||
if (as === '')
|
|
||||||
return 1;
|
|
||||||
if (bs === '')
|
|
||||||
return -1;
|
|
||||||
// normal string comparison for non-empty strings (like strcmp)
|
|
||||||
if (as < bs)
|
|
||||||
return -1;
|
|
||||||
else if(as > bs)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function: compare a single version part
|
||||||
|
*/
|
||||||
|
compareVersionParts: function (ap, bp) {
|
||||||
|
var avp = this.parseVersionPart(ap),
|
||||||
|
bvp = this.parseVersionPart(bp),
|
||||||
|
r = this.cmp(avp['numA'], bvp['numA']);
|
||||||
|
if (r) return r;
|
||||||
|
r = this.strcmp(avp['strB'], bvp['strB']);
|
||||||
|
if (r) return r;
|
||||||
|
r = this.cmp(avp['numC'], bvp['numC']);
|
||||||
|
if (r) return r;
|
||||||
|
return this.strcmp(avp['extraD'], bvp['extraD']);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function: parse a version part
|
||||||
|
*/
|
||||||
|
parseVersionPart: function (p) {
|
||||||
|
if (p == '*') {
|
||||||
|
return {
|
||||||
|
numA: Number.MAX_VALUE,
|
||||||
|
strB: '',
|
||||||
|
numC: 0,
|
||||||
|
extraD: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var pattern = /^([-\d]*)([^-\d]*)([-\d]*)(.*)$/,
|
||||||
|
m = pattern.exec(p),
|
||||||
|
r = {
|
||||||
|
numA: parseInt(m[1], 10),
|
||||||
|
strB: m[2],
|
||||||
|
numC: parseInt(m[3], 10),
|
||||||
|
extraD: m[4],
|
||||||
|
};
|
||||||
|
if (r['strB'] == '+') {
|
||||||
|
r['numA']++;
|
||||||
|
r['strB'] = 'pre';
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function: compare numeric version parts
|
||||||
|
*/
|
||||||
|
cmp: function (an, bn) {
|
||||||
|
if (isNaN(an)) an = 0;
|
||||||
|
if (isNaN(bn)) bn = 0;
|
||||||
|
if (an < bn) return -1;
|
||||||
|
if (an > bn) return 1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper function: compare string version parts
|
||||||
|
*/
|
||||||
|
strcmp: function (as, bs) {
|
||||||
|
if (as == bs) return 0;
|
||||||
|
// any string comes *before* the empty string
|
||||||
|
if (as === '') return 1;
|
||||||
|
if (bs === '') return -1;
|
||||||
|
// normal string comparison for non-empty strings (like strcmp)
|
||||||
|
if (as < bs) return -1;
|
||||||
|
else if (as > bs) return 1;
|
||||||
|
else return 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,320 +1,361 @@
|
||||||
(function() {
|
(function () {
|
||||||
|
/* Call this with something like $('.install').installButton(); */
|
||||||
|
z.button = {};
|
||||||
|
|
||||||
/* Call this with something like $('.install').installButton(); */
|
/* A library of callbacks that may be run after InstallTrigger succeeds.
|
||||||
z.button = {};
|
* ``this`` will be bound to the .install button.
|
||||||
|
*/
|
||||||
/* A library of callbacks that may be run after InstallTrigger succeeds.
|
z.button.after = {
|
||||||
* ``this`` will be bound to the .install button.
|
contrib: function (xpi_url, status) {
|
||||||
*/
|
if (status === 0) {
|
||||||
z.button.after = {'contrib': function(xpi_url, status) {
|
//success
|
||||||
if (status === 0) { //success
|
|
||||||
document.location = $(this).attr('data-developers');
|
document.location = $(this).attr('data-developers');
|
||||||
}
|
}
|
||||||
}};
|
},
|
||||||
|
};
|
||||||
|
|
||||||
var notavail = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
var notavail =
|
||||||
incompat = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
'<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
||||||
noappsupport = '<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
incompat =
|
||||||
|
'<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
||||||
|
noappsupport =
|
||||||
|
'<div class="extra"><span class="button disabled not-available" disabled>{0}</span></div>',
|
||||||
download_re = new RegExp('(/downloads/(?:latest|file)/\\d+)');
|
download_re = new RegExp('(/downloads/(?:latest|file)/\\d+)');
|
||||||
|
|
||||||
// The lowest maxVersion an app has to support to allow default-to-compatible.
|
// The lowest maxVersion an app has to support to allow default-to-compatible.
|
||||||
var D2C_MAX_VERSIONS = {
|
var D2C_MAX_VERSIONS = {
|
||||||
firefox: '4.0',
|
firefox: '4.0',
|
||||||
mobile: '11.0',
|
mobile: '11.0',
|
||||||
seamonkey: '2.1',
|
seamonkey: '2.1',
|
||||||
thunderbird: '5.0'
|
thunderbird: '5.0',
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Called by the jQuery plugin to set up a single button. */
|
/* Called by the jQuery plugin to set up a single button. */
|
||||||
var installButton = function() {
|
var installButton = function () {
|
||||||
// Create a bunch of data and helper functions, then drive the buttons
|
// Create a bunch of data and helper functions, then drive the buttons
|
||||||
// based on the button type at the end.
|
// based on the button type at the end.
|
||||||
var self = this,
|
var self = this,
|
||||||
$this = $(this),
|
$this = $(this),
|
||||||
$button = $this.find('.button');
|
$button = $this.find('.button');
|
||||||
|
|
||||||
// Unreviewed and self-hosted buttons point to the add-on detail page for
|
// Unreviewed and self-hosted buttons point to the add-on detail page for
|
||||||
// non-js safety. Flip them to the real xpi url here.
|
// non-js safety. Flip them to the real xpi url here.
|
||||||
$button.each(function() {
|
$button.each(function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
if ($this.hasattr('data-realurl')) {
|
if ($this.hasattr('data-realurl')) {
|
||||||
$this.attr('href', $this.attr('data-realurl'));
|
$this.attr('href', $this.attr('data-realurl'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we're on the mobile site but it's not a mobile browser, force
|
/* If we're on the mobile site but it's not a mobile browser, force
|
||||||
* the download url to type:attachment.
|
* the download url to type:attachment.
|
||||||
*/
|
*/
|
||||||
if (z.app === 'mobile' && !z.appMatchesUserAgent) {
|
if (z.app === 'mobile' && !z.appMatchesUserAgent) {
|
||||||
var href = $this.attr('href');
|
var href = $this.attr('href');
|
||||||
$this.attr('href', href.replace(download_re, '$1/type:attachment'));
|
$this.attr('href', href.replace(download_re, '$1/type:attachment'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var addon = $this.attr('data-addon'),
|
var addon = $this.attr('data-addon'),
|
||||||
min = $this.attr('data-min'),
|
min = $this.attr('data-min'),
|
||||||
max = $this.attr('data-max'),
|
max = $this.attr('data-max'),
|
||||||
name = $this.attr('data-name'),
|
name = $this.attr('data-name'),
|
||||||
icon = $this.attr('data-icon'),
|
icon = $this.attr('data-icon'),
|
||||||
after = $this.attr('data-after'),
|
after = $this.attr('data-after'),
|
||||||
search = $this.hasattr('data-search'),
|
search = $this.hasattr('data-search'),
|
||||||
no_compat_necessary = $this.hasattr('data-no-compat-necessary'),
|
no_compat_necessary = $this.hasattr('data-no-compat-necessary'),
|
||||||
accept_eula = $this.hasClass('accept'),
|
accept_eula = $this.hasClass('accept'),
|
||||||
compatible = $this.attr('data-is-compatible-by-default') == 'true',
|
compatible = $this.attr('data-is-compatible-by-default') == 'true',
|
||||||
compatible_app = $this.attr('data-is-compatible-app') == 'true',
|
compatible_app = $this.attr('data-is-compatible-app') == 'true',
|
||||||
has_overrides = $this.hasattr('data-compat-overrides'),
|
has_overrides = $this.hasattr('data-compat-overrides'),
|
||||||
versions_url = $this.attr('data-versions'),
|
versions_url = $this.attr('data-versions'),
|
||||||
// L10n: {0} is an app name like Firefox.
|
// L10n: {0} is an app name like Firefox.
|
||||||
_s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'),
|
_s = accept_eula ? gettext('Accept and Install') : gettext('Add to {0}'),
|
||||||
addto = format(_s, [z.appName]),
|
addto = format(_s, [z.appName]),
|
||||||
appSupported = z.appMatchesUserAgent && min && max,
|
appSupported = z.appMatchesUserAgent && min && max,
|
||||||
$body = $(document.body),
|
$body = $(document.body),
|
||||||
olderBrowser,
|
olderBrowser,
|
||||||
newerBrowser;
|
newerBrowser;
|
||||||
|
|
||||||
// If we have os-specific buttons, check that one of them matches the
|
// If we have os-specific buttons, check that one of them matches the
|
||||||
// current platform.
|
// current platform.
|
||||||
var badPlatform = ($button.find('.os').length &&
|
var badPlatform =
|
||||||
!$button.hasClass(z.platform));
|
$button.find('.os').length && !$button.hasClass(z.platform);
|
||||||
|
|
||||||
// min and max only exist if the add-on is compatible with request[APP].
|
// min and max only exist if the add-on is compatible with request[APP].
|
||||||
if (appSupported) {
|
if (appSupported) {
|
||||||
// The user *has* an older/newer browser.
|
// The user *has* an older/newer browser.
|
||||||
olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0;
|
olderBrowser = VersionCompare.compareVersions(z.browserVersion, min) < 0;
|
||||||
newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0;
|
newerBrowser = VersionCompare.compareVersions(z.browserVersion, max) > 0;
|
||||||
if (olderBrowser) {
|
if (olderBrowser) {
|
||||||
// Make sure we show the "Not available for ..." messaging.
|
// Make sure we show the "Not available for ..." messaging.
|
||||||
compatible = false;
|
compatible = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to compatible checking.
|
// Default to compatible checking.
|
||||||
if (compatible) {
|
if (compatible) {
|
||||||
if (!compatible_app) {
|
if (!compatible_app) {
|
||||||
compatible = false;
|
|
||||||
}
|
|
||||||
// If it's still compatible, check the overrides.
|
|
||||||
if (compatible && has_overrides) {
|
|
||||||
var overrides = JSON.parse($this.attr('data-compat-overrides'));
|
|
||||||
_.each(overrides, function(override) {
|
|
||||||
var _min = override[0],
|
|
||||||
_max = override[1];
|
|
||||||
if (VersionCompare.compareVersions(z.browserVersion, _min) >= 0 &&
|
|
||||||
VersionCompare.compareVersions(z.browserVersion, _max) <= 0) {
|
|
||||||
compatible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
compatible = false;
|
compatible = false;
|
||||||
|
}
|
||||||
|
// If it's still compatible, check the overrides.
|
||||||
|
if (compatible && has_overrides) {
|
||||||
|
var overrides = JSON.parse($this.attr('data-compat-overrides'));
|
||||||
|
_.each(overrides, function (override) {
|
||||||
|
var _min = override[0],
|
||||||
|
_max = override[1];
|
||||||
|
if (
|
||||||
|
VersionCompare.compareVersions(z.browserVersion, _min) >= 0 &&
|
||||||
|
VersionCompare.compareVersions(z.browserVersion, _max) <= 0
|
||||||
|
) {
|
||||||
|
compatible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
compatible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addWarning = function(msg, type) {
|
var addWarning = function (msg, type) {
|
||||||
$this.parent().append(format(type || notavail, [msg]));
|
$this.parent().append(format(type || notavail, [msg]));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Change the button text to "Add to Firefox".
|
// Change the button text to "Add to Firefox".
|
||||||
var addToApp = function() {
|
var addToApp = function () {
|
||||||
if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) {
|
if (appSupported || (no_compat_necessary && z.appMatchesUserAgent)) {
|
||||||
$button.addClass('add').removeClass('download')
|
$button
|
||||||
.find('span').text(addto);
|
.addClass('add')
|
||||||
}
|
.removeClass('download')
|
||||||
|
.find('span')
|
||||||
|
.text(addto);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calls InstallTrigger.install or AddSearchProvider if we capture a click
|
// Calls InstallTrigger.install or AddSearchProvider if we capture a click
|
||||||
// on something with a .installer class.
|
// on something with a .installer class.
|
||||||
var clickHijack = function() {
|
var clickHijack = function () {
|
||||||
try {
|
try {
|
||||||
if (!appSupported && !no_compat_necessary || !("InstallTrigger" in window)) return;
|
if (
|
||||||
} catch (e) {
|
(!appSupported && !no_compat_necessary) ||
|
||||||
return;
|
!('InstallTrigger' in window)
|
||||||
}
|
)
|
||||||
|
return;
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this.addClass('clickHijack'); // So we can disable pointer events
|
$this.addClass('clickHijack'); // So we can disable pointer events
|
||||||
|
|
||||||
$this.on('mousedown focus', function(e) {
|
$this
|
||||||
$this.addClass('active');
|
.on('mousedown focus', function (e) {
|
||||||
}).on('mouseup blur', function(e) {
|
$this.addClass('active');
|
||||||
$this.removeClass('active');
|
})
|
||||||
}).click(function(e) {
|
.on('mouseup blur', function (e) {
|
||||||
// If the click was on a.installer or a child, call the special
|
$this.removeClass('active');
|
||||||
// install method. We can't bind this directly because we add
|
})
|
||||||
// more .installers dynamically.
|
.click(function (e) {
|
||||||
var $target = $(e.target),
|
// If the click was on a.installer or a child, call the special
|
||||||
$installer = '';
|
// install method. We can't bind this directly because we add
|
||||||
if ($target.hasClass('installer')) {
|
// more .installers dynamically.
|
||||||
installer = $target;
|
var $target = $(e.target),
|
||||||
} else {
|
$installer = '';
|
||||||
installer = $target.parents('.installer').first();
|
if ($target.hasClass('installer')) {
|
||||||
if (_.indexOf($this.find('.installer'), installer[0]) == -1) {
|
installer = $target;
|
||||||
return;
|
} else {
|
||||||
}
|
installer = $target.parents('.installer').first();
|
||||||
|
if (_.indexOf($this.find('.installer'), installer[0]) == -1) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
// map download url => file hash.
|
// map download url => file hash.
|
||||||
var hashes = {};
|
var hashes = {};
|
||||||
$this.find('.button[data-hash]').each(function() {
|
$this.find('.button[data-hash]').each(function () {
|
||||||
hashes[$(this).attr('href')] = $(this).attr('data-hash');
|
hashes[$(this).attr('href')] = $(this).attr('data-hash');
|
||||||
});
|
});
|
||||||
var hash = hashes[installer.attr('href')];
|
var hash = hashes[installer.attr('href')];
|
||||||
|
|
||||||
var f = _.haskey(z.button.after, after) ? z.button.after[after] : _.identity,
|
var f = _.haskey(z.button.after, after)
|
||||||
callback = _.bind(f, self),
|
? z.button.after[after]
|
||||||
install = search ? z.installSearch : z.installAddon;
|
: _.identity,
|
||||||
install(name, installer[0].href, icon, hash, callback);
|
callback = _.bind(f, self),
|
||||||
|
install = search ? z.installSearch : z.installAddon;
|
||||||
|
install(name, installer[0].href, icon, hash, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Gather the available platforms.
|
// Gather the available platforms.
|
||||||
var platforms = $button.map(function() {
|
var platforms = $button.map(function () {
|
||||||
var name = $(this).find('.os').attr('data-os'),
|
var name = $(this).find('.os').attr('data-os'),
|
||||||
text = z.appMatchesUserAgent ?
|
text = z.appMatchesUserAgent
|
||||||
/* L10n: {0} is an platform like Windows or Linux. */
|
? /* L10n: {0} is an platform like Windows or Linux. */
|
||||||
gettext('Install for {0} anyway') : gettext('Download for {0} anyway');
|
gettext('Install for {0} anyway')
|
||||||
return {
|
: gettext('Download for {0} anyway');
|
||||||
href: $(this).attr('href'),
|
return {
|
||||||
msg: format(text, [name])
|
href: $(this).attr('href'),
|
||||||
};
|
msg: format(text, [name]),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
var showDownloadAnyway = function($button) {
|
var showDownloadAnyway = function ($button) {
|
||||||
var $visibleButton = $button.filter(':visible')
|
var $visibleButton = $button.filter(':visible');
|
||||||
var $installShell = $visibleButton.parents('.install-shell');
|
var $installShell = $visibleButton.parents('.install-shell');
|
||||||
var $downloadAnyway = $visibleButton.next('.download-anyway');
|
var $downloadAnyway = $visibleButton.next('.download-anyway');
|
||||||
if ($downloadAnyway.length) {
|
if ($downloadAnyway.length) {
|
||||||
// We want to be able to add the download anyway link regardless
|
// We want to be able to add the download anyway link regardless
|
||||||
// of what is already shown. There could be just an error message,
|
// of what is already shown. There could be just an error message,
|
||||||
// or an error message plus a link to more versions. We also want
|
// or an error message plus a link to more versions. We also want
|
||||||
// those combinations to work without the download anyway link
|
// those combinations to work without the download anyway link
|
||||||
// being shown.
|
// being shown.
|
||||||
// Append a separator to the .more-versions element:
|
// Append a separator to the .more-versions element:
|
||||||
// if it's displayed we need to separate the download anyway link
|
// if it's displayed we need to separate the download anyway link
|
||||||
// from the text shown in that span.
|
// from the text shown in that span.
|
||||||
var $moreVersions = $installShell.find('.more-versions');
|
var $moreVersions = $installShell.find('.more-versions');
|
||||||
$moreVersions.append(' | ');
|
$moreVersions.append(' | ');
|
||||||
// In any case, add the download anyway link to the parent div.
|
// In any case, add the download anyway link to the parent div.
|
||||||
// It'll show up regardless of whether we are showing the more
|
// It'll show up regardless of whether we are showing the more
|
||||||
// versions link or not.
|
// versions link or not.
|
||||||
var $newParent = $installShell.find('.extra .not-available');
|
var $newParent = $installShell.find('.extra .not-available');
|
||||||
$newParent.append($downloadAnyway);
|
$newParent.append($downloadAnyway);
|
||||||
$downloadAnyway.show();
|
$downloadAnyway.show();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
// Add version and platform warnings. This is one
|
// Add version and platform warnings. This is one
|
||||||
// big function since we merge the messaging when bad platform and version
|
// big function since we merge the messaging when bad platform and version
|
||||||
// occur simultaneously.
|
// occur simultaneously.
|
||||||
var versionsAndPlatforms = function(options) {
|
var versionsAndPlatforms = function (options) {
|
||||||
var opts = $.extend({addWarning: true}, options);
|
var opts = $.extend({ addWarning: true }, options);
|
||||||
warn = opts.addWarning ? addWarning : _.identity;
|
warn = opts.addWarning ? addWarning : _.identity;
|
||||||
|
|
||||||
// Do badPlatform prep out here since we need it in all branches.
|
// Do badPlatform prep out here since we need it in all branches.
|
||||||
if (badPlatform) {
|
if (badPlatform) {
|
||||||
warn(gettext('Not available for your platform'));
|
warn(gettext('Not available for your platform'));
|
||||||
$button.addClass('concealed');
|
$button.addClass('concealed');
|
||||||
$button.first().css('display', 'inherit');
|
$button.first().css('display', 'inherit');
|
||||||
$button.closest('.item.addon').addClass('incompatible');
|
$button.closest('.item.addon').addClass('incompatible');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appSupported && !compatible && (olderBrowser || newerBrowser)) {
|
||||||
|
// L10n: {0} is an app name.
|
||||||
|
var msg = format(
|
||||||
|
gettext('This add-on is not compatible with your version of {0}.'),
|
||||||
|
[z.appName, z.browserVersion],
|
||||||
|
);
|
||||||
|
var tpl = template(
|
||||||
|
msg +
|
||||||
|
' <br/><span class="more-versions"><a href="{versions_url}">' +
|
||||||
|
gettext('View other versions') +
|
||||||
|
'</a></span>',
|
||||||
|
);
|
||||||
|
warn(tpl({ versions_url: versions_url }));
|
||||||
|
|
||||||
|
$button.closest('div').attr('data-version-supported', false);
|
||||||
|
$button.addClass('concealed');
|
||||||
|
$button.closest('.item.addon').addClass('incompatible');
|
||||||
|
if (!badPlatform) {
|
||||||
|
showDownloadAnyway($button);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appSupported && !compatible && (olderBrowser || newerBrowser)) {
|
return true;
|
||||||
// L10n: {0} is an app name.
|
} else if (!unreviewed && (appSupported || no_compat_necessary)) {
|
||||||
var msg = format(gettext('This add-on is not compatible with your version of {0}.'),
|
// Good version, good platform.
|
||||||
[z.appName, z.browserVersion]);
|
$button.addClass('installer');
|
||||||
var tpl = template(msg +
|
$button.closest('div').attr('data-version-supported', true);
|
||||||
' <br/><span class="more-versions"><a href="{versions_url}">' +
|
} else if (!appSupported) {
|
||||||
gettext('View other versions') + '</a></span>');
|
var msg =
|
||||||
warn(tpl({'versions_url': versions_url}));
|
min && max
|
||||||
|
? gettext('Works with {app} {min} - {max}')
|
||||||
$button.closest('div').attr('data-version-supported', false);
|
: gettext('Works with {app}');
|
||||||
$button.addClass('concealed');
|
var tpl = template(
|
||||||
$button.closest('.item.addon').addClass('incompatible');
|
msg +
|
||||||
if (!badPlatform) {
|
'<br/><span class="more-versions"><a href="{versions_url}">' +
|
||||||
showDownloadAnyway($button);
|
gettext('View other versions') +
|
||||||
}
|
'</a></span>',
|
||||||
|
);
|
||||||
return true;
|
var context = {
|
||||||
} else if (!unreviewed && (appSupported || no_compat_necessary)) {
|
app: z.appName,
|
||||||
// Good version, good platform.
|
min: min,
|
||||||
$button.addClass('installer');
|
max: max,
|
||||||
$button.closest('div').attr('data-version-supported', true);
|
versions_url: versions_url,
|
||||||
} else if (!appSupported) {
|
};
|
||||||
var msg = (min && max ?
|
addWarning(tpl(context), noappsupport);
|
||||||
gettext('Works with {app} {min} - {max}') :
|
if (!badPlatform) {
|
||||||
gettext('Works with {app}'));
|
showDownloadAnyway($button);
|
||||||
var tpl = template(msg +
|
|
||||||
'<br/><span class="more-versions"><a href="{versions_url}">' +
|
|
||||||
gettext('View other versions') + '</a></span>');
|
|
||||||
var context = {'app': z.appName, 'min': min, 'max': max,
|
|
||||||
'versions_url': versions_url};
|
|
||||||
addWarning(tpl(context), noappsupport);
|
|
||||||
if (!badPlatform) {
|
|
||||||
showDownloadAnyway($button);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// What kind of button are we dealing with?
|
// What kind of button are we dealing with?
|
||||||
var unreviewed = $this.hasClass('unreviewed'),
|
var unreviewed = $this.hasClass('unreviewed'),
|
||||||
contrib = $this.hasClass('contrib'),
|
contrib = $this.hasClass('contrib'),
|
||||||
eula = $this.hasClass('eula');
|
eula = $this.hasClass('eula');
|
||||||
|
|
||||||
// Drive the install button based on its type.
|
// Drive the install button based on its type.
|
||||||
if (eula || contrib) {
|
if (eula || contrib) {
|
||||||
versionsAndPlatforms();
|
versionsAndPlatforms();
|
||||||
} else if (z.appMatchesUserAgent) {
|
} else if (z.appMatchesUserAgent) {
|
||||||
clickHijack();
|
clickHijack();
|
||||||
addToApp();
|
addToApp();
|
||||||
var opts = no_compat_necessary ? {addWarning: false} : {};
|
var opts = no_compat_necessary ? { addWarning: false } : {};
|
||||||
versionsAndPlatforms(opts);
|
versionsAndPlatforms(opts);
|
||||||
} else if (z.app == 'firefox') {
|
} else if (z.app == 'firefox') {
|
||||||
$button.addClass('CTA');
|
$button.addClass('CTA');
|
||||||
$button.text(gettext('Only with Firefox \u2014 Get Firefox Now!'));
|
$button.text(gettext('Only with Firefox \u2014 Get Firefox Now!'));
|
||||||
$button.attr('href', 'https://www.mozilla.org/firefox/new/?scene=2&utm_source=addons.mozilla.org&utm_medium=referral&utm_campaign=non-fx-button#download-fx');
|
$button.attr(
|
||||||
$('#site-nonfx').hide();
|
'href',
|
||||||
|
'https://www.mozilla.org/firefox/new/?scene=2&utm_source=addons.mozilla.org&utm_medium=referral&utm_campaign=non-fx-button#download-fx',
|
||||||
|
);
|
||||||
|
$('#site-nonfx').hide();
|
||||||
} else if (z.app == 'thunderbird') {
|
} else if (z.app == 'thunderbird') {
|
||||||
versionsAndPlatforms();
|
versionsAndPlatforms();
|
||||||
} else {
|
} else {
|
||||||
clickHijack();
|
clickHijack();
|
||||||
addToApp();
|
addToApp();
|
||||||
versionsAndPlatforms();
|
versionsAndPlatforms();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
jQuery.fn.installButton = function() {
|
jQuery.fn.installButton = function () {
|
||||||
return this.each(installButton);
|
return this.each(installButton);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Install an XPI or a JAR (or something like that).
|
/* Install an XPI or a JAR (or something like that).
|
||||||
*
|
*
|
||||||
* hash and callback are optional. callback is triggered after the
|
* hash and callback are optional. callback is triggered after the
|
||||||
* installation is complete.
|
* installation is complete.
|
||||||
*/
|
*/
|
||||||
z.installAddon = function(name, url, icon, hash, callback) {
|
z.installAddon = function (name, url, icon, hash, callback) {
|
||||||
var params = {};
|
var params = {};
|
||||||
params[name] = {
|
params[name] = {
|
||||||
URL: url,
|
URL: url,
|
||||||
IconURL: icon,
|
IconURL: icon,
|
||||||
toString: function() { return url; }
|
toString: function () {
|
||||||
|
return url;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (hash) {
|
if (hash) {
|
||||||
params[name].Hash = hash;
|
params[name].Hash = hash;
|
||||||
}
|
}
|
||||||
// InstallTrigger is a Gecko API.
|
// InstallTrigger is a Gecko API.
|
||||||
InstallTrigger.install(params, callback);
|
InstallTrigger.install(params, callback);
|
||||||
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
|
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
z.installSearch = function (name, url, icon, hash, callback) {
|
||||||
z.installSearch = function(name, url, icon, hash, callback) {
|
|
||||||
if (window.external && window.external.AddSearchProvider) {
|
if (window.external && window.external.AddSearchProvider) {
|
||||||
window.external.AddSearchProvider(url);
|
window.external.AddSearchProvider(url);
|
||||||
callback();
|
callback();
|
||||||
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
|
ga('send', 'event', 'AMO Addon / Theme Installs', 'addon', name);
|
||||||
} else {
|
} else {
|
||||||
// Alert! Deal with it.
|
// Alert! Deal with it.
|
||||||
alert(gettext('Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.'));
|
alert(
|
||||||
|
gettext(
|
||||||
|
'Sorry, you need a Mozilla-based browser (such as Firefox) to install a search plugin.',
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -5,13 +5,15 @@
|
||||||
// context
|
// context
|
||||||
|
|
||||||
function debounce(fn, ms, ctxt) {
|
function debounce(fn, ms, ctxt) {
|
||||||
var ctx = ctxt || window;
|
var ctx = ctxt || window;
|
||||||
var to, del = ms, fun = fn;
|
var to,
|
||||||
return function () {
|
del = ms,
|
||||||
var args = arguments;
|
fun = fn;
|
||||||
clearTimeout(to);
|
return function () {
|
||||||
to = setTimeout(function() {
|
var args = arguments;
|
||||||
fun.apply(ctx, args);
|
clearTimeout(to);
|
||||||
}, del);
|
to = setTimeout(function () {
|
||||||
};
|
fun.apply(ctx, args);
|
||||||
};
|
}, del);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,40 +1,44 @@
|
||||||
google.load('search', '1', {'language' : $('html').attr('lang')});
|
google.load('search', '1', { language: $('html').attr('lang') });
|
||||||
google.setOnLoadCallback(function() {
|
google.setOnLoadCallback(function () {
|
||||||
var qry = $('.header-search input[name="q"]'),
|
var qry = $('.header-search input[name="q"]'),
|
||||||
opt = new google.search.DrawOptions();
|
opt = new google.search.DrawOptions();
|
||||||
|
|
||||||
opt.setInput(qry.get(0));
|
opt.setInput(qry.get(0));
|
||||||
sc = new google.search.CustomSearchControl('007182852441266509516:fnsg3w7luc4');
|
sc = new google.search.CustomSearchControl(
|
||||||
sc.setNoResultsString(gettext('No results found.'));
|
'007182852441266509516:fnsg3w7luc4',
|
||||||
sc.setSearchStartingCallback(null, function(sc, searcher, qry) {
|
);
|
||||||
sc.maxResultCount = 0;
|
sc.setNoResultsString(gettext('No results found.'));
|
||||||
});
|
sc.setSearchStartingCallback(null, function (sc, searcher, qry) {
|
||||||
|
sc.maxResultCount = 0;
|
||||||
|
});
|
||||||
|
|
||||||
sc.setSearchCompleteCallback(null, function(sc, searcher) {
|
sc.setSearchCompleteCallback(null, function (sc, searcher) {
|
||||||
if (searcher.results.length) {
|
if (searcher.results.length) {
|
||||||
var cur = searcher.cursor,
|
var cur = searcher.cursor,
|
||||||
total = parseInt(cur.estimatedResultCount, 10);
|
total = parseInt(cur.estimatedResultCount, 10);
|
||||||
if (total > sc.maxResultCount) {
|
if (total > sc.maxResultCount) {
|
||||||
sc.maxResultCount = total;
|
sc.maxResultCount = total;
|
||||||
$('#cse').show();
|
$('#cse').show();
|
||||||
window.scroll(0, 0);
|
window.scroll(0, 0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#resultcount').hide();
|
$('#resultcount').hide();
|
||||||
$('#no-devsearch-results').show();
|
$('#no-devsearch-results').show();
|
||||||
}
|
|
||||||
$(window).resize();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#cse').hide();
|
|
||||||
sc.draw('cse', opt);
|
|
||||||
sc.execute();
|
|
||||||
|
|
||||||
if (!qry.val()) {
|
|
||||||
$('#resultcount').show();
|
|
||||||
}
|
}
|
||||||
|
$(window).resize();
|
||||||
|
});
|
||||||
|
|
||||||
$('#searchbox').submit(_pd(function(e) {
|
$('#cse').hide();
|
||||||
sc.execute();
|
sc.draw('cse', opt);
|
||||||
}));
|
sc.execute();
|
||||||
|
|
||||||
|
if (!qry.val()) {
|
||||||
|
$('#resultcount').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#searchbox').submit(
|
||||||
|
_pd(function (e) {
|
||||||
|
sc.execute();
|
||||||
|
}),
|
||||||
|
);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -34,30 +34,35 @@ _.template(`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The following is the above commented template, pre-compiled. */
|
/* The following is the above commented template, pre-compiled. */
|
||||||
function syntaxhighlighter_template(obj){
|
function syntaxhighlighter_template(obj) {
|
||||||
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
|
var __t,
|
||||||
with(obj||{}){
|
__p = '',
|
||||||
__p+='\n <div class="syntaxhighlighter">\n <table border="0" cellpadding="0" cellspacing="0">\n <colgroup><col class="highlighter-column-line-numbers"/>\n <col class="highlighter-column-code"/></colgroup>\n <tbody>\n ';
|
__j = Array.prototype.join,
|
||||||
_.each(lines, function(line) {
|
print = function () {
|
||||||
__p+='\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#'+
|
__p += __j.call(arguments, '');
|
||||||
((__t=( line.id ))==null?'':_.escape(__t))+
|
};
|
||||||
'" id="'+
|
with (obj || {}) {
|
||||||
((__t=( line.id ))==null?'':_.escape(__t))+
|
__p +=
|
||||||
'"\n class="'+
|
'\n <div class="syntaxhighlighter">\n <table border="0" cellpadding="0" cellspacing="0">\n <colgroup><col class="highlighter-column-line-numbers"/>\n <col class="highlighter-column-code"/></colgroup>\n <tbody>\n ';
|
||||||
((__t=( line.class ))==null?'':_.escape(__t))+
|
_.each(lines, function (line) {
|
||||||
' original line line-number"\n data-linenumber="'+
|
__p +=
|
||||||
((__t=( line.number ))==null?'':_.escape(__t))+
|
'\n <tr class="tr-line">\n <td class="td-line-number">\n <a href="#' +
|
||||||
'"></a>\n </td>\n <td class="'+
|
((__t = line.id) == null ? '' : _.escape(__t)) +
|
||||||
((__t=( line.class ))==null?'':_.escape(__t))+
|
'" id="' +
|
||||||
' td-line-code alt'+
|
((__t = line.id) == null ? '' : _.escape(__t)) +
|
||||||
((__t=( line.number % 2 + 1))==null?'':_.escape(__t))+
|
'"\n class="' +
|
||||||
'"><span\n class="original line line-code">'+
|
((__t = line.class) == null ? '' : _.escape(__t)) +
|
||||||
((__t=(
|
' original line line-number"\n data-linenumber="' +
|
||||||
line.code
|
((__t = line.number) == null ? '' : _.escape(__t)) +
|
||||||
))==null?'':__t)+
|
'"></a>\n </td>\n <td class="' +
|
||||||
'</span></td>\n </tr>\n ';
|
((__t = line.class) == null ? '' : _.escape(__t)) +
|
||||||
})
|
' td-line-code alt' +
|
||||||
__p+='\n </tbody>\n </table>\n </div>\n';
|
((__t = (line.number % 2) + 1) == null ? '' : _.escape(__t)) +
|
||||||
}
|
'"><span\n class="original line line-code">' +
|
||||||
return __p;
|
((__t = line.code) == null ? '' : __t) +
|
||||||
|
'</span></td>\n </tr>\n ';
|
||||||
|
});
|
||||||
|
__p += '\n </tbody>\n </table>\n </div>\n';
|
||||||
|
}
|
||||||
|
return __p;
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,31 +1,33 @@
|
||||||
// CSRF Tokens
|
// CSRF Tokens
|
||||||
// Hijack the AJAX requests, and insert a CSRF token as a header.
|
// Hijack the AJAX requests, and insert a CSRF token as a header.
|
||||||
|
|
||||||
$(document).ajaxSend(function(event, xhr, ajaxSettings) {
|
$(document)
|
||||||
|
.ajaxSend(function (event, xhr, ajaxSettings) {
|
||||||
var csrf, $meta;
|
var csrf, $meta;
|
||||||
// Block anything that starts with 'http:', 'https:', '://' or '//'.
|
// Block anything that starts with 'http:', 'https:', '://' or '//'.
|
||||||
if (!/^((https?:)|:?[/]{2})/.test(ajaxSettings.url)) {
|
if (!/^((https?:)|:?[/]{2})/.test(ajaxSettings.url)) {
|
||||||
// Only send the token to relative URLs i.e. locally.
|
// Only send the token to relative URLs i.e. locally.
|
||||||
$meta = $('meta[name=csrf]');
|
$meta = $('meta[name=csrf]');
|
||||||
if (!z.anonymous && $meta.length) {
|
if (!z.anonymous && $meta.length) {
|
||||||
csrf = $meta.attr('content');
|
csrf = $meta.attr('content');
|
||||||
} else {
|
} else {
|
||||||
csrf = $("input[name='csrfmiddlewaretoken']").val();
|
csrf = $("input[name='csrfmiddlewaretoken']").val();
|
||||||
}
|
}
|
||||||
if (csrf) {
|
if (csrf) {
|
||||||
xhr.setRequestHeader('X-CSRFToken', csrf);
|
xhr.setRequestHeader('X-CSRFToken', csrf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).ajaxSuccess(function(event, xhr, ajaxSettings) {
|
})
|
||||||
|
.ajaxSuccess(function (event, xhr, ajaxSettings) {
|
||||||
$(window).trigger('resize'); // Redraw what needs to be redrawn.
|
$(window).trigger('resize'); // Redraw what needs to be redrawn.
|
||||||
});
|
});
|
||||||
|
|
||||||
function b64toBlob(data) {
|
function b64toBlob(data) {
|
||||||
var b64str = atob(data);
|
var b64str = atob(data);
|
||||||
var counter = b64str.length;
|
var counter = b64str.length;
|
||||||
var u8arr = new Uint8Array(counter);
|
var u8arr = new Uint8Array(counter);
|
||||||
while(counter--){
|
while (counter--) {
|
||||||
u8arr[counter] = b64str.charCodeAt(counter);
|
u8arr[counter] = b64str.charCodeAt(counter);
|
||||||
}
|
}
|
||||||
return new Blob([u8arr]);
|
return new Blob([u8arr]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,51 @@
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
if (!$(document.body).hasClass('home')) {
|
if (!$(document.body).hasClass('home')) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#homepage .listing-header a').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
update(this, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Switch to the tab of the <a> given as `link`.
|
||||||
|
// Only call pushState if `push` is True.
|
||||||
|
function update(link, push) {
|
||||||
|
var target = $(link).attr('data-target');
|
||||||
|
|
||||||
|
// Change the list to show the right add-ons.
|
||||||
|
$('.addon-listing').attr('class', 'addon-listing addon-listing-' + target);
|
||||||
|
|
||||||
|
// Update the selected tab.
|
||||||
|
$('.listing-header .selected').removeClass('selected');
|
||||||
|
$('#' + target)
|
||||||
|
.addClass('selected')
|
||||||
|
.focus();
|
||||||
|
|
||||||
|
if (push && history.pushState) {
|
||||||
|
history.pushState({ target: target }, document.title, link.href);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$('#homepage .listing-header a').click(function(e) {
|
// If we already have a hash, switch to the tab.
|
||||||
e.preventDefault();
|
if (location.hash) {
|
||||||
update(this, true);
|
var selected = $('#homepage .listing-header ' + location.hash);
|
||||||
});
|
if (selected) {
|
||||||
|
selected.find('a').click().focus();
|
||||||
// Switch to the tab of the <a> given as `link`.
|
|
||||||
// Only call pushState if `push` is True.
|
|
||||||
function update(link, push) {
|
|
||||||
var target = $(link).attr('data-target');
|
|
||||||
|
|
||||||
// Change the list to show the right add-ons.
|
|
||||||
$('.addon-listing').attr('class', 'addon-listing addon-listing-' + target);
|
|
||||||
|
|
||||||
// Update the selected tab.
|
|
||||||
$('.listing-header .selected').removeClass('selected');
|
|
||||||
$('#' + target).addClass('selected').focus();
|
|
||||||
|
|
||||||
if (push && history.pushState) {
|
|
||||||
history.pushState({target: target}, document.title, link.href);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we already have a hash, switch to the tab.
|
|
||||||
if (location.hash) {
|
|
||||||
var selected = $('#homepage .listing-header ' + location.hash);
|
|
||||||
if (selected) {
|
|
||||||
selected.find('a').click().focus();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Add the current page to the history so we can get back.
|
|
||||||
var selected = $('#homepage .listing-header .selected a')[0];
|
|
||||||
update(selected, true, true);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Add the current page to the history so we can get back.
|
||||||
|
var selected = $('#homepage .listing-header .selected a')[0];
|
||||||
|
update(selected, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
// Set up our history callback.
|
// Set up our history callback.
|
||||||
$(window).on('popstate', function(ev) {
|
$(window).on('popstate', function (ev) {
|
||||||
// We don't pushState here because we'd be stuck in this position.
|
// We don't pushState here because we'd be stuck in this position.
|
||||||
var e = ev.originalEvent;
|
var e = ev.originalEvent;
|
||||||
if (e.state && e.state.target) {
|
if (e.state && e.state.target) {
|
||||||
var a = $('#' + e.state.target + ' a')[0];
|
var a = $('#' + e.state.target + ' a')[0];
|
||||||
update(a, false);
|
update(a, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,121 +1,133 @@
|
||||||
/* Global initialization script */
|
/* Global initialization script */
|
||||||
var z = {};
|
var z = {};
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
// Initialize install buttons.
|
// Initialize install buttons.
|
||||||
$('.install').installButton();
|
$('.install').installButton();
|
||||||
$(window).trigger('buttons_loaded');
|
$(window).trigger('buttons_loaded');
|
||||||
|
|
||||||
// Initialize any tabbed interfaces. See: tabs.js
|
// Initialize any tabbed interfaces. See: tabs.js
|
||||||
if ($.fn.tabify) {
|
if ($.fn.tabify) {
|
||||||
$('.tab-wrapper').tabify();
|
$('.tab-wrapper').tabify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize email links
|
// Initialize email links
|
||||||
$('span.emaillink').each(function() {
|
$('span.emaillink').each(function () {
|
||||||
$(this).find('.i').remove();
|
$(this).find('.i').remove();
|
||||||
var em = $(this).text().split('').reverse().join('');
|
var em = $(this).text().split('').reverse().join('');
|
||||||
$(this).prev('a').attr('href', 'mailto:' + em).addClass('email');
|
$(this)
|
||||||
});
|
.prev('a')
|
||||||
|
.attr('href', 'mailto:' + em)
|
||||||
|
.addClass('email');
|
||||||
|
});
|
||||||
|
|
||||||
// fake placeholders if we need to.
|
// fake placeholders if we need to.
|
||||||
if (!('placeholder' in document.createElement('input'))) {
|
if (!('placeholder' in document.createElement('input'))) {
|
||||||
$('input[placeholder]').placeholder();
|
$('input[placeholder]').placeholder();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (z.readonly) {
|
if (z.readonly) {
|
||||||
$('form[method=post]')
|
$('form[method=post]')
|
||||||
.before(gettext('This feature is temporarily disabled while we perform website maintenance. Please check back a little later.'))
|
.before(
|
||||||
.find('input, button, select').prop('disabled', true).addClass('disabled');
|
gettext(
|
||||||
}
|
'This feature is temporarily disabled while we perform website maintenance. Please check back a little later.',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.find('input, button, select')
|
||||||
|
.prop('disabled', true)
|
||||||
|
.addClass('disabled');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
z.inlineSVG = (function() {
|
z.inlineSVG = (function () {
|
||||||
var e = document.createElement('div');
|
var e = document.createElement('div');
|
||||||
e.innerHTML = '<svg></svg>';
|
e.innerHTML = '<svg></svg>';
|
||||||
return !!(window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement);
|
return !!(
|
||||||
|
window.SVGSVGElement && e.firstChild instanceof window.SVGSVGElement
|
||||||
|
);
|
||||||
})();
|
})();
|
||||||
if (!z.inlineSVG) {
|
if (!z.inlineSVG) {
|
||||||
$("body").addClass("noInlineSVG");
|
$('body').addClass('noInlineSVG');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* prevent-default function wrapper */
|
/* prevent-default function wrapper */
|
||||||
function _pd(func) {
|
function _pd(func) {
|
||||||
return function(e) {
|
return function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
func.apply(this, arguments);
|
func.apply(this, arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */
|
/* Fake the placeholder attribute since Firefox 3.6 doesn't support it. */
|
||||||
jQuery.fn.placeholder = function(new_value) {
|
jQuery.fn.placeholder = function (new_value) {
|
||||||
|
if (new_value) {
|
||||||
|
this.attr('placeholder', new_value);
|
||||||
|
}
|
||||||
|
|
||||||
if (new_value) {
|
/* Bail early if we have built-in placeholder support. */
|
||||||
this.attr('placeholder', new_value);
|
if ('placeholder' in document.createElement('input')) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_value && this.hasClass('placeholder')) {
|
||||||
|
this.val('').blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.focus(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
text = $this.attr('placeholder');
|
||||||
|
|
||||||
|
if ($this.val() == text) {
|
||||||
|
$this.val('').removeClass('placeholder');
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.blur(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
text = $this.attr('placeholder');
|
||||||
|
|
||||||
/* Bail early if we have built-in placeholder support. */
|
if ($this.val() === '') {
|
||||||
if ('placeholder' in document.createElement('input')) {
|
$this.val(text).addClass('placeholder');
|
||||||
return this;
|
}
|
||||||
}
|
})
|
||||||
|
.each(function () {
|
||||||
if (new_value && this.hasClass('placeholder')) {
|
/* Remove the placeholder text before submitting the form. */
|
||||||
this.val('').blur();
|
var self = $(this);
|
||||||
}
|
self.closest('form').submit(function () {
|
||||||
|
if (self.hasClass('placeholder')) {
|
||||||
return this.focus(function() {
|
self.val('');
|
||||||
var $this = $(this),
|
|
||||||
text = $this.attr('placeholder');
|
|
||||||
|
|
||||||
if ($this.val() == text) {
|
|
||||||
$this.val('').removeClass('placeholder');
|
|
||||||
}
|
}
|
||||||
}).blur(function() {
|
});
|
||||||
var $this = $(this),
|
})
|
||||||
text = $this.attr('placeholder');
|
.blur();
|
||||||
|
|
||||||
if ($this.val() === '') {
|
|
||||||
$this.val(text).addClass('placeholder');
|
|
||||||
}
|
|
||||||
}).each(function(){
|
|
||||||
/* Remove the placeholder text before submitting the form. */
|
|
||||||
var self = $(this);
|
|
||||||
self.closest('form').submit(function() {
|
|
||||||
if (self.hasClass('placeholder')) {
|
|
||||||
self.val('');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).blur();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jQuery.fn.hasattr = function (name) {
|
||||||
jQuery.fn.hasattr = function(name) {
|
return this.attr(name) !== undefined;
|
||||||
return this.attr(name) !== undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var escape_ = function (s) {
|
||||||
var escape_ = function(s){
|
if (s === undefined) {
|
||||||
if (s === undefined) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
return s
|
||||||
return s.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<')
|
.replace(/&/g, '&')
|
||||||
.replace(/'/g, ''').replace(/"/g, '"');
|
.replace(/>/g, '>')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/'/g, ''')
|
||||||
|
.replace(/"/g, '"');
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO(potch): kill underscore dead. until then, fake it on mobile.
|
//TODO(potch): kill underscore dead. until then, fake it on mobile.
|
||||||
if (!('_' in window)) _ = {};
|
if (!('_' in window)) _ = {};
|
||||||
/* is ``key`` in obj? */
|
/* is ``key`` in obj? */
|
||||||
_.haskey = function(obj, key) {
|
_.haskey = function (obj, key) {
|
||||||
return typeof obj[key] !== "undefined";
|
return typeof obj[key] !== 'undefined';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Detect browser, version, and OS. */
|
/* Detect browser, version, and OS. */
|
||||||
$.extend(z, BrowserUtils());
|
$.extend(z, BrowserUtils());
|
||||||
$(document.body).addClass(z.platform).toggleClass('badbrowser', z.badBrowser);
|
$(document.body).addClass(z.platform).toggleClass('badbrowser', z.badBrowser);
|
||||||
|
|
||||||
|
|
||||||
/* Details for the current application. */
|
/* Details for the current application. */
|
||||||
z.app = document.body.getAttribute('data-app');
|
z.app = document.body.getAttribute('data-app');
|
||||||
z.appName = document.body.getAttribute('data-appname');
|
z.appName = document.body.getAttribute('data-appname');
|
||||||
|
@ -128,5 +140,5 @@ z.static_url = document.body.getAttribute('data-static-url');
|
||||||
z.readonly = JSON.parse(document.body.getAttribute('data-readonly'));
|
z.readonly = JSON.parse(document.body.getAttribute('data-readonly'));
|
||||||
|
|
||||||
if (z.badBrowser) {
|
if (z.badBrowser) {
|
||||||
$(".get-fx-message").show();
|
$('.get-fx-message').show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,297 +1,337 @@
|
||||||
// Yes, this is out here for a reason.
|
// Yes, this is out here for a reason.
|
||||||
// We want to hide the non-default locales as fast as possible.
|
// We want to hide the non-default locales as fast as possible.
|
||||||
(function() {
|
(function () {
|
||||||
var dl = $('body').attr("data-default-locale");
|
var dl = $('body').attr('data-default-locale');
|
||||||
if (dl) {
|
if (dl) {
|
||||||
$(format(".trans>:not([lang='{0}'])", dl)).hide();
|
$(format(".trans>:not([lang='{0}'])", dl)).hide();
|
||||||
$(format(".trans [lang='{0}']", dl)).show();
|
$(format(".trans [lang='{0}']", dl)).show();
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
if (!$("#l10n-menu").length) return;
|
if (!$('#l10n-menu').length) return;
|
||||||
var locales = [],
|
var locales = [],
|
||||||
dl = $('body').attr("data-default-locale"),
|
dl = $('body').attr('data-default-locale'),
|
||||||
currentLocale = dl,
|
currentLocale = dl,
|
||||||
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
|
unsavedModalMsg = $('#modal-l10n-unsaved .msg').html(),
|
||||||
unsavedModal = $('#modal-l10n-unsaved').modal(),
|
unsavedModal = $('#modal-l10n-unsaved').modal(),
|
||||||
rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(),
|
rmLocaleModalMsg = $('#modal-l10n-rm .msg').html(),
|
||||||
rmLocaleModal = $('#modal-l10n-rm').modal(),
|
rmLocaleModal = $('#modal-l10n-rm').modal(),
|
||||||
modalActions = $(".modal-actions", unsavedModal),
|
modalActions = $('.modal-actions', unsavedModal),
|
||||||
translations = {}; //hold the initial values of the fields to check for changes
|
translations = {}; //hold the initial values of the fields to check for changes
|
||||||
|
|
||||||
$(".primary").on("change keyup paste blur", ".trans input, .trans textarea", checkTranslation);
|
$('.primary').on(
|
||||||
$("form").submit(function () {
|
'change keyup paste blur',
|
||||||
$(this).find(".trans .cloned").remove();
|
'.trans input, .trans textarea',
|
||||||
|
checkTranslation,
|
||||||
|
);
|
||||||
|
$('form').submit(function () {
|
||||||
|
$(this).find('.trans .cloned').remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
function popuplateTranslations(el) {
|
||||||
|
//load in the initial values of the translations
|
||||||
|
el.find('.trans input[lang], .trans textarea[lang]').each(function () {
|
||||||
|
var $input = $(this),
|
||||||
|
$trans = $input.closest('.trans'),
|
||||||
|
transKey = $trans.attr('data-name') + '_' + $input.attr('lang');
|
||||||
|
translations[transKey] = $input.val();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function popuplateTranslations(el) { //load in the initial values of the translations
|
function showExistingLocales() {
|
||||||
el.find(".trans input[lang], .trans textarea[lang]").each(function() {
|
discoverLocales();
|
||||||
var $input = $(this),
|
$el = $('#existing_locales').empty();
|
||||||
$trans = $input.closest(".trans"),
|
$('#all_locales li').show();
|
||||||
transKey = $trans.attr("data-name")+'_'+$input.attr('lang');
|
$.each(_.without(locales, dl), function () {
|
||||||
translations[transKey] = $input.val();
|
var locale_row = $(
|
||||||
});
|
format("#all_locales a[href='#{0}']", [this]),
|
||||||
|
).parent();
|
||||||
|
if (locale_row.length) {
|
||||||
|
$el.append(
|
||||||
|
format(
|
||||||
|
"<li><a title='{msg}'class='remove' href='#'>x</a>{row}</li>",
|
||||||
|
{
|
||||||
|
msg: gettext('Remove this localization'),
|
||||||
|
row: locale_row.html(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
locale_row.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTranslation(e, t) {
|
||||||
|
var $input = e.originalEvent ? $(this) : $(format("[lang='{0}']", [e]), t),
|
||||||
|
$trans = $input.closest('.trans'),
|
||||||
|
lang = e.originalEvent ? $input.attr('lang') : e,
|
||||||
|
$dl = $(format("[lang='{0}']", [dl]), $trans),
|
||||||
|
transKey = $trans.attr('data-name') + '_' + lang;
|
||||||
|
if ($input.length == 0 || $input.is('span')) {
|
||||||
|
// No translation of this element exists for the
|
||||||
|
// requested language.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (!(transKey in translations)) {
|
||||||
function showExistingLocales() {
|
translations[transKey] = $input.val();
|
||||||
discoverLocales();
|
|
||||||
$el = $("#existing_locales").empty();
|
|
||||||
$("#all_locales li").show();
|
|
||||||
$.each(_.without(locales, dl), function() {
|
|
||||||
var locale_row = $(format("#all_locales a[href='#{0}']",[this])).parent();
|
|
||||||
if (locale_row.length) {
|
|
||||||
$el.append(format("<li><a title='{msg}'class='remove' href='#'>x</a>{row}</li>",
|
|
||||||
{ msg: gettext('Remove this localization'),
|
|
||||||
row: locale_row.html()
|
|
||||||
}));
|
|
||||||
locale_row.hide();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if (lang != dl) {
|
||||||
function checkTranslation(e, t) {
|
if ($input.val() == $dl.val() && $input.val().trim().length) {
|
||||||
var $input = e.originalEvent ? $(this) : $(format("[lang='{0}']", [e]), t),
|
$input.addClass('cloned');
|
||||||
$trans = $input.closest(".trans"),
|
} else if (!$input.val().trim().length) {
|
||||||
lang = e.originalEvent ? $input.attr("lang") : e,
|
if (e.originalEvent && e.type == 'focusout') {
|
||||||
$dl = $(format("[lang='{0}']", [dl]), $trans),
|
$input.val($dl.val()).addClass('cloned');
|
||||||
transKey = $trans.attr("data-name")+'_'+lang;
|
|
||||||
if ($input.length == 0 || $input.is('span')) {
|
|
||||||
// No translation of this element exists for the
|
|
||||||
// requested language.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!(transKey in translations)) {
|
|
||||||
translations[transKey] = $input.val();
|
|
||||||
}
|
|
||||||
if (lang != dl) {
|
|
||||||
if ($input.val() == $dl.val() && $input.val().trim().length) {
|
|
||||||
$input.addClass("cloned");
|
|
||||||
} else if (!$input.val().trim().length) {
|
|
||||||
if (e.originalEvent && e.type == "focusout") {
|
|
||||||
$input.val($dl.val()).addClass("cloned");
|
|
||||||
} else {
|
|
||||||
$input.removeClass("cloned");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$input.removeClass("cloned");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (translations[transKey] != $input.val()) {
|
|
||||||
$input.addClass("unsaved");
|
|
||||||
} else {
|
} else {
|
||||||
$input.removeClass("unsaved");
|
$input.removeClass('cloned');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
$input.removeClass('cloned');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (translations[transKey] != $input.val()) {
|
||||||
|
$input.addClass('unsaved');
|
||||||
|
} else {
|
||||||
|
$input.removeClass('unsaved');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.primary').on('click', '.errorlist .l10n', switchLocale);
|
||||||
|
|
||||||
|
$('#all_locales').on('switch', 'a', switchLocale);
|
||||||
|
|
||||||
|
// If the locale switcher is visible, use the cookie.
|
||||||
|
var initLocale = dl;
|
||||||
|
if ($('#l10n-menu:visible').length) {
|
||||||
|
initLocale = $.cookie('current_locale');
|
||||||
|
}
|
||||||
|
$(format("#all_locales a[href='#{0}']", [initLocale])).trigger('switch');
|
||||||
|
|
||||||
|
function switchLocale(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$tgt = $(this);
|
||||||
|
var new_locale = $tgt.attr('data-lang') || $tgt.attr('href').substring(1);
|
||||||
|
var unsaved = $('form .trans .unsaved');
|
||||||
|
|
||||||
|
if (unsaved.length && new_locale != currentLocale) {
|
||||||
|
unsavedModal
|
||||||
|
.children('.msg')
|
||||||
|
.html(format(unsavedModalMsg, [$('#change-locale').text()]));
|
||||||
|
unsavedModal.render();
|
||||||
|
$('#l10n-save-changes')
|
||||||
|
.off()
|
||||||
|
.click(function () {
|
||||||
|
var unsavedForms = $('form:has(.trans .unsaved)');
|
||||||
|
var numFormsLeft = unsavedForms.length;
|
||||||
|
var erroredForms = 0;
|
||||||
|
modalActions.addClass('ajax-loading');
|
||||||
|
modalActions.find('button').addClass('disabled');
|
||||||
|
unsavedForms.each(function () {
|
||||||
|
var $form = $(this);
|
||||||
|
$.ajax({
|
||||||
|
url: $form.attr('action'),
|
||||||
|
type: 'post',
|
||||||
|
data: $form.serialize(),
|
||||||
|
error: function () {
|
||||||
|
modalActions.removeClass('ajax-loading');
|
||||||
|
},
|
||||||
|
success: function (d) {
|
||||||
|
var $resp = $(d);
|
||||||
|
if (
|
||||||
|
$form.attr('id') &&
|
||||||
|
$resp.find('#' + $form.attr('id')).length
|
||||||
|
) {
|
||||||
|
$resp = $resp.find('#' + $form.attr('id'));
|
||||||
|
}
|
||||||
|
// Add locale names to error messages
|
||||||
|
annotateLocalizedErrors($resp);
|
||||||
|
numFormsLeft--;
|
||||||
|
if ($resp.find('.errorlist').length) {
|
||||||
|
//display errors if they occur
|
||||||
|
$form.html($resp.html());
|
||||||
|
updateLocale();
|
||||||
|
if (
|
||||||
|
$resp.find(
|
||||||
|
format(".errorlist li[data-lang='{0}']", currentLocale),
|
||||||
|
).length
|
||||||
|
) {
|
||||||
|
erroredForms++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//clean up the errors we inserted
|
||||||
|
popuplateTranslations($form);
|
||||||
|
$form.find('.unsaved').removeClass('unsaved');
|
||||||
|
$form.find('.errorlist').remove();
|
||||||
|
}
|
||||||
|
if (numFormsLeft < 1) {
|
||||||
|
if (erroredForms) {
|
||||||
|
window.scrollTo(
|
||||||
|
0,
|
||||||
|
$('.errorlist .l10n').closest('form').offset().top,
|
||||||
|
);
|
||||||
|
$('.errorlist')
|
||||||
|
.first()
|
||||||
|
.siblings('.trans')
|
||||||
|
.find('input:visible, textarea:visible')
|
||||||
|
.focus();
|
||||||
|
} else {
|
||||||
|
updateLocale(new_locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modalActions.removeClass('ajax-loading');
|
||||||
|
modalActions.find('button').removeClass('disabled');
|
||||||
|
unsavedModal.hideMe();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#l10n-discard-changes').click(function () {
|
||||||
|
$('.trans .unsaved').remove();
|
||||||
|
updateLocale(new_locale);
|
||||||
|
unsavedModal.hideMe();
|
||||||
|
});
|
||||||
|
$('#l10n-cancel-changes').click(function () {
|
||||||
|
unsavedModal.hideMe();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateLocale(new_locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(".primary").on("click", ".errorlist .l10n", switchLocale);
|
if (localePopup) {
|
||||||
|
localePopup.hideMe();
|
||||||
$("#all_locales").on("switch", "a", switchLocale);
|
|
||||||
|
|
||||||
// If the locale switcher is visible, use the cookie.
|
|
||||||
var initLocale = dl;
|
|
||||||
if ($('#l10n-menu:visible').length) {
|
|
||||||
initLocale = $.cookie('current_locale');
|
|
||||||
}
|
}
|
||||||
$(format("#all_locales a[href='#{0}']",[initLocale])).trigger("switch");
|
}
|
||||||
|
|
||||||
function switchLocale(e) {
|
var localePopup = $('#locale-popup').popup('#change-locale', {
|
||||||
|
pointTo: '#change-locale',
|
||||||
|
width: 200,
|
||||||
|
callback: function () {
|
||||||
|
showExistingLocales();
|
||||||
|
$('#locale-popup').on('click', 'a:not(.remove)', switchLocale);
|
||||||
|
$('#locale-popup').on('click', 'a.remove', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$tgt = $(this);
|
e.stopPropagation();
|
||||||
var new_locale = $tgt.attr("data-lang") || $tgt.attr("href").substring(1);
|
var toRemove = $(this)
|
||||||
var unsaved = $("form .trans .unsaved");
|
.closest('li')
|
||||||
|
.find('a:not(.remove)')
|
||||||
|
.attr('href')
|
||||||
|
.substring(1);
|
||||||
|
rmLocaleModal.children('.msg').html(format(rmLocaleModalMsg, toRemove));
|
||||||
|
rmLocaleModal.render();
|
||||||
|
$('#l10n-cancel-rm').off().click(rmLocaleModal.hideMe);
|
||||||
|
function cleanUp() {
|
||||||
|
$('.modal-actions', rmLocaleModal).removeClass('ajax-loading');
|
||||||
|
rmLocaleModal.hideMe();
|
||||||
|
}
|
||||||
|
$('#l10n-confirm-rm')
|
||||||
|
.off()
|
||||||
|
.click(function (e) {
|
||||||
|
$('.modal-actions', rmLocaleModal).addClass('ajax-loading');
|
||||||
|
$.ajax({
|
||||||
|
url: $('#l10n-menu').attr('data-rm-locale'),
|
||||||
|
type: 'post',
|
||||||
|
data: { locale: toRemove },
|
||||||
|
error: function () {
|
||||||
|
cleanUp();
|
||||||
|
},
|
||||||
|
success: function () {
|
||||||
|
if (currentLocale == toRemove) {
|
||||||
|
updateLocale(dl);
|
||||||
|
}
|
||||||
|
$('.trans [lang=' + toRemove + ']').remove();
|
||||||
|
cleanUp();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (unsaved.length && new_locale != currentLocale) {
|
function updateLocale(lang) {
|
||||||
unsavedModal.children(".msg")
|
lang = lang || currentLocale;
|
||||||
.html(format(unsavedModalMsg,[$("#change-locale").text()]));
|
if (currentLocale != lang) {
|
||||||
unsavedModal.render();
|
currentLocale = lang;
|
||||||
$("#l10n-save-changes").off().click(function () {
|
}
|
||||||
var unsavedForms = $('form:has(.trans .unsaved)');
|
if (!_.include(locales, lang)) {
|
||||||
var numFormsLeft = unsavedForms.length;
|
locales.push(lang);
|
||||||
var erroredForms = 0;
|
}
|
||||||
modalActions.addClass("ajax-loading");
|
var current = $(format("#locale-popup [href='#{0}']", [lang]))
|
||||||
modalActions.find("button").addClass("disabled");
|
.first()
|
||||||
unsavedForms.each(function() {
|
.clone();
|
||||||
var $form = $(this);
|
current.find('em').remove();
|
||||||
$.ajax({
|
$('#change-locale').text(current.text());
|
||||||
url: $form.attr('action'),
|
$('.trans').each(function () {
|
||||||
type: "post",
|
var $el = $(this),
|
||||||
data: $form.serialize(),
|
field = $el.attr('data-name'),
|
||||||
error: function() {
|
label = $(format("label[data-for='{0}']", [field]));
|
||||||
modalActions.removeClass("ajax-loading");
|
if (!$el.find(format("[lang='{0}']", [lang])).length) {
|
||||||
},
|
if ($el.children('.trans-init').length) {
|
||||||
success: function(d) {
|
var $ni = $el.children('.trans-init').clone();
|
||||||
var $resp = $(d);
|
$ni.attr({
|
||||||
if ($form.attr('id') && $resp.find('#' + $form.attr('id')).length) {
|
class: '',
|
||||||
$resp = $resp.find('#' + $form.attr('id'));
|
lang: lang,
|
||||||
}
|
id: format('id_{0}_{1}', field, lang),
|
||||||
// Add locale names to error messages
|
name: [field, lang].join('_'),
|
||||||
annotateLocalizedErrors($resp);
|
value: $el.find(format("[lang='{0}']", [dl])).val(),
|
||||||
numFormsLeft--;
|
});
|
||||||
if ($resp.find(".errorlist").length) { //display errors if they occur
|
if (lang != dl) $ni.addClass('cloned');
|
||||||
$form.html($resp.html());
|
|
||||||
updateLocale();
|
|
||||||
if ($resp.find(format(".errorlist li[data-lang='{0}']", currentLocale)).length) {
|
|
||||||
erroredForms++;
|
|
||||||
}
|
|
||||||
} else { //clean up the errors we inserted
|
|
||||||
popuplateTranslations($form);
|
|
||||||
$form.find(".unsaved").removeClass("unsaved");
|
|
||||||
$form.find(".errorlist").remove();
|
|
||||||
}
|
|
||||||
if (numFormsLeft < 1) {
|
|
||||||
if (erroredForms) {
|
|
||||||
window.scrollTo(0,$(".errorlist .l10n").closest("form").offset().top);
|
|
||||||
$(".errorlist").first().siblings(".trans")
|
|
||||||
.find("input:visible, textarea:visible").focus();
|
|
||||||
} else {
|
|
||||||
updateLocale(new_locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
modalActions.removeClass("ajax-loading");
|
|
||||||
modalActions.find("button").removeClass("disabled");
|
|
||||||
unsavedModal.hideMe();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$("#l10n-discard-changes").click(function () {
|
|
||||||
$('.trans .unsaved').remove();
|
|
||||||
updateLocale(new_locale);
|
|
||||||
unsavedModal.hideMe();
|
|
||||||
});
|
|
||||||
$("#l10n-cancel-changes").click(function () {
|
|
||||||
unsavedModal.hideMe();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
updateLocale(new_locale);
|
var $ni = $el.find(format("[lang='{0}']", dl)).clone();
|
||||||
}
|
$ni.attr({
|
||||||
|
class: 'cloned',
|
||||||
if(localePopup) {
|
lang: lang,
|
||||||
localePopup.hideMe();
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var localePopup = $("#locale-popup").popup("#change-locale", {
|
|
||||||
pointTo: "#change-locale",
|
|
||||||
width: 200,
|
|
||||||
callback: function() {
|
|
||||||
showExistingLocales();
|
|
||||||
$("#locale-popup").on('click', 'a:not(.remove)', switchLocale);
|
|
||||||
$("#locale-popup").on('click', 'a.remove', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
var toRemove = $(this).closest("li").find("a:not(.remove)").attr("href").substring(1);
|
|
||||||
rmLocaleModal.children(".msg").html(format(rmLocaleModalMsg,toRemove));
|
|
||||||
rmLocaleModal.render();
|
|
||||||
$('#l10n-cancel-rm').off().click(rmLocaleModal.hideMe);
|
|
||||||
function cleanUp() {
|
|
||||||
$(".modal-actions", rmLocaleModal).removeClass('ajax-loading');
|
|
||||||
rmLocaleModal.hideMe();
|
|
||||||
}
|
|
||||||
$('#l10n-confirm-rm').off().click(function(e) {
|
|
||||||
$(".modal-actions", rmLocaleModal).addClass('ajax-loading');
|
|
||||||
$.ajax({
|
|
||||||
url: $('#l10n-menu').attr('data-rm-locale'),
|
|
||||||
type: "post",
|
|
||||||
data: {locale: toRemove},
|
|
||||||
error: function() {
|
|
||||||
cleanUp();
|
|
||||||
},
|
|
||||||
success: function() {
|
|
||||||
if (currentLocale == toRemove) {
|
|
||||||
updateLocale(dl);
|
|
||||||
}
|
|
||||||
$('.trans [lang='+toRemove+']').remove();
|
|
||||||
cleanUp();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
$el.append($ni);
|
||||||
|
}
|
||||||
|
checkTranslation(lang, $el);
|
||||||
|
if (label.length) {
|
||||||
|
label.children('.locale').remove();
|
||||||
|
label.append(
|
||||||
|
format("<span class='locale'>{0}</span>", [
|
||||||
|
$('#change-locale').text(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
label_for = $el.children(format("[lang='{0}']", [lang])).attr('id');
|
||||||
|
label.attr('for', label_for);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
$(format(".trans>:not([lang='{0}'])", currentLocale)).hide();
|
||||||
function updateLocale(lang) {
|
$(format(".trans [lang='{0}']", currentLocale)).show();
|
||||||
lang = lang || currentLocale;
|
initCharCount();
|
||||||
if (currentLocale != lang) {
|
if ($.cookie('current_locale') != currentLocale && currentLocale != dl) {
|
||||||
currentLocale = lang;
|
$.cookie('current_locale', null);
|
||||||
}
|
$.cookie('current_locale', currentLocale, { expires: 0 });
|
||||||
if (!_.include(locales,lang)) {
|
|
||||||
locales.push(lang);
|
|
||||||
}
|
|
||||||
var current = $(format("#locale-popup [href='#{0}']", [lang])).first().clone();
|
|
||||||
current.find('em').remove();
|
|
||||||
$("#change-locale").text(current.text());
|
|
||||||
$(".trans").each(function () {
|
|
||||||
var $el = $(this),
|
|
||||||
field = $el.attr('data-name'),
|
|
||||||
label = $(format("label[data-for='{0}']",[field]));
|
|
||||||
if (!$el.find(format("[lang='{0}']",[lang])).length) {
|
|
||||||
if ($el.children(".trans-init").length) {
|
|
||||||
var $ni = $el.children(".trans-init").clone();
|
|
||||||
$ni.attr({
|
|
||||||
"class": "",
|
|
||||||
lang: lang,
|
|
||||||
id: format('id_{0}_{1}', field, lang),
|
|
||||||
name: [field,lang].join('_'),
|
|
||||||
value: $el.find(format("[lang='{0}']",[dl])).val()
|
|
||||||
});
|
|
||||||
if (lang != dl) $ni.addClass("cloned");
|
|
||||||
} else {
|
|
||||||
var $ni = $el.find(format("[lang='{0}']",dl)).clone();
|
|
||||||
$ni.attr({
|
|
||||||
"class": "cloned",
|
|
||||||
lang: lang
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$el.append($ni);
|
|
||||||
}
|
|
||||||
checkTranslation(lang, $el);
|
|
||||||
if (label.length) {
|
|
||||||
label.children(".locale").remove();
|
|
||||||
label.append(format("<span class='locale'>{0}</span>",[$("#change-locale").text()]));
|
|
||||||
label_for = $el.children(format("[lang='{0}']",[lang])).attr('id');
|
|
||||||
label.attr('for', label_for);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
$(format(".trans>:not([lang='{0}'])", currentLocale)).hide();
|
|
||||||
$(format(".trans [lang='{0}']", currentLocale)).show();
|
|
||||||
initCharCount();
|
|
||||||
if ($.cookie('current_locale') != currentLocale &&
|
|
||||||
currentLocale != dl) {
|
|
||||||
$.cookie('current_locale', null);
|
|
||||||
$.cookie('current_locale', currentLocale, {expires: 0});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function discoverLocales(locale) {
|
function discoverLocales(locale) {
|
||||||
var seen_locales = {};
|
var seen_locales = {};
|
||||||
$(".trans [lang]").each(function () {
|
$('.trans [lang]').each(function () {
|
||||||
seen_locales[$(this).attr('lang')] = true;
|
seen_locales[$(this).attr('lang')] = true;
|
||||||
});
|
});
|
||||||
locales = _.keys(seen_locales);
|
locales = _.keys(seen_locales);
|
||||||
}
|
}
|
||||||
|
|
||||||
z.refreshL10n = function(lang) {
|
z.refreshL10n = function (lang) {
|
||||||
updateLocale(lang);
|
updateLocale(lang);
|
||||||
};
|
};
|
||||||
updateLocale();
|
updateLocale();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function annotateLocalizedErrors($el) {
|
function annotateLocalizedErrors($el) {
|
||||||
$el.find(".errorlist li[data-lang]:not(.l10n)").each(function() {
|
$el.find('.errorlist li[data-lang]:not(.l10n)').each(function () {
|
||||||
var err = $(this),
|
var err = $(this),
|
||||||
t = err.text(),
|
t = err.text(),
|
||||||
l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')])).first().text();
|
l = $(format("#locale-popup [href='#{0}']", [err.attr('data-lang')]))
|
||||||
err.text(format("{0}: ",[l])+t).addClass("l10n");
|
.first()
|
||||||
});
|
.text();
|
||||||
|
err.text(format('{0}: ', [l]) + t).addClass('l10n');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function loc(s) {
|
function loc(s) {
|
||||||
// A noop function for strings that are not ready to be localized.
|
// A noop function for strings that are not ready to be localized.
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +1,114 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
var report = $('.review-reason').html();
|
var report = $('.review-reason').html();
|
||||||
|
|
||||||
$(".review-reason").popup(".flag-review", {
|
$('.review-reason').popup('.flag-review', {
|
||||||
delegate: $(document.body),
|
delegate: $(document.body),
|
||||||
width: 'inherit',
|
width: 'inherit',
|
||||||
callback: function(obj) {
|
callback: function (obj) {
|
||||||
var ct = $(obj.click_target),
|
var ct = $(obj.click_target),
|
||||||
$popup = this;
|
$popup = this;
|
||||||
|
|
||||||
function addFlag(flag, note) {
|
function addFlag(flag, note) {
|
||||||
$.ajax({type: 'POST',
|
$.ajax({
|
||||||
url: ct.attr("href"),
|
type: 'POST',
|
||||||
data: {flag: flag, note: note},
|
url: ct.attr('href'),
|
||||||
success: function() {
|
data: { flag: flag, note: note },
|
||||||
$popup.removeClass("other")
|
success: function () {
|
||||||
.hideMe();
|
$popup.removeClass('other').hideMe();
|
||||||
ct.replaceWith(gettext('Flagged for review'));
|
ct.replaceWith(gettext('Flagged for review'));
|
||||||
},
|
},
|
||||||
error: function(){ },
|
error: function () {},
|
||||||
dataType: 'json'
|
dataType: 'json',
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
$popup.on("click", "li a", function(e) {
|
$popup.on('click', 'li a', function (e) {
|
||||||
e.preventDefault();
|
|
||||||
var el = $(e.target);
|
|
||||||
if (el.attr("href") == "#review_flag_reason_other") {
|
|
||||||
$popup.addClass('other')
|
|
||||||
.on("submit", "form", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
var note = $popup.find('#id_note').val();
|
|
||||||
if (!note) {
|
|
||||||
alert(gettext('Your input is required'));
|
|
||||||
} else {
|
|
||||||
addFlag('review_flag_reason_other', note);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setPos(ct)
|
|
||||||
.find('input[type=text]')
|
|
||||||
.focus();
|
|
||||||
} else {
|
|
||||||
addFlag(el.attr("href").slice(1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$popup.html(report);
|
|
||||||
return { pointTo: ct };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.primary').on('click', '.review-edit', function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var $form = $("#review-edit-form"),
|
var el = $(e.target);
|
||||||
$review = $(this).parents(".review"),
|
if (el.attr('href') == '#review_flag_reason_other') {
|
||||||
rating = $review.attr("data-rating"),
|
$popup
|
||||||
edit_url = $("a.permalink", $review).attr("href") + "edit";
|
.addClass('other')
|
||||||
$cancel = $("#review-edit-cancel");
|
.on('submit', 'form', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
$review.attr("action", edit_url);
|
var note = $popup.find('#id_note').val();
|
||||||
$form.detach().insertAfter($review);
|
if (!note) {
|
||||||
$("#id_title").val($review.children("h5").text());
|
alert(gettext('Your input is required'));
|
||||||
$(".ratingwidget input:radio[value=" + rating + "]", $form).click();
|
} else {
|
||||||
$("#id_body").val($review.children("p.review-body").text());
|
addFlag('review_flag_reason_other', note);
|
||||||
$review.hide();
|
}
|
||||||
$form.show();
|
})
|
||||||
|
.setPos(ct)
|
||||||
function done_edit() {
|
.find('input[type=text]')
|
||||||
$form.off().hide();
|
.focus();
|
||||||
$review.show();
|
} else {
|
||||||
$cancel.off();
|
addFlag(el.attr('href').slice(1));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$cancel.click(function(e) {
|
$popup.html(report);
|
||||||
e.preventDefault();
|
return { pointTo: ct };
|
||||||
done_edit();
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
$form.submit(function (e) {
|
$('.primary').on('click', '.review-edit', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$.ajax({type: 'POST',
|
var $form = $('#review-edit-form'),
|
||||||
url: edit_url,
|
$review = $(this).parents('.review'),
|
||||||
data: $form.serialize(),
|
rating = $review.attr('data-rating'),
|
||||||
success: function(response, status) {
|
edit_url = $('a.permalink', $review).attr('href') + 'edit';
|
||||||
$review.children("h5").text($("#id_title").val());
|
$cancel = $('#review-edit-cancel');
|
||||||
rating = $(".ratingwidget input:radio:checked", $form).val();
|
|
||||||
$(".stars", $review).removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5').addClass('stars-' + rating);
|
$review.attr('action', edit_url);
|
||||||
rating = $review.attr("data-rating", rating);
|
$form.detach().insertAfter($review);
|
||||||
$review.children("p.review-body").text($("#id_body").val());
|
$('#id_title').val($review.children('h5').text());
|
||||||
done_edit();
|
$('.ratingwidget input:radio[value=' + rating + ']', $form).click();
|
||||||
},
|
$('#id_body').val($review.children('p.review-body').text());
|
||||||
dataType: 'json'
|
$review.hide();
|
||||||
});
|
$form.show();
|
||||||
return false;
|
|
||||||
});
|
function done_edit() {
|
||||||
|
$form.off().hide();
|
||||||
|
$review.show();
|
||||||
|
$cancel.off();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cancel.click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
done_edit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$form.submit(function (e) {
|
||||||
$('.delete-review').click(function(e) {
|
e.preventDefault();
|
||||||
e.preventDefault();
|
$.ajax({
|
||||||
var target = $(e.target);
|
type: 'POST',
|
||||||
$.post(target.attr('href'), function() {
|
url: edit_url,
|
||||||
target.replaceWith(gettext('Marked for deletion'));
|
data: $form.serialize(),
|
||||||
});
|
success: function (response, status) {
|
||||||
target.closest('.review').addClass('deleted');
|
$review.children('h5').text($('#id_title').val());
|
||||||
|
rating = $('.ratingwidget input:radio:checked', $form).val();
|
||||||
|
$('.stars', $review)
|
||||||
|
.removeClass('stars-0 stars-1 stars-2 stars-3 stars-4 stars-5')
|
||||||
|
.addClass('stars-' + rating);
|
||||||
|
rating = $review.attr('data-rating', rating);
|
||||||
|
$review.children('p.review-body').text($('#id_body').val());
|
||||||
|
done_edit();
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
});
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$("select[name='rating']").ratingwidget();
|
$('.delete-review').click(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var target = $(e.target);
|
||||||
|
$.post(target.attr('href'), function () {
|
||||||
|
target.replaceWith(gettext('Marked for deletion'));
|
||||||
|
});
|
||||||
|
target.closest('.review').addClass('deleted');
|
||||||
|
});
|
||||||
|
|
||||||
$('.review-flagged.disabled input:not([type=hidden])').prop('disabled', true);
|
$("select[name='rating']").ratingwidget();
|
||||||
|
|
||||||
|
$('.review-flagged.disabled input:not([type=hidden])').prop('disabled', true);
|
||||||
});
|
});
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,251 +1,297 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
$('#theme-wizard').each(initThemeWizard);
|
||||||
|
|
||||||
$('#theme-wizard').each(initThemeWizard);
|
var MAX_STATICTHEME_SIZE = 7 * 1024 * 1024;
|
||||||
|
|
||||||
var MAX_STATICTHEME_SIZE = 7 * 1024 * 1024;
|
function initThemeWizard() {
|
||||||
|
var $wizard = $(this);
|
||||||
|
var preLoadBlob = null;
|
||||||
|
var headerImageError = false;
|
||||||
|
|
||||||
function initThemeWizard() {
|
function getFile() {
|
||||||
var $wizard = $(this);
|
file_selector = $wizard.find('#header-img')[0];
|
||||||
var preLoadBlob = null;
|
file = file_selector.files[0];
|
||||||
var headerImageError = false;
|
if (
|
||||||
|
file &&
|
||||||
function getFile() {
|
$wizard
|
||||||
file_selector = $wizard.find('#header-img')[0];
|
.find('#header-img')
|
||||||
file = file_selector.files[0];
|
.attr('accept')
|
||||||
if (file && $wizard.find('#header-img').attr('accept').split(',').indexOf(file.type) == -1)
|
.split(',')
|
||||||
return null;
|
.indexOf(file.type) == -1
|
||||||
return file ? file : preLoadBlob;
|
)
|
||||||
}
|
return null;
|
||||||
|
return file ? file : preLoadBlob;
|
||||||
$wizard.on('click', '.reset', _pd(function() {
|
|
||||||
var $this = $(this),
|
|
||||||
$row = $this.closest('.row');
|
|
||||||
$row.find('input[type="file"]').click();
|
|
||||||
}));
|
|
||||||
|
|
||||||
$wizard.on('change', 'input[type="file"]', function() {
|
|
||||||
var $row = $(this).closest('.row');
|
|
||||||
var reader = new FileReader(),
|
|
||||||
file = getFile();
|
|
||||||
if (!file) return; // don't do anything if no file selected.
|
|
||||||
var $preview_img = $row.find('.preview');
|
|
||||||
|
|
||||||
reader.onload = function(e) {
|
|
||||||
$preview_img.attr('src', e.target.result);
|
|
||||||
$preview_img.show().addClass('loaded');
|
|
||||||
$row.find('.reset').show().css('display', 'block');
|
|
||||||
$row.find('input[type=file], .note').hide();
|
|
||||||
var filename = file.name.replace(/\.[^/.]+$/, "");
|
|
||||||
$wizard.find('a.download').attr('download', filename + ".zip");
|
|
||||||
var name_input = $wizard.find('#theme-name');
|
|
||||||
if (!name_input.val()) {
|
|
||||||
name_input.val(filename);
|
|
||||||
}
|
|
||||||
updateManifest();
|
|
||||||
};
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
});
|
|
||||||
$wizard.find('input[type="file"]').trigger('change');
|
|
||||||
|
|
||||||
$wizard.find('img.preview').on('load', function(e) {
|
|
||||||
var $svg_img = $('#svg-header-img'),
|
|
||||||
$svg = $('#preview-svg-root');
|
|
||||||
$svg_img.attr('href', ($svg_img.src = e.target.src));
|
|
||||||
$svg_img.attr('height', e.target.naturalHeight);
|
|
||||||
var meetOrSlice = (e.target.naturalWidth < $svg.width())? 'meet' : 'slice';
|
|
||||||
$svg_img.attr('preserveAspectRatio', 'xMaxYMin '+ meetOrSlice);
|
|
||||||
});
|
|
||||||
|
|
||||||
$wizard.find('#theme-header').each(function(index, element) {
|
|
||||||
var img_src = $(element).data('existing-header');
|
|
||||||
// If we already have a preview from a selected file don't overwrite it.
|
|
||||||
if (getFile() || !img_src) return;
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("GET", window.location.href + "/background");
|
|
||||||
xhr.responseType = "json";
|
|
||||||
// load the image as a blob so we can treat it as a File
|
|
||||||
xhr.onload = function() {
|
|
||||||
jsonResponse = xhr.response;
|
|
||||||
preLoadBlob = b64toBlob(jsonResponse[img_src]);
|
|
||||||
preLoadBlob.name = img_src;
|
|
||||||
$wizard.find('input[type="file"]').trigger('change');
|
|
||||||
};
|
|
||||||
xhr.send();
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateManifest() {
|
|
||||||
textarea = $wizard.find('#manifest').val(generateManifest());
|
|
||||||
toggleSubmitIfNeeded();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleSubmitIfNeeded() {
|
|
||||||
$wizard.find('button.upload').attr('disabled', ! required_fields_present());
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateManifest() {
|
|
||||||
var headerFile = getFile(),
|
|
||||||
headerPath = headerFile ? headerFile.name : "";
|
|
||||||
|
|
||||||
function colVal(id) {
|
|
||||||
return $wizard.find('#' + id).val();
|
|
||||||
}
|
|
||||||
|
|
||||||
var colors = {
|
|
||||||
"frame": colVal('frame'),
|
|
||||||
"tab_background_text": colVal('tab_background_text'),
|
|
||||||
"toolbar": colVal('toolbar'),
|
|
||||||
"bookmark_text": colVal('bookmark_text'),
|
|
||||||
"toolbar_field": colVal('toolbar_field'),
|
|
||||||
"toolbar_field_text": colVal('toolbar_field_text')
|
|
||||||
};
|
|
||||||
colors = _.omit(colors, function(value) {return value === "";});
|
|
||||||
|
|
||||||
manifest = {
|
|
||||||
name: $wizard.find('#theme-name').val(),
|
|
||||||
manifest_version: 2,
|
|
||||||
version: $wizard.data('version'),
|
|
||||||
theme: {
|
|
||||||
images: {
|
|
||||||
theme_frame: headerPath
|
|
||||||
},
|
|
||||||
colors: colors
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return JSON.stringify(manifest, null, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildZip() {
|
|
||||||
var zip = new JSZip();
|
|
||||||
zip.file('manifest.json', generateManifest());
|
|
||||||
var header_img = getFile();
|
|
||||||
if (header_img) {
|
|
||||||
zip.file(header_img.name, header_img);
|
|
||||||
}
|
|
||||||
return zip;
|
|
||||||
}
|
|
||||||
|
|
||||||
var $color = $wizard.find('input.color-picker');
|
|
||||||
$color.change(function() {
|
|
||||||
var $this = $(this),
|
|
||||||
color_property_selector = '.' + $this[0].id,
|
|
||||||
$svg_element = $(color_property_selector),
|
|
||||||
// If there's no value set and we have a fallback color we can use that instead
|
|
||||||
$have_fallback = $(color_property_selector + '[data-fallback]').not('[data-fallback=' + $this[0].id + ']');
|
|
||||||
if (!$this.val()) {
|
|
||||||
$svg_element.attr('fill', $svg_element.data('fill'));
|
|
||||||
$have_fallback.attr('fill', $('#' + $svg_element.data('fallback')).val())
|
|
||||||
.addClass($svg_element.data('fallback'));
|
|
||||||
} else {
|
|
||||||
$have_fallback.removeClass($svg_element.data('fallback'));
|
|
||||||
$svg_element.attr('fill', $this.val());
|
|
||||||
}
|
|
||||||
updateManifest();
|
|
||||||
}).trigger('change');
|
|
||||||
|
|
||||||
$color.minicolors({
|
|
||||||
dataUris: true,
|
|
||||||
opacity: true,
|
|
||||||
format: 'rgb',
|
|
||||||
change: function() {
|
|
||||||
$color.trigger('change');
|
|
||||||
updateManifest();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/* Force the pop-up panel ltr or the images end up in the wrong
|
|
||||||
position. */
|
|
||||||
$wizard.find('div.minicolors-panel').attr('dir', 'ltr');
|
|
||||||
|
|
||||||
/* The submit button availability needs to follow changes to the theme
|
|
||||||
name as soon as they happen, to react properly if it's modified but
|
|
||||||
the user hasn't focused something else yet */
|
|
||||||
$wizard.on('input', '#theme-name', toggleSubmitIfNeeded);
|
|
||||||
/* We update the full manifest when a proper change event is triggered,
|
|
||||||
the user has finished editing the name at this point. */
|
|
||||||
$wizard.on('change', '#theme-name', updateManifest);
|
|
||||||
|
|
||||||
$wizard.on('click', 'button.upload', _pd(function(event) {
|
|
||||||
var $button = $(event.target);
|
|
||||||
var zip = buildZip();
|
|
||||||
$button.addClass('uploading').addClass('disabled')
|
|
||||||
.data('upload-text', $button.text())
|
|
||||||
.text($button.data('uploading-text'));
|
|
||||||
|
|
||||||
zip.generateAsync({type: 'blob'}).then(function (blob) {
|
|
||||||
if (blob.size > MAX_STATICTHEME_SIZE) {
|
|
||||||
headerImageError = true;
|
|
||||||
throw format(gettext("Maximum upload size is {0} - choose a smaller background image."), fileSizeFormat(MAX_STATICTHEME_SIZE));
|
|
||||||
}
|
|
||||||
return blob;
|
|
||||||
}).then(function (blob) {
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append('upload', blob, 'upload.zip');
|
|
||||||
$.ajax({
|
|
||||||
type: 'POST',
|
|
||||||
url: $button.attr('formaction'),
|
|
||||||
data: formData,
|
|
||||||
processData: false,
|
|
||||||
contentType: false
|
|
||||||
}).done(function (data){
|
|
||||||
$('#id_upload').val(data.upload);
|
|
||||||
uploadDone(data);
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
// Fake the validation so we can display as an error.
|
|
||||||
uploadDone({validation:{
|
|
||||||
errors:1,
|
|
||||||
messages:[{message:err}]
|
|
||||||
}});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
function uploadDone(data) {
|
|
||||||
if (!data.validation) {
|
|
||||||
setTimeout(function() {
|
|
||||||
$.ajax({
|
|
||||||
url: data.url,
|
|
||||||
dataType: 'json',
|
|
||||||
success: uploadDone,
|
|
||||||
error: function (xhr, text, error) {
|
|
||||||
if (xhr.responseJSON && xhr.responseJSON.validation) {
|
|
||||||
// even though we got an error response code, it's validation json.
|
|
||||||
data = xhr.responseJSON;
|
|
||||||
} else {
|
|
||||||
// Fake the validation so we can display as an error.
|
|
||||||
data = {
|
|
||||||
validation:{
|
|
||||||
errors:1,
|
|
||||||
messages:[{message:error}]
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
uploadDone(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
if (data.validation.errors === 0 ) {
|
|
||||||
$wizard.find('#submit-describe').submit();
|
|
||||||
} else {
|
|
||||||
data.validation.messages.forEach(function(message) {
|
|
||||||
if (headerImageError) {
|
|
||||||
$('.header-image-error').append($('<li>', {'html': message.message}));
|
|
||||||
} else {
|
|
||||||
$('.general-validation-error').append($('<li>', {'html': message.message}));
|
|
||||||
}
|
|
||||||
console.error(message);
|
|
||||||
});
|
|
||||||
$('button.upload').removeClass('uploading').removeClass('disabled')
|
|
||||||
.text($('button.upload').data('upload-text'));
|
|
||||||
headerImageError = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function required_fields_present() {
|
|
||||||
return $wizard.find('#theme-name').val() !== "" &&
|
|
||||||
getFile() &&
|
|
||||||
$wizard.find('#frame').val() !== "" &&
|
|
||||||
$wizard.find('#tab_background_text').val() !== "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$wizard.on(
|
||||||
|
'click',
|
||||||
|
'.reset',
|
||||||
|
_pd(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
$row = $this.closest('.row');
|
||||||
|
$row.find('input[type="file"]').click();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
$wizard.on('change', 'input[type="file"]', function () {
|
||||||
|
var $row = $(this).closest('.row');
|
||||||
|
var reader = new FileReader(),
|
||||||
|
file = getFile();
|
||||||
|
if (!file) return; // don't do anything if no file selected.
|
||||||
|
var $preview_img = $row.find('.preview');
|
||||||
|
|
||||||
|
reader.onload = function (e) {
|
||||||
|
$preview_img.attr('src', e.target.result);
|
||||||
|
$preview_img.show().addClass('loaded');
|
||||||
|
$row.find('.reset').show().css('display', 'block');
|
||||||
|
$row.find('input[type=file], .note').hide();
|
||||||
|
var filename = file.name.replace(/\.[^/.]+$/, '');
|
||||||
|
$wizard.find('a.download').attr('download', filename + '.zip');
|
||||||
|
var name_input = $wizard.find('#theme-name');
|
||||||
|
if (!name_input.val()) {
|
||||||
|
name_input.val(filename);
|
||||||
|
}
|
||||||
|
updateManifest();
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
});
|
||||||
|
$wizard.find('input[type="file"]').trigger('change');
|
||||||
|
|
||||||
|
$wizard.find('img.preview').on('load', function (e) {
|
||||||
|
var $svg_img = $('#svg-header-img'),
|
||||||
|
$svg = $('#preview-svg-root');
|
||||||
|
$svg_img.attr('href', ($svg_img.src = e.target.src));
|
||||||
|
$svg_img.attr('height', e.target.naturalHeight);
|
||||||
|
var meetOrSlice = e.target.naturalWidth < $svg.width() ? 'meet' : 'slice';
|
||||||
|
$svg_img.attr('preserveAspectRatio', 'xMaxYMin ' + meetOrSlice);
|
||||||
|
});
|
||||||
|
|
||||||
|
$wizard.find('#theme-header').each(function (index, element) {
|
||||||
|
var img_src = $(element).data('existing-header');
|
||||||
|
// If we already have a preview from a selected file don't overwrite it.
|
||||||
|
if (getFile() || !img_src) return;
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', window.location.href + '/background');
|
||||||
|
xhr.responseType = 'json';
|
||||||
|
// load the image as a blob so we can treat it as a File
|
||||||
|
xhr.onload = function () {
|
||||||
|
jsonResponse = xhr.response;
|
||||||
|
preLoadBlob = b64toBlob(jsonResponse[img_src]);
|
||||||
|
preLoadBlob.name = img_src;
|
||||||
|
$wizard.find('input[type="file"]').trigger('change');
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateManifest() {
|
||||||
|
textarea = $wizard.find('#manifest').val(generateManifest());
|
||||||
|
toggleSubmitIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSubmitIfNeeded() {
|
||||||
|
$wizard
|
||||||
|
.find('button.upload')
|
||||||
|
.attr('disabled', !required_fields_present());
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateManifest() {
|
||||||
|
var headerFile = getFile(),
|
||||||
|
headerPath = headerFile ? headerFile.name : '';
|
||||||
|
|
||||||
|
function colVal(id) {
|
||||||
|
return $wizard.find('#' + id).val();
|
||||||
|
}
|
||||||
|
|
||||||
|
var colors = {
|
||||||
|
frame: colVal('frame'),
|
||||||
|
tab_background_text: colVal('tab_background_text'),
|
||||||
|
toolbar: colVal('toolbar'),
|
||||||
|
bookmark_text: colVal('bookmark_text'),
|
||||||
|
toolbar_field: colVal('toolbar_field'),
|
||||||
|
toolbar_field_text: colVal('toolbar_field_text'),
|
||||||
|
};
|
||||||
|
colors = _.omit(colors, function (value) {
|
||||||
|
return value === '';
|
||||||
|
});
|
||||||
|
|
||||||
|
manifest = {
|
||||||
|
name: $wizard.find('#theme-name').val(),
|
||||||
|
manifest_version: 2,
|
||||||
|
version: $wizard.data('version'),
|
||||||
|
theme: {
|
||||||
|
images: {
|
||||||
|
theme_frame: headerPath,
|
||||||
|
},
|
||||||
|
colors: colors,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return JSON.stringify(manifest, null, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildZip() {
|
||||||
|
var zip = new JSZip();
|
||||||
|
zip.file('manifest.json', generateManifest());
|
||||||
|
var header_img = getFile();
|
||||||
|
if (header_img) {
|
||||||
|
zip.file(header_img.name, header_img);
|
||||||
|
}
|
||||||
|
return zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $color = $wizard.find('input.color-picker');
|
||||||
|
$color
|
||||||
|
.change(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
color_property_selector = '.' + $this[0].id,
|
||||||
|
$svg_element = $(color_property_selector),
|
||||||
|
// If there's no value set and we have a fallback color we can use that instead
|
||||||
|
$have_fallback = $(color_property_selector + '[data-fallback]').not(
|
||||||
|
'[data-fallback=' + $this[0].id + ']',
|
||||||
|
);
|
||||||
|
if (!$this.val()) {
|
||||||
|
$svg_element.attr('fill', $svg_element.data('fill'));
|
||||||
|
$have_fallback
|
||||||
|
.attr('fill', $('#' + $svg_element.data('fallback')).val())
|
||||||
|
.addClass($svg_element.data('fallback'));
|
||||||
|
} else {
|
||||||
|
$have_fallback.removeClass($svg_element.data('fallback'));
|
||||||
|
$svg_element.attr('fill', $this.val());
|
||||||
|
}
|
||||||
|
updateManifest();
|
||||||
|
})
|
||||||
|
.trigger('change');
|
||||||
|
|
||||||
|
$color.minicolors({
|
||||||
|
dataUris: true,
|
||||||
|
opacity: true,
|
||||||
|
format: 'rgb',
|
||||||
|
change: function () {
|
||||||
|
$color.trigger('change');
|
||||||
|
updateManifest();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
/* Force the pop-up panel ltr or the images end up in the wrong
|
||||||
|
position. */
|
||||||
|
$wizard.find('div.minicolors-panel').attr('dir', 'ltr');
|
||||||
|
|
||||||
|
/* The submit button availability needs to follow changes to the theme
|
||||||
|
name as soon as they happen, to react properly if it's modified but
|
||||||
|
the user hasn't focused something else yet */
|
||||||
|
$wizard.on('input', '#theme-name', toggleSubmitIfNeeded);
|
||||||
|
/* We update the full manifest when a proper change event is triggered,
|
||||||
|
the user has finished editing the name at this point. */
|
||||||
|
$wizard.on('change', '#theme-name', updateManifest);
|
||||||
|
|
||||||
|
$wizard.on(
|
||||||
|
'click',
|
||||||
|
'button.upload',
|
||||||
|
_pd(function (event) {
|
||||||
|
var $button = $(event.target);
|
||||||
|
var zip = buildZip();
|
||||||
|
$button
|
||||||
|
.addClass('uploading')
|
||||||
|
.addClass('disabled')
|
||||||
|
.data('upload-text', $button.text())
|
||||||
|
.text($button.data('uploading-text'));
|
||||||
|
|
||||||
|
zip
|
||||||
|
.generateAsync({ type: 'blob' })
|
||||||
|
.then(function (blob) {
|
||||||
|
if (blob.size > MAX_STATICTHEME_SIZE) {
|
||||||
|
headerImageError = true;
|
||||||
|
throw format(
|
||||||
|
gettext(
|
||||||
|
'Maximum upload size is {0} - choose a smaller background image.',
|
||||||
|
),
|
||||||
|
fileSizeFormat(MAX_STATICTHEME_SIZE),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return blob;
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
function (blob) {
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('upload', blob, 'upload.zip');
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: $button.attr('formaction'),
|
||||||
|
data: formData,
|
||||||
|
processData: false,
|
||||||
|
contentType: false,
|
||||||
|
}).done(function (data) {
|
||||||
|
$('#id_upload').val(data.upload);
|
||||||
|
uploadDone(data);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (err) {
|
||||||
|
// Fake the validation so we can display as an error.
|
||||||
|
uploadDone({
|
||||||
|
validation: {
|
||||||
|
errors: 1,
|
||||||
|
messages: [{ message: err }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
function uploadDone(data) {
|
||||||
|
if (!data.validation) {
|
||||||
|
setTimeout(function () {
|
||||||
|
$.ajax({
|
||||||
|
url: data.url,
|
||||||
|
dataType: 'json',
|
||||||
|
success: uploadDone,
|
||||||
|
error: function (xhr, text, error) {
|
||||||
|
if (xhr.responseJSON && xhr.responseJSON.validation) {
|
||||||
|
// even though we got an error response code, it's validation json.
|
||||||
|
data = xhr.responseJSON;
|
||||||
|
} else {
|
||||||
|
// Fake the validation so we can display as an error.
|
||||||
|
data = {
|
||||||
|
validation: {
|
||||||
|
errors: 1,
|
||||||
|
messages: [{ message: error }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
uploadDone(data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
if (data.validation.errors === 0) {
|
||||||
|
$wizard.find('#submit-describe').submit();
|
||||||
|
} else {
|
||||||
|
data.validation.messages.forEach(function (message) {
|
||||||
|
if (headerImageError) {
|
||||||
|
$('.header-image-error').append(
|
||||||
|
$('<li>', { html: message.message }),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$('.general-validation-error').append(
|
||||||
|
$('<li>', { html: message.message }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.error(message);
|
||||||
|
});
|
||||||
|
$('button.upload')
|
||||||
|
.removeClass('uploading')
|
||||||
|
.removeClass('disabled')
|
||||||
|
.text($('button.upload').data('upload-text'));
|
||||||
|
headerImageError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function required_fields_present() {
|
||||||
|
return (
|
||||||
|
$wizard.find('#theme-name').val() !== '' &&
|
||||||
|
getFile() &&
|
||||||
|
$wizard.find('#frame').val() !== '' &&
|
||||||
|
$wizard.find('#tab_background_text').val() !== ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,61 +15,61 @@
|
||||||
*
|
*
|
||||||
* Requires jQuery and jQuery Cookie plugin.
|
* Requires jQuery and jQuery Cookie plugin.
|
||||||
*/
|
*/
|
||||||
z.Storage = (function() {
|
z.Storage = (function () {
|
||||||
var cookieStorage = {
|
var cookieStorage = {
|
||||||
expires: 30,
|
expires: 30,
|
||||||
getItem: function(key) {
|
getItem: function (key) {
|
||||||
return $.cookie(key);
|
return $.cookie(key);
|
||||||
},
|
},
|
||||||
setItem: function(key, value) {
|
setItem: function (key, value) {
|
||||||
return $.cookie(key, value, {path: '/', expires: this.expires});
|
return $.cookie(key, value, { path: '/', expires: this.expires });
|
||||||
},
|
},
|
||||||
removeItem: function(key) {
|
removeItem: function (key) {
|
||||||
return $.cookie(key, null);
|
return $.cookie(key, null);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
var engine = z.capabilities.localStorage ? localStorage : cookieStorage;
|
var engine = z.capabilities.localStorage ? localStorage : cookieStorage;
|
||||||
return function(namespace) {
|
return function (namespace) {
|
||||||
namespace = namespace ? namespace + '-' : '';
|
namespace = namespace ? namespace + '-' : '';
|
||||||
return {
|
return {
|
||||||
get: function(key) {
|
get: function (key) {
|
||||||
return engine.getItem(namespace + key);
|
return engine.getItem(namespace + key);
|
||||||
},
|
},
|
||||||
set: function(key, value) {
|
set: function (key, value) {
|
||||||
return engine.setItem(namespace + key, value);
|
return engine.setItem(namespace + key, value);
|
||||||
},
|
},
|
||||||
remove: function(key) {
|
remove: function (key) {
|
||||||
return engine.removeItem(namespace + key);
|
return engine.removeItem(namespace + key);
|
||||||
}
|
},
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
z.SessionStorage = (function() {
|
z.SessionStorage = (function () {
|
||||||
var cookieStorage = {
|
var cookieStorage = {
|
||||||
getItem: function(key) {
|
getItem: function (key) {
|
||||||
return $.cookie(key);
|
return $.cookie(key);
|
||||||
},
|
},
|
||||||
setItem: function(key, value) {
|
setItem: function (key, value) {
|
||||||
return $.cookie(key, value, {path: '/'});
|
return $.cookie(key, value, { path: '/' });
|
||||||
},
|
},
|
||||||
removeItem: function(key) {
|
removeItem: function (key) {
|
||||||
return $.cookie(key, null);
|
return $.cookie(key, null);
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
var engine = z.capabilities.localStorage ? sessionStorage : cookieStorage;
|
var engine = z.capabilities.localStorage ? sessionStorage : cookieStorage;
|
||||||
return function(namespace) {
|
return function (namespace) {
|
||||||
namespace = namespace ? namespace + '-' : '';
|
namespace = namespace ? namespace + '-' : '';
|
||||||
return {
|
return {
|
||||||
get: function(key) {
|
get: function (key) {
|
||||||
return engine.getItem(namespace + key);
|
return engine.getItem(namespace + key);
|
||||||
},
|
},
|
||||||
set: function(key, value) {
|
set: function (key, value) {
|
||||||
return engine.setItem(namespace + key, value);
|
return engine.setItem(namespace + key, value);
|
||||||
},
|
},
|
||||||
remove: function(key) {
|
remove: function (key) {
|
||||||
return engine.removeItem(namespace + key);
|
return engine.removeItem(namespace + key);
|
||||||
}
|
},
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -7,131 +7,131 @@
|
||||||
* .tabify() plugin so it can be accessed later.
|
* .tabify() plugin so it can be accessed later.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Tabs = function(el) {
|
var Tabs = function (el) {
|
||||||
this.root = $(el);
|
this.root = $(el);
|
||||||
this.init();
|
this.init();
|
||||||
};
|
};
|
||||||
|
|
||||||
Tabs.prototype = {
|
Tabs.prototype = {
|
||||||
init: function() {
|
init: function () {
|
||||||
this.root.addClass('tab-wrapper');
|
this.root.addClass('tab-wrapper');
|
||||||
this.tabMap = {};
|
this.tabMap = {};
|
||||||
this.panelMap = {};
|
this.panelMap = {};
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
||||||
this.select();
|
this.select();
|
||||||
|
|
||||||
/* Bind hashchange, trigger event to check for existing hash. */
|
/* Bind hashchange, trigger event to check for existing hash. */
|
||||||
var self = this;
|
var self = this;
|
||||||
$(document).on('hashchange', function(e) {
|
$(document)
|
||||||
self.hashChange(e);
|
.on('hashchange', function (e) {
|
||||||
}).trigger('hashchange');
|
self.hashChange(e);
|
||||||
},
|
})
|
||||||
|
.trigger('hashchange');
|
||||||
|
},
|
||||||
|
|
||||||
/* Find and prepare all the tabs and panels. Can be called multiple times,
|
/* Find and prepare all the tabs and panels. Can be called multiple times,
|
||||||
* e.g. to update tabs after insertion/deletion.
|
* e.g. to update tabs after insertion/deletion.
|
||||||
*/
|
*/
|
||||||
reset: function(o) {
|
reset: function (o) {
|
||||||
this.findTabs();
|
this.findTabs();
|
||||||
this.findPanels();
|
this.findPanels();
|
||||||
this.styleTabs(this.tabs);
|
this.styleTabs(this.tabs);
|
||||||
this.stylePanels(this.panels);
|
this.stylePanels(this.panels);
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Find tabs (li a[href]) and bind their click event. */
|
|
||||||
findTabs: function() {
|
|
||||||
this.list = this.root.find('ol,ul').eq(0);
|
|
||||||
this.tabs = $('li:has(a[href])', this.list);
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var cb = function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
self.select($(e.target).attr('href'), true);
|
|
||||||
$("a", this).blur();
|
|
||||||
};
|
|
||||||
this.tabs.off('click', cb).click(cb);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Get the fragment this tab points to. */
|
|
||||||
getHash: function(tab) {
|
|
||||||
return $(tab).find('a').attr('href');
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Find all the panels to go along with the tabs. */
|
|
||||||
findPanels: function() {
|
|
||||||
var self = this;
|
|
||||||
var panels = [];
|
|
||||||
this.tabs.each(function() {
|
|
||||||
var hash = self.getHash(this);
|
|
||||||
var panel = self.root.find(hash)[0];
|
|
||||||
if (panel) {
|
|
||||||
self.tabMap[hash] = this;
|
|
||||||
self.panelMap[hash] = panel;
|
|
||||||
panels.push(panel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.panels = $(panels);
|
|
||||||
},
|
|
||||||
|
|
||||||
styleTabs: function(tabs) {
|
|
||||||
tabs = tabs || self.tabs;
|
|
||||||
this.list.addClass('tab-nav');
|
|
||||||
$(tabs).addClass('tab');
|
|
||||||
},
|
|
||||||
|
|
||||||
stylePanels: function(panels) {
|
|
||||||
panels = panels || self.panels;
|
|
||||||
$(panels).addClass('tab-panel');
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Focus on the tab pointing to #hash.
|
|
||||||
* If hash is not given, the first tab will be selected.
|
|
||||||
* If updateHash is true, location.hash will be updated.
|
|
||||||
*/
|
|
||||||
select: function(hash, updateHash) {
|
|
||||||
if (typeof hash === 'undefined') {
|
|
||||||
if (!this.tabs.filter('.tab-selected').length) {
|
|
||||||
return this.select(this.getHash(this.tabs[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tab = this.tabMap[hash],
|
|
||||||
panel = this.panelMap[hash];
|
|
||||||
|
|
||||||
this.tabs.filter('.tab-selected').removeClass('tab-selected');
|
|
||||||
this.panels.filter('.tab-selected').removeClass('tab-selected');
|
|
||||||
$([tab, panel]).addClass('tab-selected');
|
|
||||||
|
|
||||||
this.root.trigger('tabselect', {tab: tab, panel: panel});
|
|
||||||
|
|
||||||
if (updateHash) {
|
|
||||||
safeHashChange(hash);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Handler for onhashchange. */
|
|
||||||
hashChange: function(e) {
|
|
||||||
if (location.hash && _.haskey(this.tabMap, location.hash)) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.select(location.hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
$.fn.tabify = function() {
|
|
||||||
this.each(function() {
|
|
||||||
this.tab = new Tabs(this);
|
|
||||||
});
|
|
||||||
return this;
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Find tabs (li a[href]) and bind their click event. */
|
||||||
|
findTabs: function () {
|
||||||
|
this.list = this.root.find('ol,ul').eq(0);
|
||||||
|
this.tabs = $('li:has(a[href])', this.list);
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var cb = function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
self.select($(e.target).attr('href'), true);
|
||||||
|
$('a', this).blur();
|
||||||
|
};
|
||||||
|
this.tabs.off('click', cb).click(cb);
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Get the fragment this tab points to. */
|
||||||
|
getHash: function (tab) {
|
||||||
|
return $(tab).find('a').attr('href');
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Find all the panels to go along with the tabs. */
|
||||||
|
findPanels: function () {
|
||||||
|
var self = this;
|
||||||
|
var panels = [];
|
||||||
|
this.tabs.each(function () {
|
||||||
|
var hash = self.getHash(this);
|
||||||
|
var panel = self.root.find(hash)[0];
|
||||||
|
if (panel) {
|
||||||
|
self.tabMap[hash] = this;
|
||||||
|
self.panelMap[hash] = panel;
|
||||||
|
panels.push(panel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.panels = $(panels);
|
||||||
|
},
|
||||||
|
|
||||||
|
styleTabs: function (tabs) {
|
||||||
|
tabs = tabs || self.tabs;
|
||||||
|
this.list.addClass('tab-nav');
|
||||||
|
$(tabs).addClass('tab');
|
||||||
|
},
|
||||||
|
|
||||||
|
stylePanels: function (panels) {
|
||||||
|
panels = panels || self.panels;
|
||||||
|
$(panels).addClass('tab-panel');
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Focus on the tab pointing to #hash.
|
||||||
|
* If hash is not given, the first tab will be selected.
|
||||||
|
* If updateHash is true, location.hash will be updated.
|
||||||
|
*/
|
||||||
|
select: function (hash, updateHash) {
|
||||||
|
if (typeof hash === 'undefined') {
|
||||||
|
if (!this.tabs.filter('.tab-selected').length) {
|
||||||
|
return this.select(this.getHash(this.tabs[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tab = this.tabMap[hash],
|
||||||
|
panel = this.panelMap[hash];
|
||||||
|
|
||||||
|
this.tabs.filter('.tab-selected').removeClass('tab-selected');
|
||||||
|
this.panels.filter('.tab-selected').removeClass('tab-selected');
|
||||||
|
$([tab, panel]).addClass('tab-selected');
|
||||||
|
|
||||||
|
this.root.trigger('tabselect', { tab: tab, panel: panel });
|
||||||
|
|
||||||
|
if (updateHash) {
|
||||||
|
safeHashChange(hash);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Handler for onhashchange. */
|
||||||
|
hashChange: function (e) {
|
||||||
|
if (location.hash && _.haskey(this.tabMap, location.hash)) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.select(location.hash);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$.fn.tabify = function () {
|
||||||
|
this.each(function () {
|
||||||
|
this.tab = new Tabs(this);
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/* Change location.hash without scrolling. */
|
/* Change location.hash without scrolling. */
|
||||||
var safeHashChange = function(hash) {
|
var safeHashChange = function (hash) {
|
||||||
var el = $(hash);
|
var el = $(hash);
|
||||||
el.attr('id', '');
|
el.attr('id', '');
|
||||||
location.hash = hash;
|
location.hash = hash;
|
||||||
el.attr('id', hash.slice(1));
|
el.attr('id', hash.slice(1));
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
$(document).ready(function(){
|
$(document).ready(function () {
|
||||||
if (!z.appMatchesUserAgent) {
|
if (!z.appMatchesUserAgent) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeCompat = function () {
|
||||||
|
var $el = $(this),
|
||||||
|
min = $el.attr('data-min'),
|
||||||
|
max = $el.attr('data-max');
|
||||||
|
if (
|
||||||
|
VersionCompare.compareVersions(z.browserVersion, min) < 0 ||
|
||||||
|
VersionCompare.compareVersions(z.browserVersion, max) > 0
|
||||||
|
) {
|
||||||
|
$el.addClass('incompatible');
|
||||||
|
var msg = format(
|
||||||
|
gettext('This theme is incompatible with your version of {0}'),
|
||||||
|
[z.appName],
|
||||||
|
);
|
||||||
|
$el.append('<div class="overlay"><p>' + msg + '</p></div>').hover(
|
||||||
|
function () {
|
||||||
|
$(this).children('.overlay').show();
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
$(this).children('.overlay').fadeOut();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var themeCompat = function() {
|
$('.browse-thumbs .thumbs li').each(themeCompat);
|
||||||
var $el = $(this),
|
|
||||||
min = $el.attr('data-min'),
|
|
||||||
max = $el.attr('data-max');
|
|
||||||
if (VersionCompare.compareVersions(z.browserVersion, min) < 0
|
|
||||||
|| VersionCompare.compareVersions(z.browserVersion, max) > 0) {
|
|
||||||
$el.addClass('incompatible');
|
|
||||||
var msg = format(gettext('This theme is incompatible with your version of {0}'),
|
|
||||||
[z.appName]);
|
|
||||||
$el.append('<div class="overlay"><p>' + msg + '</p></div>')
|
|
||||||
.hover(function() { $(this).children('.overlay').show(); },
|
|
||||||
function() { $(this).children('.overlay').fadeOut(); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$('.browse-thumbs .thumbs li').each(themeCompat);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,470 +1,529 @@
|
||||||
(function($) {
|
(function ($) {
|
||||||
/* jQuery.ScrollTo by Ariel Flesler */
|
/* jQuery.ScrollTo by Ariel Flesler */
|
||||||
$.fn.scrollTo = function(opts) {
|
$.fn.scrollTo = function (opts) {
|
||||||
if (!this.length) return this;
|
if (!this.length) return this;
|
||||||
opts = $.extend({
|
opts = $.extend(
|
||||||
duration: 250,
|
{
|
||||||
marginTop: 0,
|
duration: 250,
|
||||||
complete: undefined
|
marginTop: 0,
|
||||||
}, opts || { });
|
complete: undefined,
|
||||||
var top = this.offset().top - opts.marginTop;
|
},
|
||||||
$('html, body').animate({ 'scrollTop': top }, opts.duration, undefined, opts.complete);
|
opts || {},
|
||||||
return this;
|
);
|
||||||
};
|
var top = this.offset().top - opts.marginTop;
|
||||||
|
$('html, body').animate(
|
||||||
|
{ scrollTop: top },
|
||||||
|
opts.duration,
|
||||||
|
undefined,
|
||||||
|
opts.complete,
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
|
var win = $(window);
|
||||||
|
var doc = $(document);
|
||||||
|
|
||||||
(function($) {
|
$.fn.themeQueue = function () {
|
||||||
var win = $(window);
|
return this.each(function () {
|
||||||
var doc = $(document);
|
var queue = this;
|
||||||
|
var currentTheme = 0;
|
||||||
|
var cacheQueueHeight;
|
||||||
|
|
||||||
|
var $queueContext = $('.queue-context');
|
||||||
|
var actionConstants = $queueContext.data('actions');
|
||||||
|
|
||||||
$.fn.themeQueue = function() {
|
var themesList = $('div.theme', queue);
|
||||||
return this.each(function() {
|
var themes = themesList
|
||||||
var queue = this;
|
.map(function () {
|
||||||
var currentTheme = 0;
|
return {
|
||||||
var cacheQueueHeight;
|
element: this,
|
||||||
|
top: 0,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
|
||||||
var $queueContext = $('.queue-context');
|
function nthTheme(i) {
|
||||||
var actionConstants = $queueContext.data('actions');
|
return themesList[i];
|
||||||
|
}
|
||||||
|
|
||||||
var themesList = $('div.theme', queue);
|
doc.scroll(
|
||||||
var themes = themesList.map(function() {
|
_.throttle(function () {
|
||||||
return {
|
updateMetrics();
|
||||||
element: this,
|
var i = findCurrentTheme();
|
||||||
top: 0
|
if (i >= 0 && i != currentTheme) {
|
||||||
};
|
|
||||||
}).get();
|
|
||||||
|
|
||||||
function nthTheme(i) {
|
|
||||||
return themesList[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.scroll(_.throttle(function() {
|
|
||||||
updateMetrics();
|
|
||||||
var i = findCurrentTheme();
|
|
||||||
if (i >= 0 && i != currentTheme) {
|
|
||||||
switchTheme(findCurrentTheme());
|
|
||||||
}
|
|
||||||
// Undo sidebar-truncation fix in goToTheme if user goes
|
|
||||||
// into free-scrolling mode.
|
|
||||||
if (i === 0) {
|
|
||||||
$('.sidebar').removeClass('lineup');
|
|
||||||
}
|
|
||||||
}, 250));
|
|
||||||
|
|
||||||
doc.keyup(function(e) {
|
|
||||||
if (!$(queue).hasClass('shortcuts')) return;
|
|
||||||
|
|
||||||
// Ignore key-bindings when textarea focused.
|
|
||||||
if (fieldFocused(e) && e.which != z.keys.ENTER) return;
|
|
||||||
|
|
||||||
// For using Enter to submit textareas.
|
|
||||||
if (e.which == z.keys.ENTER && z.keys.ENTER in keymap) {
|
|
||||||
keymap[z.keys.ENTER]();
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = String.fromCharCode(e.which).toLowerCase();
|
|
||||||
|
|
||||||
if (!(key in keymap)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var action = keymap[key];
|
|
||||||
if (action && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
|
||||||
themeActions[action[0]](currentTheme, action[1]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Pressing Enter in text field doesn't add carriage return.
|
|
||||||
$('textarea').keypress(function(e) {
|
|
||||||
if (e.keyCode == z.keys.ENTER) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.theme', queue).removeClass('active');
|
|
||||||
updateMetrics();
|
|
||||||
switchTheme(findCurrentTheme());
|
switchTheme(findCurrentTheme());
|
||||||
|
}
|
||||||
|
// Undo sidebar-truncation fix in goToTheme if user goes
|
||||||
|
// into free-scrolling mode.
|
||||||
|
if (i === 0) {
|
||||||
|
$('.sidebar').removeClass('lineup');
|
||||||
|
}
|
||||||
|
}, 250),
|
||||||
|
);
|
||||||
|
|
||||||
function updateMetrics() {
|
doc.keyup(function (e) {
|
||||||
var queueHeight = $(queue).height();
|
if (!$(queue).hasClass('shortcuts')) return;
|
||||||
if (queueHeight === cacheQueueHeight) return;
|
|
||||||
cacheQueueHeight = queueHeight;
|
|
||||||
|
|
||||||
$.each(themes, function(i, obj) {
|
// Ignore key-bindings when textarea focused.
|
||||||
var elem = $(obj.element);
|
if (fieldFocused(e) && e.which != z.keys.ENTER) return;
|
||||||
obj.top = elem.offset().top + elem.outerHeight()/2;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThemeParent(elem) {
|
// For using Enter to submit textareas.
|
||||||
// Given an element (like an approve button),
|
if (e.which == z.keys.ENTER && z.keys.ENTER in keymap) {
|
||||||
// return the theme for which it is related to.
|
keymap[z.keys.ENTER]();
|
||||||
return $(elem).closest('.theme').data('id');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function goToTheme(i, delay, duration) {
|
var key = String.fromCharCode(e.which).toLowerCase();
|
||||||
delay = delay || 0;
|
|
||||||
duration = duration || 250;
|
|
||||||
setTimeout(function() {
|
|
||||||
if (i >= 0 && i < themes.length) {
|
|
||||||
$(themes[i].element).scrollTo({ duration: duration, marginTop: 20 });
|
|
||||||
}
|
|
||||||
}, delay);
|
|
||||||
$('.rq-dropdown').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function switchTheme(i) {
|
if (!(key in keymap)) {
|
||||||
if (!themes[currentTheme]) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$(themes[currentTheme].element).removeClass('active');
|
var action = keymap[key];
|
||||||
$(themes[i].element).addClass('active');
|
if (action && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
||||||
vertAlignSidebar(win, $('.theme.active'));
|
themeActions[action[0]](currentTheme, action[1]);
|
||||||
currentTheme = i;
|
}
|
||||||
$('.rq-dropdown').hide();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function findCurrentTheme() {
|
// Pressing Enter in text field doesn't add carriage return.
|
||||||
// Uses location of the window within the page to determine
|
$('textarea').keypress(function (e) {
|
||||||
// which theme we're currently looking at.
|
if (e.keyCode == z.keys.ENTER) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// $(window).scroll() fires too early.
|
$('.theme', queue).removeClass('active');
|
||||||
if (!themes[currentTheme]) {
|
updateMetrics();
|
||||||
return 0;
|
switchTheme(findCurrentTheme());
|
||||||
}
|
|
||||||
|
|
||||||
var pageTop = win.scrollTop();
|
function updateMetrics() {
|
||||||
if (pageTop <= themes[currentTheme].top) {
|
var queueHeight = $(queue).height();
|
||||||
for (var i = currentTheme - 1; i >= 0; i--) {
|
if (queueHeight === cacheQueueHeight) return;
|
||||||
if (themes[i].top < pageTop) {
|
cacheQueueHeight = queueHeight;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i+1;
|
|
||||||
} else {
|
|
||||||
for (var i = currentTheme; i < themes.length; i++) {
|
|
||||||
// Scroll down the themes until we find a theme
|
|
||||||
// that is at the top of our page. That is our current
|
|
||||||
// theme.
|
|
||||||
if (pageTop <= themes[i].top) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var keymap = {
|
$.each(themes, function (i, obj) {
|
||||||
j: ['next', null],
|
var elem = $(obj.element);
|
||||||
k: ['prev', null],
|
obj.top = elem.offset().top + elem.outerHeight() / 2;
|
||||||
a: ['approve', null],
|
|
||||||
r: ['reject_reason', null],
|
|
||||||
d: ['duplicate', null],
|
|
||||||
f: ['flag', null],
|
|
||||||
m: ['moreinfo', null]
|
|
||||||
};
|
|
||||||
// keymap[0] = ['other_reject_reason', 0];
|
|
||||||
for (var j =1; j <= 9; j++) {
|
|
||||||
keymap[j] = ['reject', j];
|
|
||||||
}
|
|
||||||
|
|
||||||
function setReviewed(i, action) {
|
|
||||||
var ac = actionConstants;
|
|
||||||
var actionMap = {};
|
|
||||||
actionMap[ac.moreinfo] = [gettext('Requested Info'), 'blue'];
|
|
||||||
actionMap[ac.flagged] = [gettext('Flagged'), 'red'];
|
|
||||||
actionMap[ac.duplicate] = [gettext('Duplicate'), 'red'];
|
|
||||||
actionMap[ac.reject] = [gettext('Rejected'), 'red'];
|
|
||||||
actionMap[ac.approve] = [gettext('Approved'), 'green'];
|
|
||||||
var text = actionMap[action][0];
|
|
||||||
var color = actionMap[action][1];
|
|
||||||
|
|
||||||
$(nthTheme(i)).addClass('reviewed');
|
|
||||||
$('.status', nthTheme(i)).removeClass('red blue green')
|
|
||||||
.addClass('reviewed ' + color).find('.status-text').text(text);
|
|
||||||
|
|
||||||
$('#reviewed-count').text($('div.theme.reviewed').length);
|
|
||||||
if ($(queue).hasClass('advance')) {
|
|
||||||
goToTheme(i + 1, 250);
|
|
||||||
} else {
|
|
||||||
delete keymap[z.keys.ENTER];
|
|
||||||
$('.rq-dropdown').hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isRejecting = false;
|
|
||||||
$(document).on('click', 'li.reject_reason', _pd(function(e) {
|
|
||||||
if (isRejecting) {
|
|
||||||
var i = getThemeParent(e.currentTarget);
|
|
||||||
var rejectId = $(this).data('id');
|
|
||||||
if (rejectId === 0) {
|
|
||||||
themeActions.other_reject_reason(i);
|
|
||||||
} else {
|
|
||||||
themeActions.reject(i, rejectId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
var themeActions = {
|
|
||||||
next: function (i) { goToTheme(i + 1); },
|
|
||||||
prev: function (i) { goToTheme(i - 1); },
|
|
||||||
|
|
||||||
approve: function (i) {
|
|
||||||
$('input.action', nthTheme(i)).val(actionConstants.approve);
|
|
||||||
setReviewed(i, actionConstants.approve);
|
|
||||||
},
|
|
||||||
|
|
||||||
reject_reason: function (i) {
|
|
||||||
// Open up dropdown of rejection reasons and set up
|
|
||||||
// key and click-bindings for choosing a reason. This
|
|
||||||
// function does not actually do the rejecting as the
|
|
||||||
// rejecting is only done once a reason is supplied.
|
|
||||||
$('.rq-dropdown:not(.reject-reason-dropdown)').hide();
|
|
||||||
$('.reject-reason-dropdown', nthTheme(i)).toggle();
|
|
||||||
isRejecting = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
other_reject_reason: function(i) {
|
|
||||||
if (!isRejecting) { return; }
|
|
||||||
|
|
||||||
// Open text area to enter in a custom rejection reason.
|
|
||||||
$('.rq-dropdown:not(.reject-reason-detail-dropdown)').hide();
|
|
||||||
$('.reject-reason-detail-dropdown', nthTheme(i)).toggle();
|
|
||||||
var textArea = $('.reject-reason-detail-dropdown textarea', nthTheme(i)).focus();
|
|
||||||
|
|
||||||
// Submit link/URL of the duplicate.
|
|
||||||
var submit = function() {
|
|
||||||
if (textArea.val()) {
|
|
||||||
$('input.comment', nthTheme(i)).val(textArea.val());
|
|
||||||
textArea.blur();
|
|
||||||
themeActions.reject(i, 0);
|
|
||||||
} else {
|
|
||||||
$('.reject-reason-detail-dropdown .error-required',
|
|
||||||
nthTheme(i)).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
keymap[z.keys.ENTER] = submit;
|
|
||||||
$('.reject-reason-detail-dropdown button').click(_pd(submit));
|
|
||||||
},
|
|
||||||
|
|
||||||
reject: function(i, rejectId) {
|
|
||||||
if (!isRejecting) { return; }
|
|
||||||
|
|
||||||
// Given the rejection reason, does the actual rejection of
|
|
||||||
// the Theme.
|
|
||||||
$('input.action', nthTheme(i)).val(actionConstants.reject);
|
|
||||||
$('input.reject-reason', nthTheme(i)).val(rejectId);
|
|
||||||
setReviewed(i, actionConstants.reject);
|
|
||||||
isRejecting = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
duplicate: function(i) {
|
|
||||||
// Open up dropdown to enter ID/URL of duplicate.
|
|
||||||
$('.rq-dropdown:not(.duplicate-dropdown)').hide();
|
|
||||||
$('.duplicate-dropdown', nthTheme(i)).toggle();
|
|
||||||
var textArea = $('.duplicate-dropdown textarea', nthTheme(i)).focus();
|
|
||||||
|
|
||||||
// Submit link/URL of the duplicate.
|
|
||||||
var submit = function() {
|
|
||||||
if (textArea.val()) {
|
|
||||||
$('input.action', nthTheme(i)).val(actionConstants.duplicate);
|
|
||||||
$('input.comment', nthTheme(i)).val(textArea.val());
|
|
||||||
textArea.blur();
|
|
||||||
setReviewed(i, actionConstants.duplicate);
|
|
||||||
} else {
|
|
||||||
$('.duplicate-dropdown .error-required',
|
|
||||||
nthTheme(i)).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
keymap[z.keys.ENTER] = submit;
|
|
||||||
$('.duplicate-dropdown button').click(_pd(submit));
|
|
||||||
},
|
|
||||||
|
|
||||||
flag: function(i) {
|
|
||||||
// Open up dropdown to enter reason for flagging.
|
|
||||||
$('.rq-dropdown:not(.flag-dropdown)').hide();
|
|
||||||
$('.flag-dropdown', nthTheme(i)).toggle();
|
|
||||||
var textArea = $('.flag-dropdown textarea', nthTheme(i)).focus();
|
|
||||||
|
|
||||||
// Submit link/URL of the flag.
|
|
||||||
var submit = function() {
|
|
||||||
if (textArea.val()) {
|
|
||||||
$('input.action', nthTheme(i)).val(actionConstants.flag);
|
|
||||||
$('input.comment', nthTheme(i)).val(textArea.val());
|
|
||||||
textArea.blur();
|
|
||||||
setReviewed(i, actionConstants.flagged);
|
|
||||||
} else {
|
|
||||||
$('.flag-dropdown .error-required',
|
|
||||||
nthTheme(i)).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
keymap[z.keys.ENTER] = submit;
|
|
||||||
$('.flag-dropdown button').click(_pd(submit));
|
|
||||||
},
|
|
||||||
|
|
||||||
moreinfo: function(i) {
|
|
||||||
// Open up dropdown to enter ID/URL of moreinfo.
|
|
||||||
$('.rq-dropdown:not(.moreinfo-dropdown)').hide();
|
|
||||||
$('.moreinfo-dropdown', nthTheme(i)).toggle();
|
|
||||||
var textArea = $('.moreinfo-dropdown textarea', nthTheme(i)).focus();
|
|
||||||
|
|
||||||
// Submit link/URL of the moreinfo.
|
|
||||||
var submit = function() {
|
|
||||||
if (textArea.val()) {
|
|
||||||
$('input.action', nthTheme(i)).val(actionConstants.moreinfo);
|
|
||||||
$('input.comment', nthTheme(i)).val(textArea.val());
|
|
||||||
textArea.blur();
|
|
||||||
setReviewed(i, actionConstants.moreinfo);
|
|
||||||
} else {
|
|
||||||
$('.moreinfo-dropdown .error-required',
|
|
||||||
nthTheme(i)).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
keymap[z.keys.ENTER] = submit;
|
|
||||||
$('.moreinfo-dropdown button').click(_pd(submit));
|
|
||||||
},
|
|
||||||
|
|
||||||
clearReview: function(i) {
|
|
||||||
$('input.action, input.comment, input.reject-reason',
|
|
||||||
nthTheme(i)).prop('value', '');
|
|
||||||
$(nthTheme(i)).removeClass('reviewed');
|
|
||||||
$('.status', nthTheme(i)).removeClass('reviewed');
|
|
||||||
|
|
||||||
$('#reviewed-count').text($('div.theme.reviewed').length);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on('click', 'button.approve', _pd(function(e) {
|
|
||||||
themeActions.approve(getThemeParent(e.currentTarget));
|
|
||||||
}))
|
|
||||||
.on('click', 'button.reject', _pd(function(e) {
|
|
||||||
themeActions.reject_reason(getThemeParent(e.currentTarget));
|
|
||||||
}))
|
|
||||||
.on('click', 'button.duplicate', _pd(function(e) {
|
|
||||||
themeActions.duplicate(getThemeParent(e.currentTarget));
|
|
||||||
}))
|
|
||||||
.on('click', 'button.flag', _pd(function(e) {
|
|
||||||
themeActions.flag(getThemeParent(e.currentTarget));
|
|
||||||
}))
|
|
||||||
.on('click', 'button.moreinfo', _pd(function(e) {
|
|
||||||
themeActions.moreinfo(getThemeParent(e.currentTarget));
|
|
||||||
}))
|
|
||||||
.on('click', '.clear-review', _pd(function(e) {
|
|
||||||
themeActions.clearReview(getThemeParent(e.currentTarget));
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
$.fn.themeQueueOptions = function(queueSelector) {
|
function getThemeParent(elem) {
|
||||||
return this.each(function() {
|
// Given an element (like an approve button),
|
||||||
var self = this;
|
// return the theme for which it is related to.
|
||||||
|
return $(elem).closest('.theme').data('id');
|
||||||
|
}
|
||||||
|
|
||||||
$('input', self).click(onChange);
|
function goToTheme(i, delay, duration) {
|
||||||
$('select', self).change(onChange);
|
delay = delay || 0;
|
||||||
onChange();
|
duration = duration || 250;
|
||||||
|
setTimeout(function () {
|
||||||
|
if (i >= 0 && i < themes.length) {
|
||||||
|
$(themes[i].element).scrollTo({
|
||||||
|
duration: duration,
|
||||||
|
marginTop: 20,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
|
$('.rq-dropdown').hide();
|
||||||
|
}
|
||||||
|
|
||||||
function onChange(e) {
|
function switchTheme(i) {
|
||||||
var category = $('#rq-category', self).val();
|
if (!themes[currentTheme]) {
|
||||||
var advance = $('#rq-advance:checked', self).val();
|
return;
|
||||||
var shortcuts = $('#rq-shortcuts:checked', self).val();
|
}
|
||||||
|
|
||||||
$(queueSelector)
|
$(themes[currentTheme].element).removeClass('active');
|
||||||
.toggleClass('advance', !!advance)
|
$(themes[i].element).addClass('active');
|
||||||
.toggleClass('shortcuts', !!shortcuts);
|
vertAlignSidebar(win, $('.theme.active'));
|
||||||
|
currentTheme = i;
|
||||||
|
$('.rq-dropdown').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findCurrentTheme() {
|
||||||
|
// Uses location of the window within the page to determine
|
||||||
|
// which theme we're currently looking at.
|
||||||
|
|
||||||
|
// $(window).scroll() fires too early.
|
||||||
|
if (!themes[currentTheme]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageTop = win.scrollTop();
|
||||||
|
if (pageTop <= themes[currentTheme].top) {
|
||||||
|
for (var i = currentTheme - 1; i >= 0; i--) {
|
||||||
|
if (themes[i].top < pageTop) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
};
|
return i + 1;
|
||||||
|
} else {
|
||||||
|
for (var i = currentTheme; i < themes.length; i++) {
|
||||||
|
// Scroll down the themes until we find a theme
|
||||||
|
// that is at the top of our page. That is our current
|
||||||
|
// theme.
|
||||||
|
if (pageTop <= themes[i].top) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var keymap = {
|
||||||
|
j: ['next', null],
|
||||||
|
k: ['prev', null],
|
||||||
|
a: ['approve', null],
|
||||||
|
r: ['reject_reason', null],
|
||||||
|
d: ['duplicate', null],
|
||||||
|
f: ['flag', null],
|
||||||
|
m: ['moreinfo', null],
|
||||||
|
};
|
||||||
|
// keymap[0] = ['other_reject_reason', 0];
|
||||||
|
for (var j = 1; j <= 9; j++) {
|
||||||
|
keymap[j] = ['reject', j];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setReviewed(i, action) {
|
||||||
|
var ac = actionConstants;
|
||||||
|
var actionMap = {};
|
||||||
|
actionMap[ac.moreinfo] = [gettext('Requested Info'), 'blue'];
|
||||||
|
actionMap[ac.flagged] = [gettext('Flagged'), 'red'];
|
||||||
|
actionMap[ac.duplicate] = [gettext('Duplicate'), 'red'];
|
||||||
|
actionMap[ac.reject] = [gettext('Rejected'), 'red'];
|
||||||
|
actionMap[ac.approve] = [gettext('Approved'), 'green'];
|
||||||
|
var text = actionMap[action][0];
|
||||||
|
var color = actionMap[action][1];
|
||||||
|
|
||||||
|
$(nthTheme(i)).addClass('reviewed');
|
||||||
|
$('.status', nthTheme(i))
|
||||||
|
.removeClass('red blue green')
|
||||||
|
.addClass('reviewed ' + color)
|
||||||
|
.find('.status-text')
|
||||||
|
.text(text);
|
||||||
|
|
||||||
|
$('#reviewed-count').text($('div.theme.reviewed').length);
|
||||||
|
if ($(queue).hasClass('advance')) {
|
||||||
|
goToTheme(i + 1, 250);
|
||||||
|
} else {
|
||||||
|
delete keymap[z.keys.ENTER];
|
||||||
|
$('.rq-dropdown').hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRejecting = false;
|
||||||
|
$(document).on(
|
||||||
|
'click',
|
||||||
|
'li.reject_reason',
|
||||||
|
_pd(function (e) {
|
||||||
|
if (isRejecting) {
|
||||||
|
var i = getThemeParent(e.currentTarget);
|
||||||
|
var rejectId = $(this).data('id');
|
||||||
|
if (rejectId === 0) {
|
||||||
|
themeActions.other_reject_reason(i);
|
||||||
|
} else {
|
||||||
|
themeActions.reject(i, rejectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
var themeActions = {
|
||||||
|
next: function (i) {
|
||||||
|
goToTheme(i + 1);
|
||||||
|
},
|
||||||
|
prev: function (i) {
|
||||||
|
goToTheme(i - 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
approve: function (i) {
|
||||||
|
$('input.action', nthTheme(i)).val(actionConstants.approve);
|
||||||
|
setReviewed(i, actionConstants.approve);
|
||||||
|
},
|
||||||
|
|
||||||
|
reject_reason: function (i) {
|
||||||
|
// Open up dropdown of rejection reasons and set up
|
||||||
|
// key and click-bindings for choosing a reason. This
|
||||||
|
// function does not actually do the rejecting as the
|
||||||
|
// rejecting is only done once a reason is supplied.
|
||||||
|
$('.rq-dropdown:not(.reject-reason-dropdown)').hide();
|
||||||
|
$('.reject-reason-dropdown', nthTheme(i)).toggle();
|
||||||
|
isRejecting = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
other_reject_reason: function (i) {
|
||||||
|
if (!isRejecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open text area to enter in a custom rejection reason.
|
||||||
|
$('.rq-dropdown:not(.reject-reason-detail-dropdown)').hide();
|
||||||
|
$('.reject-reason-detail-dropdown', nthTheme(i)).toggle();
|
||||||
|
var textArea = $(
|
||||||
|
'.reject-reason-detail-dropdown textarea',
|
||||||
|
nthTheme(i),
|
||||||
|
).focus();
|
||||||
|
|
||||||
|
// Submit link/URL of the duplicate.
|
||||||
|
var submit = function () {
|
||||||
|
if (textArea.val()) {
|
||||||
|
$('input.comment', nthTheme(i)).val(textArea.val());
|
||||||
|
textArea.blur();
|
||||||
|
themeActions.reject(i, 0);
|
||||||
|
} else {
|
||||||
|
$(
|
||||||
|
'.reject-reason-detail-dropdown .error-required',
|
||||||
|
nthTheme(i),
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
keymap[z.keys.ENTER] = submit;
|
||||||
|
$('.reject-reason-detail-dropdown button').click(_pd(submit));
|
||||||
|
},
|
||||||
|
|
||||||
|
reject: function (i, rejectId) {
|
||||||
|
if (!isRejecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given the rejection reason, does the actual rejection of
|
||||||
|
// the Theme.
|
||||||
|
$('input.action', nthTheme(i)).val(actionConstants.reject);
|
||||||
|
$('input.reject-reason', nthTheme(i)).val(rejectId);
|
||||||
|
setReviewed(i, actionConstants.reject);
|
||||||
|
isRejecting = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
duplicate: function (i) {
|
||||||
|
// Open up dropdown to enter ID/URL of duplicate.
|
||||||
|
$('.rq-dropdown:not(.duplicate-dropdown)').hide();
|
||||||
|
$('.duplicate-dropdown', nthTheme(i)).toggle();
|
||||||
|
var textArea = $('.duplicate-dropdown textarea', nthTheme(i)).focus();
|
||||||
|
|
||||||
|
// Submit link/URL of the duplicate.
|
||||||
|
var submit = function () {
|
||||||
|
if (textArea.val()) {
|
||||||
|
$('input.action', nthTheme(i)).val(actionConstants.duplicate);
|
||||||
|
$('input.comment', nthTheme(i)).val(textArea.val());
|
||||||
|
textArea.blur();
|
||||||
|
setReviewed(i, actionConstants.duplicate);
|
||||||
|
} else {
|
||||||
|
$('.duplicate-dropdown .error-required', nthTheme(i)).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
keymap[z.keys.ENTER] = submit;
|
||||||
|
$('.duplicate-dropdown button').click(_pd(submit));
|
||||||
|
},
|
||||||
|
|
||||||
|
flag: function (i) {
|
||||||
|
// Open up dropdown to enter reason for flagging.
|
||||||
|
$('.rq-dropdown:not(.flag-dropdown)').hide();
|
||||||
|
$('.flag-dropdown', nthTheme(i)).toggle();
|
||||||
|
var textArea = $('.flag-dropdown textarea', nthTheme(i)).focus();
|
||||||
|
|
||||||
|
// Submit link/URL of the flag.
|
||||||
|
var submit = function () {
|
||||||
|
if (textArea.val()) {
|
||||||
|
$('input.action', nthTheme(i)).val(actionConstants.flag);
|
||||||
|
$('input.comment', nthTheme(i)).val(textArea.val());
|
||||||
|
textArea.blur();
|
||||||
|
setReviewed(i, actionConstants.flagged);
|
||||||
|
} else {
|
||||||
|
$('.flag-dropdown .error-required', nthTheme(i)).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
keymap[z.keys.ENTER] = submit;
|
||||||
|
$('.flag-dropdown button').click(_pd(submit));
|
||||||
|
},
|
||||||
|
|
||||||
|
moreinfo: function (i) {
|
||||||
|
// Open up dropdown to enter ID/URL of moreinfo.
|
||||||
|
$('.rq-dropdown:not(.moreinfo-dropdown)').hide();
|
||||||
|
$('.moreinfo-dropdown', nthTheme(i)).toggle();
|
||||||
|
var textArea = $('.moreinfo-dropdown textarea', nthTheme(i)).focus();
|
||||||
|
|
||||||
|
// Submit link/URL of the moreinfo.
|
||||||
|
var submit = function () {
|
||||||
|
if (textArea.val()) {
|
||||||
|
$('input.action', nthTheme(i)).val(actionConstants.moreinfo);
|
||||||
|
$('input.comment', nthTheme(i)).val(textArea.val());
|
||||||
|
textArea.blur();
|
||||||
|
setReviewed(i, actionConstants.moreinfo);
|
||||||
|
} else {
|
||||||
|
$('.moreinfo-dropdown .error-required', nthTheme(i)).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
keymap[z.keys.ENTER] = submit;
|
||||||
|
$('.moreinfo-dropdown button').click(_pd(submit));
|
||||||
|
},
|
||||||
|
|
||||||
|
clearReview: function (i) {
|
||||||
|
$(
|
||||||
|
'input.action, input.comment, input.reject-reason',
|
||||||
|
nthTheme(i),
|
||||||
|
).prop('value', '');
|
||||||
|
$(nthTheme(i)).removeClass('reviewed');
|
||||||
|
$('.status', nthTheme(i)).removeClass('reviewed');
|
||||||
|
|
||||||
|
$('#reviewed-count').text($('div.theme.reviewed').length);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'button.approve',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.approve(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'button.reject',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.reject_reason(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'button.duplicate',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.duplicate(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'button.flag',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.flag(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'button.moreinfo',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.moreinfo(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'click',
|
||||||
|
'.clear-review',
|
||||||
|
_pd(function (e) {
|
||||||
|
themeActions.clearReview(getThemeParent(e.currentTarget));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.themeQueueOptions = function (queueSelector) {
|
||||||
|
return this.each(function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
$('input', self).click(onChange);
|
||||||
|
$('select', self).change(onChange);
|
||||||
|
onChange();
|
||||||
|
|
||||||
|
function onChange(e) {
|
||||||
|
var category = $('#rq-category', self).val();
|
||||||
|
var advance = $('#rq-advance:checked', self).val();
|
||||||
|
var shortcuts = $('#rq-shortcuts:checked', self).val();
|
||||||
|
|
||||||
|
$(queueSelector)
|
||||||
|
.toggleClass('advance', !!advance)
|
||||||
|
.toggleClass('shortcuts', !!shortcuts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
||||||
|
|
||||||
function vertAlignSidebar(win) {
|
function vertAlignSidebar(win) {
|
||||||
var activeThemeTop = ($('.theme.active').offset().top -
|
var activeThemeTop = $('.theme.active').offset().top - win.scrollTop();
|
||||||
win.scrollTop());
|
$('.sidebar .align.fixed').css('top', activeThemeTop + 'px');
|
||||||
$('.sidebar .align.fixed').css('top', activeThemeTop + 'px');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
var $theme_queue = $('.theme-queue');
|
||||||
|
if ($theme_queue.length) {
|
||||||
|
$('.zoombox').zoomBox();
|
||||||
|
$('.zoombox img').previewPersona();
|
||||||
|
$theme_queue.themeQueue();
|
||||||
|
$('.sidebar').themeQueueOptions('.theme-queue');
|
||||||
|
$('#commit').click(
|
||||||
|
_pd(function (e) {
|
||||||
|
$('#theme-queue-form').submit();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
$(document).ready(function() {
|
// Align sidebar with active theme.
|
||||||
var $theme_queue = $('.theme-queue');
|
if ($('.theme.active').length) {
|
||||||
if ($theme_queue.length) {
|
var win = $(window);
|
||||||
$('.zoombox').zoomBox();
|
win.scroll(
|
||||||
$('.zoombox img').previewPersona();
|
_.throttle(function () {
|
||||||
$theme_queue.themeQueue();
|
vertAlignSidebar(win);
|
||||||
$('.sidebar').themeQueueOptions('.theme-queue');
|
}, 100),
|
||||||
$('#commit').click(_pd(function(e) {
|
);
|
||||||
$('#theme-queue-form').submit();
|
vertAlignSidebar(win);
|
||||||
}));
|
|
||||||
|
|
||||||
// Align sidebar with active theme.
|
|
||||||
if ($('.theme.active').length) {
|
|
||||||
var win = $(window);
|
|
||||||
win.scroll(_.throttle(function() {
|
|
||||||
vertAlignSidebar(win);
|
|
||||||
}, 100));
|
|
||||||
vertAlignSidebar(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If daily message is present, align fixed sidebar.
|
|
||||||
if (['none', undefined].indexOf($('.daily-message').css('display')) < 0) {
|
|
||||||
var $sidebar = $('.sidebar .align.fixed');
|
|
||||||
var top = parseInt($sidebar.css('top'), 10) + 82;
|
|
||||||
$sidebar.css('top', top + 'px');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($('.theme-search').length) {
|
// If daily message is present, align fixed sidebar.
|
||||||
initSearch();
|
if (['none', undefined].indexOf($('.daily-message').css('display')) < 0) {
|
||||||
|
var $sidebar = $('.sidebar .align.fixed');
|
||||||
|
var top = parseInt($sidebar.css('top'), 10) + 82;
|
||||||
|
$sidebar.css('top', top + 'px');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($('.theme-search').length) {
|
||||||
|
initSearch();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function initSearch() {
|
function initSearch() {
|
||||||
var no_results = '<p class="no-results">' + gettext('No results found') + '</p>';
|
var no_results =
|
||||||
|
'<p class="no-results">' + gettext('No results found') + '</p>';
|
||||||
|
|
||||||
var $clear = $('.clear-queue-search'),
|
var $clear = $('.clear-queue-search'),
|
||||||
$appQueue = $('.search-toggle'),
|
$appQueue = $('.search-toggle'),
|
||||||
$search = $('.queue-search'),
|
$search = $('.queue-search'),
|
||||||
$searchIsland = $('#search-island');
|
$searchIsland = $('#search-island');
|
||||||
|
|
||||||
if ($search.length) {
|
if ($search.length) {
|
||||||
var apiUrl = $search.data('api-url');
|
var apiUrl = $search.data('api-url');
|
||||||
var review_url = $search.data('review-url');
|
var review_url = $search.data('review-url');
|
||||||
var statuses = $searchIsland.data('statuses');
|
var statuses = $searchIsland.data('statuses');
|
||||||
|
|
||||||
$('form', $search).submit(_pd(function() {
|
$('form', $search).submit(
|
||||||
var $form = $(this);
|
_pd(function () {
|
||||||
$.get(apiUrl, $form.serialize()).done(function(data) {
|
var $form = $(this);
|
||||||
// Hide app queue.
|
$.get(apiUrl, $form.serialize()).done(function (data) {
|
||||||
$appQueue.hide();
|
// Hide app queue.
|
||||||
$clear.show();
|
$appQueue.hide();
|
||||||
// Show results.
|
$clear.show();
|
||||||
if (data.meta.total_count === 0) {
|
// Show results.
|
||||||
$searchIsland.html(no_results).show().removeClass('hidden');
|
if (data.meta.total_count === 0) {
|
||||||
} else {
|
$searchIsland.html(no_results).show().removeClass('hidden');
|
||||||
var results = [];
|
} else {
|
||||||
$.each(data.objects, function(i, item) {
|
var results = [];
|
||||||
item = buildThemeResultRow(item, review_url,
|
$.each(data.objects, function (i, item) {
|
||||||
statuses);
|
item = buildThemeResultRow(item, review_url, statuses);
|
||||||
results.push(search_result_row_template(item));
|
results.push(search_result_row_template(item));
|
||||||
});
|
|
||||||
$searchIsland.html(search_results_template({rows: results.join('')}));
|
|
||||||
$searchIsland.removeClass('hidden').show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}));
|
$searchIsland.html(
|
||||||
}
|
search_results_template({ rows: results.join('') }),
|
||||||
|
);
|
||||||
|
$searchIsland.removeClass('hidden').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildThemeResultRow(theme, review_url, statuses) {
|
function buildThemeResultRow(theme, review_url, statuses) {
|
||||||
// Add some extra pretty attrs for the template.
|
// Add some extra pretty attrs for the template.
|
||||||
theme.name = theme.name[0];
|
theme.name = theme.name[0];
|
||||||
|
|
||||||
// Rather resolve URLs in backend, infer from slug.
|
// Rather resolve URLs in backend, infer from slug.
|
||||||
theme.review_url = review_url.replace(
|
theme.review_url = review_url.replace('__slug__', theme.slug);
|
||||||
'__slug__', theme.slug);
|
theme.status = statuses[theme.status];
|
||||||
theme.status = statuses[theme.status];
|
return theme;
|
||||||
return theme;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,22 +24,27 @@ _.template(`
|
||||||
|
|
||||||
/* The following is the above commented template, pre-compiled. */
|
/* The following is the above commented template, pre-compiled. */
|
||||||
|
|
||||||
function search_results_template(obj){
|
function search_results_template(obj) {
|
||||||
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
|
var __t,
|
||||||
with(obj||{}){
|
__p = '',
|
||||||
__p+='\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>'+
|
__j = Array.prototype.join,
|
||||||
((__t=( gettext('Theme') ))==null?'':_.escape(__t))+
|
print = function () {
|
||||||
'</th>\n <th>'+
|
__p += __j.call(arguments, '');
|
||||||
((__t=( gettext('Reviewer') ))==null?'':_.escape(__t))+
|
};
|
||||||
'</th>\n <th>'+
|
with (obj || {}) {
|
||||||
((__t=( gettext('Status') ))==null?'':_.escape(__t))+
|
__p +=
|
||||||
'</th>\n </tr>\n </thead>\n <tbody>'+
|
'\n <table id="search-queue" class="data-grid items">\n <thead>\n <tr class="listing-header">\n <th>' +
|
||||||
((__t=( rows ))==null?'':__t)+
|
((__t = gettext('Theme')) == null ? '' : _.escape(__t)) +
|
||||||
'</tbody>\n </table>\n';
|
'</th>\n <th>' +
|
||||||
|
((__t = gettext('Reviewer')) == null ? '' : _.escape(__t)) +
|
||||||
|
'</th>\n <th>' +
|
||||||
|
((__t = gettext('Status')) == null ? '' : _.escape(__t)) +
|
||||||
|
'</th>\n </tr>\n </thead>\n <tbody>' +
|
||||||
|
((__t = rows) == null ? '' : __t) +
|
||||||
|
'</tbody>\n </table>\n';
|
||||||
|
}
|
||||||
|
return __p;
|
||||||
}
|
}
|
||||||
return __p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
_.template(`
|
_.template(`
|
||||||
|
@ -59,24 +64,32 @@ _.template(`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The following is the above commented template, pre-compiled. */
|
/* The following is the above commented template, pre-compiled. */
|
||||||
function search_result_row_template(obj){
|
function search_result_row_template(obj) {
|
||||||
var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};
|
var __t,
|
||||||
with(obj||{}){
|
__p = '',
|
||||||
__p+='\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">'+
|
__j = Array.prototype.join,
|
||||||
((__t=( id ))==null?'':__t)+
|
print = function () {
|
||||||
'</span>\n <a href="'+
|
__p += __j.call(arguments, '');
|
||||||
((__t=( review_url ))==null?'':__t)+
|
};
|
||||||
'">'+
|
with (obj || {}) {
|
||||||
((__t=( name ))==null?'':_.escape(__t))+
|
__p +=
|
||||||
'</a>\n </td>\n <td>\n ';
|
'\n <tr class="addon-row">\n <td class="app-name">\n <span class="addon-id">' +
|
||||||
if (typeof reviewer !== "undefined") {
|
((__t = id) == null ? '' : __t) +
|
||||||
__p+='\n '+
|
'</span>\n <a href="' +
|
||||||
((__t=( reviewer ))==null?'':_.escape(__t))+
|
((__t = review_url) == null ? '' : __t) +
|
||||||
'\n ';
|
'">' +
|
||||||
}
|
((__t = name) == null ? '' : _.escape(__t)) +
|
||||||
__p+='\n </td>\n <td>'+
|
'</a>\n </td>\n <td>\n ';
|
||||||
((__t=( status ))==null?'':__t)+
|
if (typeof reviewer !== 'undefined') {
|
||||||
'</td>\n </tr>';
|
__p +=
|
||||||
}
|
'\n ' +
|
||||||
return __p;
|
((__t = reviewer) == null ? '' : _.escape(__t)) +
|
||||||
|
'\n ';
|
||||||
|
}
|
||||||
|
__p +=
|
||||||
|
'\n </td>\n <td>' +
|
||||||
|
((__t = status) == null ? '' : __t) +
|
||||||
|
'</td>\n </tr>';
|
||||||
|
}
|
||||||
|
return __p;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,66 @@
|
||||||
$.fn.truncate = function(opts) {
|
$.fn.truncate = function (opts) {
|
||||||
this.each(function() {
|
this.each(function () {
|
||||||
truncate(this, opts);
|
truncate(this, opts);
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
$.fn.untruncate = function() {
|
$.fn.untruncate = function () {
|
||||||
this.each(function() {
|
this.each(function () {
|
||||||
var $el = $(this),
|
var $el = $(this),
|
||||||
oTxt = $el.attr("oldtext");
|
oTxt = $el.attr('oldtext');
|
||||||
if (oTxt) {
|
if (oTxt) {
|
||||||
$el.text(oTxt);
|
$el.text(oTxt);
|
||||||
}
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
$.fn.lineclamp = function(lines) {
|
|
||||||
// This function limits the number of visible `lines` of text. Overflown
|
|
||||||
// text is gracefully ellipsed: http://en.wiktionary.org/wiki/ellipse#Verb.
|
|
||||||
if (!lines) {
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
return this.each(function() {
|
});
|
||||||
var $this = $(this),
|
return this;
|
||||||
lh = $this.css('line-height');
|
|
||||||
if (typeof lh == 'string' && lh.substr(-2) == 'px') {
|
|
||||||
lh = parseFloat(lh.replace('px', ''));
|
|
||||||
var maxHeight = Math.ceil(lh) * lines,
|
|
||||||
truncated;
|
|
||||||
if ((this.scrollHeight - maxHeight) > 2) {
|
|
||||||
$this.css({'height': maxHeight + 2, 'overflow': 'hidden',
|
|
||||||
'text-overflow': 'ellipsis'});
|
|
||||||
// Add an ellipsis.
|
|
||||||
$this.truncate({dir: 'v'});
|
|
||||||
} else {
|
|
||||||
$this.css({'max-height': maxHeight, 'overflow': 'hidden',
|
|
||||||
'text-overflow': 'ellipsis'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
$.fn.linefit = function(lines) {
|
$.fn.lineclamp = function (lines) {
|
||||||
// This function shrinks text to fit on one line.
|
// This function limits the number of visible `lines` of text. Overflown
|
||||||
var min_font_size = 7;
|
// text is gracefully ellipsed: http://en.wiktionary.org/wiki/ellipse#Verb.
|
||||||
lines = lines || 1;
|
if (!lines) {
|
||||||
return this.each(function() {
|
return this;
|
||||||
var $this = $(this),
|
}
|
||||||
fs = parseFloat($this.css('font-size').replace('px', '')),
|
return this.each(function () {
|
||||||
max_height = Math.ceil(parseFloat($this.css('line-height').replace('px', ''))) * lines,
|
var $this = $(this),
|
||||||
height = $this.height();
|
lh = $this.css('line-height');
|
||||||
while (height > max_height && fs > min_font_size) {
|
if (typeof lh == 'string' && lh.substr(-2) == 'px') {
|
||||||
// Repeatedly shrink the text by 0.5px until all the text fits.
|
lh = parseFloat(lh.replace('px', ''));
|
||||||
fs -= .5;
|
var maxHeight = Math.ceil(lh) * lines,
|
||||||
$this.css('font-size', fs);
|
truncated;
|
||||||
height = $this.height();
|
if (this.scrollHeight - maxHeight > 2) {
|
||||||
}
|
$this.css({
|
||||||
});
|
height: maxHeight + 2,
|
||||||
|
overflow: 'hidden',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
});
|
||||||
|
// Add an ellipsis.
|
||||||
|
$this.truncate({ dir: 'v' });
|
||||||
|
} else {
|
||||||
|
$this.css({
|
||||||
|
'max-height': maxHeight,
|
||||||
|
overflow: 'hidden',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
$.fn.linefit = function (lines) {
|
||||||
|
// This function shrinks text to fit on one line.
|
||||||
|
var min_font_size = 7;
|
||||||
|
lines = lines || 1;
|
||||||
|
return this.each(function () {
|
||||||
|
var $this = $(this),
|
||||||
|
fs = parseFloat($this.css('font-size').replace('px', '')),
|
||||||
|
max_height =
|
||||||
|
Math.ceil(parseFloat($this.css('line-height').replace('px', ''))) *
|
||||||
|
lines,
|
||||||
|
height = $this.height();
|
||||||
|
while (height > max_height && fs > min_font_size) {
|
||||||
|
// Repeatedly shrink the text by 0.5px until all the text fits.
|
||||||
|
fs -= 0.5;
|
||||||
|
$this.css('font-size', fs);
|
||||||
|
height = $this.height();
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Javascript's unicode support sucks a lot.
|
// Javascript's unicode support sucks a lot.
|
||||||
// This allows us to match Unicode letters.
|
// This allows us to match Unicode letters.
|
||||||
z.unicode_letters = "\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0525\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971\u0972\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3D\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8B\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCB\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA65F\uA662-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B\uA78C\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA2D\uFA30-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
|
z.unicode_letters =
|
||||||
$(document).trigger('unicode_loaded');
|
'\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0525\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971\u0972\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3D\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8B\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCB\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA65F\uA662-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B\uA78C\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA2D\uFA30-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC';
|
||||||
|
$(document).trigger('unicode_loaded');
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
// Hijack "Admin / Editor Log in" context menuitem.
|
// Hijack "Admin / Editor Log in" context menuitem.
|
||||||
$('#admin-login').click(function() {
|
$('#admin-login').click(function () {
|
||||||
window.location = $(this).attr('data-url');
|
window.location = $(this).attr('data-url');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Recaptcha
|
// Recaptcha
|
||||||
var RecaptchaOptions = { theme : 'custom' };
|
var RecaptchaOptions = { theme: 'custom' };
|
||||||
|
|
||||||
$('#recaptcha_different').click(function(e) {
|
$('#recaptcha_different').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Recaptcha.reload();
|
Recaptcha.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#recaptcha_audio').click(function(e) {
|
$('#recaptcha_audio').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var toggleType = this.getAttribute('data-nextType') || 'audio';
|
var toggleType = this.getAttribute('data-nextType') || 'audio';
|
||||||
Recaptcha.switch_type(toggleType);
|
Recaptcha.switch_type(toggleType);
|
||||||
this.setAttribute('data-nextType', toggleType === 'audio' ? 'image' : 'audio');
|
this.setAttribute(
|
||||||
|
'data-nextType',
|
||||||
|
toggleType === 'audio' ? 'image' : 'audio',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#recaptcha_help').click(function(e) {
|
$('#recaptcha_help').click(function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
Recaptcha.showhelp();
|
Recaptcha.showhelp();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,477 +1,528 @@
|
||||||
$(document).ready(function() {
|
$(document).ready(function () {
|
||||||
|
if ($('.addon-validator-suite').length) {
|
||||||
if ($('.addon-validator-suite').length) {
|
initValidator();
|
||||||
initValidator();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function initValidator($doc) {
|
function initValidator($doc) {
|
||||||
$doc = $doc || $(document);
|
$doc = $doc || $(document);
|
||||||
|
|
||||||
function inherit(OtherClass, constructor) {
|
function inherit(OtherClass, constructor) {
|
||||||
var NewClass = function() {
|
var NewClass = function () {
|
||||||
OtherClass.apply(this, arguments);
|
OtherClass.apply(this, arguments);
|
||||||
if (typeof constructor !== 'undefined') {
|
if (typeof constructor !== 'undefined') {
|
||||||
constructor.apply(this, arguments);
|
constructor.apply(this, arguments);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
$.extend(NewClass.prototype, OtherClass.prototype);
|
$.extend(NewClass.prototype, OtherClass.prototype);
|
||||||
return NewClass;
|
return NewClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyFn() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResultsTier($suite, tierId, options) {
|
||||||
|
if (typeof options === 'undefined') {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
if (typeof options.app === 'undefined') {
|
||||||
|
options.app = null;
|
||||||
|
}
|
||||||
|
if (typeof options.testsWereRun === 'undefined') {
|
||||||
|
options.testsWereRun = true;
|
||||||
|
}
|
||||||
|
this.$results = $('.results', $suite);
|
||||||
|
this.app = options.app;
|
||||||
|
this.testsWereRun = options.testsWereRun;
|
||||||
|
this.counts = { error: 0, warning: 0, notice: 0 };
|
||||||
|
this.tierId = tierId;
|
||||||
|
this.$suite = $suite;
|
||||||
|
this.$dom = $('#suite-results-tier-' + tierId, $suite);
|
||||||
|
if (!this.$dom.length) {
|
||||||
|
this.$dom = this.createDom();
|
||||||
|
this.$results.append(this.$dom);
|
||||||
|
}
|
||||||
|
this.$tierResults = $('.tier-results', this.$dom);
|
||||||
|
this.wakeUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultsTier.prototype.clear = function () {
|
||||||
|
this.$tierResults.empty();
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultsTier.prototype.tallyMsgType = function (type_) {
|
||||||
|
this.counts[type_] += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultsTier.prototype.createDom = function () {
|
||||||
|
var $tier = $($('.template', this.$suite).html().trim());
|
||||||
|
$tier.attr('id', 'suite-results-tier-' + this.tierId);
|
||||||
|
return $tier;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultsTier.prototype.summarize = function () {
|
||||||
|
var sm = resultSummary(
|
||||||
|
this.counts.error,
|
||||||
|
this.counts.warning,
|
||||||
|
this.counts.notice,
|
||||||
|
this.testsWereRun,
|
||||||
|
),
|
||||||
|
resultClass,
|
||||||
|
summaryMsg;
|
||||||
|
$('.result-summary', this.$dom)
|
||||||
|
.css('visibility', 'visible')
|
||||||
|
.empty()
|
||||||
|
.text(sm);
|
||||||
|
if (this.counts.error) {
|
||||||
|
resultClass = 'tests-failed';
|
||||||
|
} else if (this.counts.warning) {
|
||||||
|
resultClass = 'tests-passed-warnings';
|
||||||
|
} else if (this.counts.notice) {
|
||||||
|
resultClass = 'tests-passed-notices';
|
||||||
|
} else {
|
||||||
|
if (this.testsWereRun) {
|
||||||
|
summaryMsg = gettext('All tests passed successfully.');
|
||||||
|
resultClass = 'tests-passed';
|
||||||
|
} else {
|
||||||
|
summaryMsg = gettext('These tests were not run.');
|
||||||
|
resultClass = 'tests-notrun';
|
||||||
|
// No summary since no tests were run:
|
||||||
|
$('.result-summary', this.$dom).html(' ');
|
||||||
|
}
|
||||||
|
this.$tierResults.append('<span>' + summaryMsg + '</span>');
|
||||||
|
}
|
||||||
|
this.$tierResults
|
||||||
|
.removeClass(
|
||||||
|
'ajax-loading tests-failed ' +
|
||||||
|
'tests-passed tests-passed-warnings ' +
|
||||||
|
'tests-passed-notices tests-notrun',
|
||||||
|
)
|
||||||
|
.addClass(resultClass);
|
||||||
|
if ($('.test-tier', this.$suite).length) {
|
||||||
|
this.topSummary();
|
||||||
|
}
|
||||||
|
return this.counts;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultsTier.prototype.topSummary = function () {
|
||||||
|
var $top = $(
|
||||||
|
'[class~="test-tier"]' + '[data-tier="' + this.tierId + '"]',
|
||||||
|
this.$suite,
|
||||||
|
),
|
||||||
|
summaryMsg = resultSummary(
|
||||||
|
this.counts.error,
|
||||||
|
this.counts.warning,
|
||||||
|
this.counts.notice,
|
||||||
|
this.testsWereRun,
|
||||||
|
);
|
||||||
|
|
||||||
|
$('.tier-summary', $top).text(summaryMsg);
|
||||||
|
$top.removeClass(
|
||||||
|
'ajax-loading',
|
||||||
|
'tests-failed',
|
||||||
|
'tests-passed',
|
||||||
|
'tests-notrun',
|
||||||
|
);
|
||||||
|
if (this.counts.error > 0) {
|
||||||
|
$top.addClass('tests-failed');
|
||||||
|
} else if (this.counts.warning > 0) {
|
||||||
|
$top.addClass('tests-warnings');
|
||||||
|
} else if (this.testsWereRun) {
|
||||||
|
$top.addClass('tests-passed');
|
||||||
|
} else {
|
||||||
|
$top.addClass('tests-notrun');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultsTier.prototype.wakeUp = function () {
|
||||||
|
var $title = $('h4', this.$dom),
|
||||||
|
changeLink;
|
||||||
|
$('.tier-results', this.$dom).empty();
|
||||||
|
this.$dom.removeClass('hidden');
|
||||||
|
this.$dom.show();
|
||||||
|
if (this.app) {
|
||||||
|
// Override the title with a special app/version title
|
||||||
|
$title.text(
|
||||||
|
format(
|
||||||
|
'{0} {1} {2}',
|
||||||
|
this.app.trans[this.app.guid],
|
||||||
|
this.app.version,
|
||||||
|
gettext('Tests'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
changeLink = this.app.versionChangeLinks[
|
||||||
|
this.app.guid + ' ' + this.app.version
|
||||||
|
];
|
||||||
|
if (changeLink) {
|
||||||
|
this.$dom.prepend(
|
||||||
|
format(
|
||||||
|
'<a class="version-change-link" href="{0}">{1}</a>',
|
||||||
|
changeLink,
|
||||||
|
// L10n: Example: Changes in Firefox 5
|
||||||
|
gettext(
|
||||||
|
format(
|
||||||
|
'Changes in {0} {1}',
|
||||||
|
this.app.trans[this.app.guid],
|
||||||
|
/\d+/.exec(this.app.version),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (!$title.text()) {
|
||||||
|
$title.text(gettext('Tests'));
|
||||||
|
}
|
||||||
|
$('.tier-results', this.$dom).removeClass('ajax-loading');
|
||||||
|
};
|
||||||
|
|
||||||
|
function MsgVisitor(suite, data) {
|
||||||
|
this.$suite = suite;
|
||||||
|
this.data = data;
|
||||||
|
this.$results = $('.results', suite);
|
||||||
|
this.msgSet = {};
|
||||||
|
this.tiers = {};
|
||||||
|
this.appTrans = null;
|
||||||
|
this.versionChangeLinks = null;
|
||||||
|
this.allCounts = { error: 0, warning: 0 };
|
||||||
|
this.fileURL = suite.data('fileUrl');
|
||||||
|
this.fileID = suite.data('fileId');
|
||||||
|
}
|
||||||
|
|
||||||
|
MsgVisitor.prototype.createTier = function (tierId, options) {
|
||||||
|
var tier = new ResultsTier(this.$suite, tierId, this.tierOptions(options));
|
||||||
|
return tier;
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.finish = function () {
|
||||||
|
var self = this;
|
||||||
|
$('.result', this.$suite).each(function (i, res) {
|
||||||
|
if (!$('.msg', res).length) {
|
||||||
|
// No messages so no tier was created.
|
||||||
|
self.getTier($('.tier-results', res).attr('data-tier'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$.each(this.tiers, function (tierId, tier) {
|
||||||
|
var tierSum = tier.summarize();
|
||||||
|
self.allCounts.error += tierSum.error;
|
||||||
|
self.allCounts.warning += tierSum.warning;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.clear = function () {
|
||||||
|
$.each(this.tiers, function (tierId, tier) {
|
||||||
|
tier.clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.getMsgType = function (msg) {
|
||||||
|
return msg['type'];
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.getTier = function (tierId, options) {
|
||||||
|
if (typeof options === 'undefined') {
|
||||||
|
options = { app: null };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!options.app &&
|
||||||
|
this.data.validation.ending_tier &&
|
||||||
|
this.data.validation.ending_tier < tierId
|
||||||
|
) {
|
||||||
|
options.testsWereRun = false;
|
||||||
|
}
|
||||||
|
if (typeof this.tiers[tierId] === 'undefined') {
|
||||||
|
this.tiers[tierId] = this.createTier(tierId, options);
|
||||||
|
}
|
||||||
|
return this.tiers[tierId];
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.filterMessage = function (msg) {
|
||||||
|
return !(this.hideIgnored && msg.ignored);
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.message = function (msg, options) {
|
||||||
|
if (!this.filterMessage(msg)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function emptyFn() {
|
if (typeof this.msgSet[msg.uid] !== 'undefined') {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
this.msgSet[msg.uid] = true;
|
||||||
|
|
||||||
function ResultsTier($suite, tierId, options) {
|
var tier = this.getTier(msg.tier, options),
|
||||||
if (typeof options === 'undefined') {
|
msgDiv = $('<div class="msg"><h5></h5></div>'),
|
||||||
options = {}
|
effectiveType = this.getMsgType(msg),
|
||||||
}
|
prefix = effectiveType == 'error' ? gettext('Error') : gettext('Warning');
|
||||||
if (typeof options.app === 'undefined') {
|
|
||||||
options.app = null;
|
|
||||||
}
|
|
||||||
if (typeof options.testsWereRun === 'undefined') {
|
|
||||||
options.testsWereRun = true;
|
|
||||||
}
|
|
||||||
this.$results = $('.results', $suite);
|
|
||||||
this.app = options.app;
|
|
||||||
this.testsWereRun = options.testsWereRun;
|
|
||||||
this.counts = {error: 0, warning: 0, notice: 0};
|
|
||||||
this.tierId = tierId;
|
|
||||||
this.$suite = $suite;
|
|
||||||
this.$dom = $('#suite-results-tier-' + tierId, $suite);
|
|
||||||
if (!this.$dom.length) {
|
|
||||||
this.$dom = this.createDom();
|
|
||||||
this.$results.append(this.$dom);
|
|
||||||
}
|
|
||||||
this.$tierResults = $('.tier-results', this.$dom);
|
|
||||||
this.wakeUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultsTier.prototype.clear = function() {
|
tier.tallyMsgType(effectiveType);
|
||||||
this.$tierResults.empty();
|
msgDiv.attr('id', 'v-msg-' + msg.uid);
|
||||||
};
|
msgDiv.addClass('msg-' + effectiveType);
|
||||||
|
|
||||||
ResultsTier.prototype.tallyMsgType = function(type_) {
|
// The "message" and "description" properties are escaped and linkified
|
||||||
this.counts[type_] += 1;
|
// before we receive them.
|
||||||
};
|
$('h5', msgDiv).html(msg.message); // Sanitized HTML value.
|
||||||
|
|
||||||
ResultsTier.prototype.createDom = function() {
|
// The validator returns the "description" as either string, or
|
||||||
var $tier = $($('.template', this.$suite).html().trim());
|
// arrays of strings. We turn it into arrays when sanitizing.
|
||||||
$tier.attr('id', 'suite-results-tier-' + this.tierId);
|
$.each(msg.description, function (i, val) {
|
||||||
return $tier;
|
var $desc = $('<p>').html(val); // Sanitized HTML value.
|
||||||
}
|
if (i === 0) {
|
||||||
|
$desc.prepend(format('<strong>{0}:</strong> ', prefix));
|
||||||
ResultsTier.prototype.summarize = function() {
|
}
|
||||||
var sm = resultSummary(this.counts.error, this.counts.warning, this.counts.notice,
|
msgDiv.append($desc);
|
||||||
this.testsWereRun),
|
|
||||||
resultClass, summaryMsg;
|
|
||||||
$('.result-summary', this.$dom).css('visibility', 'visible')
|
|
||||||
.empty().text(sm);
|
|
||||||
if (this.counts.error) {
|
|
||||||
resultClass = 'tests-failed';
|
|
||||||
} else if (this.counts.warning) {
|
|
||||||
resultClass = 'tests-passed-warnings';
|
|
||||||
} else if (this.counts.notice) {
|
|
||||||
resultClass = 'tests-passed-notices';
|
|
||||||
} else {
|
|
||||||
if (this.testsWereRun) {
|
|
||||||
summaryMsg = gettext('All tests passed successfully.');
|
|
||||||
resultClass = 'tests-passed';
|
|
||||||
} else {
|
|
||||||
summaryMsg = gettext('These tests were not run.');
|
|
||||||
resultClass = 'tests-notrun';
|
|
||||||
// No summary since no tests were run:
|
|
||||||
$('.result-summary', this.$dom).html(' ');
|
|
||||||
}
|
|
||||||
this.$tierResults.append('<span>' + summaryMsg + '</span>');
|
|
||||||
}
|
|
||||||
this.$tierResults.removeClass('ajax-loading tests-failed ' +
|
|
||||||
'tests-passed tests-passed-warnings ' +
|
|
||||||
'tests-passed-notices tests-notrun')
|
|
||||||
.addClass(resultClass);
|
|
||||||
if ($('.test-tier', this.$suite).length) {
|
|
||||||
this.topSummary();
|
|
||||||
}
|
|
||||||
return this.counts;
|
|
||||||
};
|
|
||||||
|
|
||||||
ResultsTier.prototype.topSummary = function() {
|
|
||||||
var $top = $('[class~="test-tier"]' +
|
|
||||||
'[data-tier="' + this.tierId + '"]', this.$suite),
|
|
||||||
summaryMsg = resultSummary(this.counts.error, this.counts.warning, this.counts.notice,
|
|
||||||
this.testsWereRun);
|
|
||||||
|
|
||||||
$('.tier-summary', $top).text(summaryMsg);
|
|
||||||
$top.removeClass('ajax-loading', 'tests-failed', 'tests-passed',
|
|
||||||
'tests-notrun');
|
|
||||||
if (this.counts.error > 0) {
|
|
||||||
$top.addClass('tests-failed');
|
|
||||||
} else if (this.counts.warning > 0) {
|
|
||||||
$top.addClass('tests-warnings');
|
|
||||||
} else if (this.testsWereRun) {
|
|
||||||
$top.addClass('tests-passed');
|
|
||||||
} else {
|
|
||||||
$top.addClass('tests-notrun');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ResultsTier.prototype.wakeUp = function() {
|
|
||||||
var $title = $('h4', this.$dom),
|
|
||||||
changeLink;
|
|
||||||
$('.tier-results', this.$dom).empty();
|
|
||||||
this.$dom.removeClass('hidden');
|
|
||||||
this.$dom.show();
|
|
||||||
if (this.app) {
|
|
||||||
// Override the title with a special app/version title
|
|
||||||
$title.text(format('{0} {1} {2}',
|
|
||||||
this.app.trans[this.app.guid],
|
|
||||||
this.app.version,
|
|
||||||
gettext('Tests')));
|
|
||||||
changeLink = this.app.versionChangeLinks[this.app.guid + ' ' +
|
|
||||||
this.app.version];
|
|
||||||
if (changeLink) {
|
|
||||||
this.$dom.prepend(
|
|
||||||
format('<a class="version-change-link" href="{0}">{1}</a>',
|
|
||||||
changeLink,
|
|
||||||
// L10n: Example: Changes in Firefox 5
|
|
||||||
gettext(format('Changes in {0} {1}',
|
|
||||||
this.app.trans[this.app.guid],
|
|
||||||
/\d+/.exec(this.app.version)))));
|
|
||||||
}
|
|
||||||
} else if (!$title.text()) {
|
|
||||||
$title.text(gettext('Tests'));
|
|
||||||
}
|
|
||||||
$('.tier-results', this.$dom).removeClass('ajax-loading');
|
|
||||||
};
|
|
||||||
|
|
||||||
function MsgVisitor(suite, data) {
|
|
||||||
this.$suite = suite;
|
|
||||||
this.data = data;
|
|
||||||
this.$results = $('.results', suite);
|
|
||||||
this.msgSet = {};
|
|
||||||
this.tiers = {};
|
|
||||||
this.appTrans = null;
|
|
||||||
this.versionChangeLinks = null;
|
|
||||||
this.allCounts = {error: 0, warning: 0};
|
|
||||||
this.fileURL = suite.data('fileUrl');
|
|
||||||
this.fileID = suite.data('fileId');
|
|
||||||
}
|
|
||||||
|
|
||||||
MsgVisitor.prototype.createTier = function(tierId, options) {
|
|
||||||
var tier = new ResultsTier(this.$suite, tierId,
|
|
||||||
this.tierOptions(options));
|
|
||||||
return tier;
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.finish = function() {
|
|
||||||
var self = this;
|
|
||||||
$('.result', this.$suite).each(function(i, res) {
|
|
||||||
if (!$('.msg', res).length) {
|
|
||||||
// No messages so no tier was created.
|
|
||||||
self.getTier($('.tier-results', res).attr('data-tier'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$.each(this.tiers, function(tierId, tier) {
|
|
||||||
var tierSum = tier.summarize();
|
|
||||||
self.allCounts.error += tierSum.error;
|
|
||||||
self.allCounts.warning += tierSum.warning;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.clear = function() {
|
|
||||||
$.each(this.tiers, function(tierId, tier) {
|
|
||||||
tier.clear();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.getMsgType = function(msg) {
|
|
||||||
return msg['type'];
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.getTier = function(tierId, options) {
|
|
||||||
if (typeof options === 'undefined') {
|
|
||||||
options = {app: null};
|
|
||||||
}
|
|
||||||
if (!options.app
|
|
||||||
&& this.data.validation.ending_tier
|
|
||||||
&& this.data.validation.ending_tier < tierId) {
|
|
||||||
options.testsWereRun = false;
|
|
||||||
}
|
|
||||||
if (typeof this.tiers[tierId] === 'undefined') {
|
|
||||||
this.tiers[tierId] = this.createTier(tierId, options);
|
|
||||||
}
|
|
||||||
return this.tiers[tierId];
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.filterMessage = function(msg) {
|
|
||||||
return !(this.hideIgnored && msg.ignored)
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.message = function(msg, options) {
|
|
||||||
if (!this.filterMessage(msg)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.msgSet[msg.uid] !== 'undefined') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.msgSet[msg.uid] = true;
|
|
||||||
|
|
||||||
var tier = this.getTier(msg.tier, options),
|
|
||||||
msgDiv = $('<div class="msg"><h5></h5></div>'),
|
|
||||||
effectiveType = this.getMsgType(msg),
|
|
||||||
prefix = effectiveType == 'error' ? gettext('Error')
|
|
||||||
: gettext('Warning');
|
|
||||||
|
|
||||||
tier.tallyMsgType(effectiveType);
|
|
||||||
msgDiv.attr('id', 'v-msg-' + msg.uid);
|
|
||||||
msgDiv.addClass('msg-' + effectiveType);
|
|
||||||
|
|
||||||
// The "message" and "description" properties are escaped and linkified
|
|
||||||
// before we receive them.
|
|
||||||
$('h5', msgDiv).html(msg.message); // Sanitized HTML value.
|
|
||||||
|
|
||||||
// The validator returns the "description" as either string, or
|
|
||||||
// arrays of strings. We turn it into arrays when sanitizing.
|
|
||||||
$.each(msg.description, function(i, val) {
|
|
||||||
var $desc = $('<p>').html(val); // Sanitized HTML value.
|
|
||||||
if (i === 0) {
|
|
||||||
$desc.prepend(format('<strong>{0}:</strong> ', prefix));
|
|
||||||
}
|
|
||||||
msgDiv.append($desc);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (msg.file) {
|
|
||||||
var file = msg.file;
|
|
||||||
if (typeof file !== 'string') {
|
|
||||||
// For sub-packages, this will be a list of archive paths and
|
|
||||||
// a final file path, which we need to turn into a string.
|
|
||||||
// ['foo.xpi', 'chrome/thing.jar', 'content/file.js']
|
|
||||||
file = file.join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.fileURL) {
|
|
||||||
var url = this.fileURL + '?path=' + file;
|
|
||||||
if (msg.line) {
|
|
||||||
url += "#L" + msg.line;
|
|
||||||
}
|
|
||||||
var $link = $('<a>', { href: url, text: file,
|
|
||||||
target: 'file-viewer-' + this.fileID });
|
|
||||||
} else {
|
|
||||||
// There's no file browse URL for bare file uploads, so
|
|
||||||
// just display a path without a link to the sources.
|
|
||||||
$link = $('<span>', { text: file });
|
|
||||||
}
|
|
||||||
|
|
||||||
var $context = $('<div class="context">').append(
|
|
||||||
$('<div class="file">').append($link))
|
|
||||||
|
|
||||||
if (msg.context) {
|
|
||||||
var $code = $('<div class="code"></div>');
|
|
||||||
var $lines = $('<div class="lines"></div>');
|
|
||||||
var $innerCode = $('<div class="inner-code"></div>');
|
|
||||||
|
|
||||||
$code.append($lines, $innerCode);
|
|
||||||
|
|
||||||
// The line number in the message refers to the middle
|
|
||||||
// line of the context, so adjust accordingly.
|
|
||||||
var offset = Math.floor(msg.context.length / 2);
|
|
||||||
msg.context = formatCodeIndentation(msg.context);
|
|
||||||
$.each(msg.context, function(idx, code) {
|
|
||||||
if (code != null) {
|
|
||||||
$lines.append($('<div>', { text: msg.line + idx - offset }))
|
|
||||||
$innerCode.append($('<div>', { text: code }))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$context.append($code);
|
|
||||||
} else if (msg.line && typeof msg.column !== 'undefined') {
|
|
||||||
// Normally, the line number would be displayed with the
|
|
||||||
// context. If we have no context, display it with the
|
|
||||||
// filename.
|
|
||||||
$link.text(format(gettext('{0} line {1} column {2}'), [file, msg.line, msg.column]));
|
|
||||||
} else if (msg.line) {
|
|
||||||
$link.text(format(gettext('{0} line {1}'), [file, msg.line]));
|
|
||||||
}
|
|
||||||
|
|
||||||
msgDiv.append($context);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.tier-results', tier.$dom).append(msgDiv);
|
|
||||||
};
|
|
||||||
|
|
||||||
MsgVisitor.prototype.tierOptions = function(options) {
|
|
||||||
if (options && options.app) {
|
|
||||||
options.app.trans = this.appTrans;
|
|
||||||
options.app.versionChangeLinks = this.versionChangeLinks;
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
};
|
|
||||||
|
|
||||||
function buildResults(suite, data) {
|
|
||||||
var vis,
|
|
||||||
validation = data.validation,
|
|
||||||
summaryTxt;
|
|
||||||
|
|
||||||
function sortByType(messages) {
|
|
||||||
var ordering = [
|
|
||||||
'error', 'warning', 'notice', undefined /* no type */];
|
|
||||||
return _.sortBy(messages, function(msg) {
|
|
||||||
return ordering.indexOf(msg.type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function rebuildResults() {
|
|
||||||
vis = new MsgVisitor(suite, data);
|
|
||||||
$.each(sortByType(validation.messages), function(i, msg) {
|
|
||||||
vis.message(msg);
|
|
||||||
});
|
|
||||||
vis.finish();
|
|
||||||
|
|
||||||
if (validation.errors > 0) {
|
|
||||||
summaryTxt = gettext('Add-on failed validation.');
|
|
||||||
} else {
|
|
||||||
summaryTxt = gettext('Add-on passed validation.');
|
|
||||||
}
|
|
||||||
$('.suite-summary span', suite).text(summaryTxt);
|
|
||||||
$('.suite-summary', suite).show();
|
|
||||||
}
|
|
||||||
rebuildResults();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resultSummary(numErrors, numWarnings, numNotices, testsWereRun) {
|
|
||||||
if (!testsWereRun) {
|
|
||||||
return gettext('These tests were not run.');
|
|
||||||
}
|
|
||||||
// e.g. '1 error, 3 warnings'
|
|
||||||
var errors = format(ngettext('{0} error', '{0} errors', numErrors),
|
|
||||||
[numErrors]),
|
|
||||||
warnings = format(ngettext('{0} warning', '{0} warnings', numWarnings),
|
|
||||||
[numWarnings]),
|
|
||||||
notices = format(ngettext('{0} notice', '{0} notices', numNotices),
|
|
||||||
[numNotices]);
|
|
||||||
return format('{0}, {1}, {2}', errors, warnings, notices);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatCodeIndentation(lines) {
|
|
||||||
// Replaces leading tabs with spaces, and then trims the
|
|
||||||
// smallest common indentation space from each line.
|
|
||||||
|
|
||||||
function retab(line, tabstops) {
|
|
||||||
// Replaces tabs with spaces, to match the given tab stops.
|
|
||||||
|
|
||||||
var SPACES = " ";
|
|
||||||
tabstops = Math.min(tabstops || 4, SPACES.length);
|
|
||||||
|
|
||||||
function replace_tab(full_match, non_tab) {
|
|
||||||
if (non_tab) {
|
|
||||||
position += non_tab.length;
|
|
||||||
return non_tab;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var pos = position;
|
|
||||||
position += position % tabstops || tabstops;
|
|
||||||
return SPACES.substr(0, position - pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var position = 0;
|
|
||||||
return line.replace(/([^\t]+)|\t/g, replace_tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retab all lines and find the common indent.
|
|
||||||
var indent = Infinity;
|
|
||||||
lines = lines.map(function(line) {
|
|
||||||
// When the context line is at the start or end of the file,
|
|
||||||
// the line before or after the context line will be null.
|
|
||||||
if (line == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need the replace function to run even if there's no
|
|
||||||
// whitespace, so `indent` is properly updated. Stick with
|
|
||||||
// \s* rather than \s+.
|
|
||||||
return line.replace(/^(\s*)/, function(match) {
|
|
||||||
match = retab(match);
|
|
||||||
indent = Math.min(indent, match.length);
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trim off the common white space.
|
|
||||||
return lines.map(function(line) {
|
|
||||||
// Line may be null. Do not try to slice null.
|
|
||||||
return line && line.slice(indent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.addon-validator-suite', $doc).on('validate', function(e) {
|
|
||||||
var el = $(this),
|
|
||||||
data = el.data();
|
|
||||||
|
|
||||||
if (data.annotateUrl) {
|
|
||||||
el.on('change', '.ignore-duplicates-checkbox',
|
|
||||||
function(event) {
|
|
||||||
var $target = $(event.target);
|
|
||||||
$.ajax({type: 'POST',
|
|
||||||
url: data.annotateUrl,
|
|
||||||
data: { message: $target.attr('name'),
|
|
||||||
ignore_duplicates: $target.prop('checked') || undefined },
|
|
||||||
dataType: 'json'})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.validation) {
|
|
||||||
buildResults(el, {validation: data.validation})
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.test-tier,.tier-results', el).addClass('ajax-loading');
|
|
||||||
|
|
||||||
$.ajax({type: 'POST',
|
|
||||||
url: data.validateurl,
|
|
||||||
data: {},
|
|
||||||
success: function(data) {
|
|
||||||
if (data.validation == '') {
|
|
||||||
// Note: traceback is in data.error
|
|
||||||
data.validation = {
|
|
||||||
ending_tier: 1,
|
|
||||||
messages: [{
|
|
||||||
'type':'error',
|
|
||||||
message: gettext('Error'),
|
|
||||||
description: [
|
|
||||||
gettext('Validation task could not ' +
|
|
||||||
'complete or completed with ' +
|
|
||||||
'errors')],
|
|
||||||
tier: 1,
|
|
||||||
uid: '__global_error__'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
buildResults(el, data);
|
|
||||||
el.trigger('success.validation');
|
|
||||||
},
|
|
||||||
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
|
||||||
buildResults(el, {
|
|
||||||
validation: {
|
|
||||||
ending_tier: 1,
|
|
||||||
messages: [{
|
|
||||||
'type':'error',
|
|
||||||
message: gettext('Error'),
|
|
||||||
description: [gettext('Internal server error')],
|
|
||||||
tier: 1,
|
|
||||||
uid: '__global_error__'
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
el.trigger('badresponse.validation');
|
|
||||||
},
|
|
||||||
dataType: 'json'
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validate when the page loads.
|
if (msg.file) {
|
||||||
$('#addon-validator-suite').trigger('validate');
|
var file = msg.file;
|
||||||
|
if (typeof file !== 'string') {
|
||||||
|
// For sub-packages, this will be a list of archive paths and
|
||||||
|
// a final file path, which we need to turn into a string.
|
||||||
|
// ['foo.xpi', 'chrome/thing.jar', 'content/file.js']
|
||||||
|
file = file.join('/');
|
||||||
|
}
|
||||||
|
|
||||||
};
|
if (this.fileURL) {
|
||||||
|
var url = this.fileURL + '?path=' + file;
|
||||||
|
if (msg.line) {
|
||||||
|
url += '#L' + msg.line;
|
||||||
|
}
|
||||||
|
var $link = $('<a>', {
|
||||||
|
href: url,
|
||||||
|
text: file,
|
||||||
|
target: 'file-viewer-' + this.fileID,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// There's no file browse URL for bare file uploads, so
|
||||||
|
// just display a path without a link to the sources.
|
||||||
|
$link = $('<span>', { text: file });
|
||||||
|
}
|
||||||
|
|
||||||
|
var $context = $('<div class="context">').append(
|
||||||
|
$('<div class="file">').append($link),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (msg.context) {
|
||||||
|
var $code = $('<div class="code"></div>');
|
||||||
|
var $lines = $('<div class="lines"></div>');
|
||||||
|
var $innerCode = $('<div class="inner-code"></div>');
|
||||||
|
|
||||||
|
$code.append($lines, $innerCode);
|
||||||
|
|
||||||
|
// The line number in the message refers to the middle
|
||||||
|
// line of the context, so adjust accordingly.
|
||||||
|
var offset = Math.floor(msg.context.length / 2);
|
||||||
|
msg.context = formatCodeIndentation(msg.context);
|
||||||
|
$.each(msg.context, function (idx, code) {
|
||||||
|
if (code != null) {
|
||||||
|
$lines.append($('<div>', { text: msg.line + idx - offset }));
|
||||||
|
$innerCode.append($('<div>', { text: code }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$context.append($code);
|
||||||
|
} else if (msg.line && typeof msg.column !== 'undefined') {
|
||||||
|
// Normally, the line number would be displayed with the
|
||||||
|
// context. If we have no context, display it with the
|
||||||
|
// filename.
|
||||||
|
$link.text(
|
||||||
|
format(gettext('{0} line {1} column {2}'), [
|
||||||
|
file,
|
||||||
|
msg.line,
|
||||||
|
msg.column,
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
} else if (msg.line) {
|
||||||
|
$link.text(format(gettext('{0} line {1}'), [file, msg.line]));
|
||||||
|
}
|
||||||
|
|
||||||
|
msgDiv.append($context);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.tier-results', tier.$dom).append(msgDiv);
|
||||||
|
};
|
||||||
|
|
||||||
|
MsgVisitor.prototype.tierOptions = function (options) {
|
||||||
|
if (options && options.app) {
|
||||||
|
options.app.trans = this.appTrans;
|
||||||
|
options.app.versionChangeLinks = this.versionChangeLinks;
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildResults(suite, data) {
|
||||||
|
var vis,
|
||||||
|
validation = data.validation,
|
||||||
|
summaryTxt;
|
||||||
|
|
||||||
|
function sortByType(messages) {
|
||||||
|
var ordering = ['error', 'warning', 'notice', undefined /* no type */];
|
||||||
|
return _.sortBy(messages, function (msg) {
|
||||||
|
return ordering.indexOf(msg.type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function rebuildResults() {
|
||||||
|
vis = new MsgVisitor(suite, data);
|
||||||
|
$.each(sortByType(validation.messages), function (i, msg) {
|
||||||
|
vis.message(msg);
|
||||||
|
});
|
||||||
|
vis.finish();
|
||||||
|
|
||||||
|
if (validation.errors > 0) {
|
||||||
|
summaryTxt = gettext('Add-on failed validation.');
|
||||||
|
} else {
|
||||||
|
summaryTxt = gettext('Add-on passed validation.');
|
||||||
|
}
|
||||||
|
$('.suite-summary span', suite).text(summaryTxt);
|
||||||
|
$('.suite-summary', suite).show();
|
||||||
|
}
|
||||||
|
rebuildResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resultSummary(numErrors, numWarnings, numNotices, testsWereRun) {
|
||||||
|
if (!testsWereRun) {
|
||||||
|
return gettext('These tests were not run.');
|
||||||
|
}
|
||||||
|
// e.g. '1 error, 3 warnings'
|
||||||
|
var errors = format(ngettext('{0} error', '{0} errors', numErrors), [
|
||||||
|
numErrors,
|
||||||
|
]),
|
||||||
|
warnings = format(ngettext('{0} warning', '{0} warnings', numWarnings), [
|
||||||
|
numWarnings,
|
||||||
|
]),
|
||||||
|
notices = format(ngettext('{0} notice', '{0} notices', numNotices), [
|
||||||
|
numNotices,
|
||||||
|
]);
|
||||||
|
return format('{0}, {1}, {2}', errors, warnings, notices);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCodeIndentation(lines) {
|
||||||
|
// Replaces leading tabs with spaces, and then trims the
|
||||||
|
// smallest common indentation space from each line.
|
||||||
|
|
||||||
|
function retab(line, tabstops) {
|
||||||
|
// Replaces tabs with spaces, to match the given tab stops.
|
||||||
|
|
||||||
|
var SPACES = ' ';
|
||||||
|
tabstops = Math.min(tabstops || 4, SPACES.length);
|
||||||
|
|
||||||
|
function replace_tab(full_match, non_tab) {
|
||||||
|
if (non_tab) {
|
||||||
|
position += non_tab.length;
|
||||||
|
return non_tab;
|
||||||
|
} else {
|
||||||
|
var pos = position;
|
||||||
|
position += position % tabstops || tabstops;
|
||||||
|
return SPACES.substr(0, position - pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var position = 0;
|
||||||
|
return line.replace(/([^\t]+)|\t/g, replace_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retab all lines and find the common indent.
|
||||||
|
var indent = Infinity;
|
||||||
|
lines = lines.map(function (line) {
|
||||||
|
// When the context line is at the start or end of the file,
|
||||||
|
// the line before or after the context line will be null.
|
||||||
|
if (line == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need the replace function to run even if there's no
|
||||||
|
// whitespace, so `indent` is properly updated. Stick with
|
||||||
|
// \s* rather than \s+.
|
||||||
|
return line.replace(/^(\s*)/, function (match) {
|
||||||
|
match = retab(match);
|
||||||
|
indent = Math.min(indent, match.length);
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trim off the common white space.
|
||||||
|
return lines.map(function (line) {
|
||||||
|
// Line may be null. Do not try to slice null.
|
||||||
|
return line && line.slice(indent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.addon-validator-suite', $doc).on('validate', function (e) {
|
||||||
|
var el = $(this),
|
||||||
|
data = el.data();
|
||||||
|
|
||||||
|
if (data.annotateUrl) {
|
||||||
|
el.on('change', '.ignore-duplicates-checkbox', function (event) {
|
||||||
|
var $target = $(event.target);
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: data.annotateUrl,
|
||||||
|
data: {
|
||||||
|
message: $target.attr('name'),
|
||||||
|
ignore_duplicates: $target.prop('checked') || undefined,
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.validation) {
|
||||||
|
buildResults(el, { validation: data.validation });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.test-tier,.tier-results', el).addClass('ajax-loading');
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: data.validateurl,
|
||||||
|
data: {},
|
||||||
|
success: function (data) {
|
||||||
|
if (data.validation == '') {
|
||||||
|
// Note: traceback is in data.error
|
||||||
|
data.validation = {
|
||||||
|
ending_tier: 1,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
message: gettext('Error'),
|
||||||
|
description: [
|
||||||
|
gettext(
|
||||||
|
'Validation task could not ' +
|
||||||
|
'complete or completed with ' +
|
||||||
|
'errors',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
tier: 1,
|
||||||
|
uid: '__global_error__',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
buildResults(el, data);
|
||||||
|
el.trigger('success.validation');
|
||||||
|
},
|
||||||
|
error: function (XMLHttpRequest, textStatus, errorThrown) {
|
||||||
|
buildResults(el, {
|
||||||
|
validation: {
|
||||||
|
ending_tier: 1,
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
type: 'error',
|
||||||
|
message: gettext('Error'),
|
||||||
|
description: [gettext('Internal server error')],
|
||||||
|
tier: 1,
|
||||||
|
uid: '__global_error__',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
el.trigger('badresponse.validation');
|
||||||
|
},
|
||||||
|
dataType: 'json',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Validate when the page loads.
|
||||||
|
$('#addon-validator-suite').trigger('validate');
|
||||||
|
}
|
||||||
|
|
|
@ -72,10 +72,10 @@ describe(__filename, () => {
|
||||||
stats_stats(global.$);
|
stats_stats(global.$);
|
||||||
|
|
||||||
expect($('#export_data_csv').attr('href')).toEqual(
|
expect($('#export_data_csv').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20191007-20191013.csv`
|
`${defaultBaseUrl}${report}-day-20191007-20191013.csv`,
|
||||||
);
|
);
|
||||||
expect($('#export_data_json').attr('href')).toEqual(
|
expect($('#export_data_json').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20191007-20191013.json`
|
`${defaultBaseUrl}${report}-day-20191007-20191013.json`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -86,10 +86,10 @@ describe(__filename, () => {
|
||||||
stats_stats(global.$);
|
stats_stats(global.$);
|
||||||
|
|
||||||
expect($('#export_data_csv').attr('href')).toEqual(
|
expect($('#export_data_csv').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20190914-20191013.csv`
|
`${defaultBaseUrl}${report}-day-20190914-20191013.csv`,
|
||||||
);
|
);
|
||||||
expect($('#export_data_json').attr('href')).toEqual(
|
expect($('#export_data_json').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20190914-20191013.json`
|
`${defaultBaseUrl}${report}-day-20190914-20191013.json`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -116,10 +116,10 @@ describe(__filename, () => {
|
||||||
stats_stats(global.$, fakeSessionStorage);
|
stats_stats(global.$, fakeSessionStorage);
|
||||||
|
|
||||||
expect($('#export_data_csv').attr('href')).toEqual(
|
expect($('#export_data_csv').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20191115-20191125.csv`
|
`${defaultBaseUrl}${report}-day-20191115-20191125.csv`,
|
||||||
);
|
);
|
||||||
expect($('#export_data_json').attr('href')).toEqual(
|
expect($('#export_data_json').attr('href')).toEqual(
|
||||||
`${defaultBaseUrl}${report}-day-20191115-20191125.json`
|
`${defaultBaseUrl}${report}-day-20191115-20191125.json`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -74,7 +74,7 @@ commands =
|
||||||
[testenv:codestyle]
|
[testenv:codestyle]
|
||||||
recreate = True
|
recreate = True
|
||||||
commands =
|
commands =
|
||||||
make setup-codestyle
|
make setup-codestyle install_node_dependencies
|
||||||
make lint-codestyle
|
make lint-codestyle
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
|
|
Загрузка…
Ссылка в новой задаче