مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2026-01-17 13:39:46 +00:00
initial commit from appmail
هذا الالتزام موجود في:
55
app/assets/javascripts/application/application.coffee
Normal file
55
app/assets/javascripts/application/application.coffee
Normal file
@@ -0,0 +1,55 @@
|
||||
#= require jquery
|
||||
#= require jquery_ujs
|
||||
#= require turbolinks
|
||||
#= require_tree ./vendor/.
|
||||
#= require_self
|
||||
#= require_tree .
|
||||
|
||||
$ ->
|
||||
|
||||
isFirefox = -> !!navigator.userAgent.match(/firefox/i)
|
||||
|
||||
$('html').addClass('browser-firefox') if isFirefox()
|
||||
|
||||
$(document).on 'turbolinks:load', ->
|
||||
$('.js-multibox').multibox({inputCount: 6, classNames: {container: "multibox", input: 'input input--text multibox__input'}})
|
||||
|
||||
$(document).on 'keyup', (event)->
|
||||
return if $(event.target).is('input, select, textarea')
|
||||
if event.keyCode == 83
|
||||
$('.js-focus-on-s').focus()
|
||||
event.preventDefault()
|
||||
if event.keyCode == 70
|
||||
$('.js-focus-on-f').focus()
|
||||
event.preventDefault()
|
||||
|
||||
$(document).on 'click', 'html.main .flashMessage', ->
|
||||
$(this).hide 'fast', ->
|
||||
$(this).remove()
|
||||
|
||||
$(document).on 'click', '.js-toggle-helpbox', ->
|
||||
helpBox = $('.js-helpbox')
|
||||
if helpBox.hasClass('is-hidden')
|
||||
helpBox.removeClass('is-hidden')
|
||||
else
|
||||
helpBox.addClass('is-hidden')
|
||||
return false
|
||||
|
||||
$(document).on 'input', 'input[type=range]', ->
|
||||
value = $(this).val()
|
||||
updateAttr = $(this).attr('data-update')
|
||||
if updateAttr && updateAttr.length
|
||||
$("." + $(this).attr('data-update')).text(parseFloat(value, 10).toFixed(1))
|
||||
|
||||
$(document).on 'change', '.js-checkbox-list-toggle', ->
|
||||
$this = $(this)
|
||||
value = $this.val()
|
||||
$list = $this.parent().find('.checkboxList')
|
||||
if value == 'false' then $list.show() else $list.hide()
|
||||
|
||||
$(document).on 'click', '.js-toggle', ->
|
||||
$link = $(this)
|
||||
element = $link.attr('data-element')
|
||||
$(element, $link.parent()).toggle()
|
||||
false
|
||||
|
||||
61
app/assets/javascripts/application/elements/ajax.coffee
Normal file
61
app/assets/javascripts/application/elements/ajax.coffee
Normal file
@@ -0,0 +1,61 @@
|
||||
onStart = (event) ->
|
||||
$('.flashMessage').remove()
|
||||
$('input, select, textarea').blur()
|
||||
$target = $(event.target)
|
||||
if $target.is('form')
|
||||
$('.js-form-submit', $target).addClass('is-spinning')
|
||||
if $target.hasClass('button')
|
||||
$($target).addClass('is-spinning')
|
||||
|
||||
onComplete = (event, xhr)->
|
||||
$target = $(event.target)
|
||||
if xhr.responseJSON
|
||||
data = xhr.responseJSON
|
||||
if data.redirect_to
|
||||
Turbolinks.clearCache()
|
||||
Turbolinks.visit(data.redirect_to, {"action":"replace"})
|
||||
console.log "Redirected to #{data.redirect_to}"
|
||||
|
||||
if data.alert
|
||||
unSpin($target)
|
||||
alert(data.alert)
|
||||
|
||||
if data.form_errors
|
||||
if $target.is('form')
|
||||
unSpin($target)
|
||||
handleErrors($target, data.form_errors)
|
||||
|
||||
if data.flash
|
||||
unSpin($target)
|
||||
$('body .flashMessage').remove()
|
||||
for key, value of data.flash
|
||||
$message = $("<div class='flashMessage flashMessage--#{key}'>#{value}</div>")
|
||||
$('body').prepend($message)
|
||||
|
||||
if data.region_html
|
||||
unSpin($target)
|
||||
$('.js-ajax-region').replaceWith(data.region_html)
|
||||
$('[autofocus]', '.js-ajax-region').focus()
|
||||
|
||||
else
|
||||
console.log "Unsupported return."
|
||||
|
||||
unSpin = ($target)->
|
||||
$target.removeClass('is-spinning')
|
||||
$('.js-form-submit', $target).removeClass('is-spinning')
|
||||
|
||||
|
||||
handleErrors = (form, errors)->
|
||||
html = $("<div class='formErrors errorExplanation'><ul></ul</div>")
|
||||
list = $('ul', html)
|
||||
$.each errors, ->
|
||||
list.append("<li>#{this}</li>")
|
||||
$('.formErrors', form).remove()
|
||||
form.prepend($(html))
|
||||
console.log errors
|
||||
|
||||
$ ->
|
||||
$.ajaxSettings.dataType = 'json'
|
||||
$(document)
|
||||
.on 'ajax:before', onStart
|
||||
.on 'ajax:complete', onComplete
|
||||
@@ -0,0 +1,32 @@
|
||||
$(document).on 'turbolinks:load', ->
|
||||
|
||||
mailGraph = $('.mailGraph')
|
||||
|
||||
if mailGraph.length
|
||||
data = JSON.parse(mailGraph.attr('data-data'))
|
||||
incomingMail = []
|
||||
outgoingMail = []
|
||||
for d in data
|
||||
incomingMail.push(d.incoming)
|
||||
outgoingMail.push(d.outgoing)
|
||||
|
||||
data =
|
||||
series: [outgoingMail, incomingMail]
|
||||
options =
|
||||
fullWidth: true
|
||||
axisY:
|
||||
offset:40
|
||||
axisX:
|
||||
showGrid: false
|
||||
offset: 0
|
||||
showLabel: true
|
||||
height: '230px'
|
||||
showArea: true
|
||||
high: if incomingMail? && incomingMail.length then undefined else 1000
|
||||
chartPadding:
|
||||
top:0
|
||||
right:0
|
||||
bottom:0
|
||||
left:0
|
||||
|
||||
new Chartist.Line '.mailGraph__graph', data, options
|
||||
@@ -0,0 +1,8 @@
|
||||
$ ->
|
||||
$(document).on 'click', '.js-remember a', ->
|
||||
$parent = $(this).parents('.js-remember')
|
||||
value = $(this).attr('data-remember')
|
||||
$parent.remove()
|
||||
if value == 'yes'
|
||||
$.post('/persist')
|
||||
false
|
||||
113
app/assets/javascripts/application/elements/searchable.coffee
Normal file
113
app/assets/javascripts/application/elements/searchable.coffee
Normal file
@@ -0,0 +1,113 @@
|
||||
ENTER = 13
|
||||
DOWN_ARROW = 40
|
||||
UP_ARROW = 38
|
||||
|
||||
filterList = ($container, query) ->
|
||||
$items = getItems($container)
|
||||
index = $container.data('searchifyIndex')
|
||||
re = new RegExp(query, 'g')
|
||||
$matches = $items.filter (i, item) ->
|
||||
value = $(item).data('value')
|
||||
re.test(value)
|
||||
$items.addClass('is-hidden').filter($matches).removeClass('is-hidden')
|
||||
toggleState($container, $matches.length > 0)
|
||||
if index?
|
||||
index = 0
|
||||
$container.data('searchifyIndex', index)
|
||||
highlightItem($container, $matches, index)
|
||||
|
||||
getContainer = ($el) ->
|
||||
$el.closest('.js-searchable')
|
||||
|
||||
getEmpty = ($container) ->
|
||||
$('.js-searchable__empty', $container)
|
||||
|
||||
getList = ($container) ->
|
||||
$('.js-searchable__list', $container)
|
||||
|
||||
getItems = ($container) ->
|
||||
$('.js-searchable__item', $container)
|
||||
|
||||
highlightItem = ($container, $scope, index) ->
|
||||
$items = getItems($container)
|
||||
$items.removeClass('is-highlighted')
|
||||
$scope.eq(index).addClass('is-highlighted') if index? && $scope.length
|
||||
|
||||
highlightNext = ($container) ->
|
||||
$matches = getMatches($container)
|
||||
index = $container.data('searchifyIndex')
|
||||
return unless $matches.length
|
||||
if index?
|
||||
return if index == $matches.length - 1
|
||||
newIndex = index + 1
|
||||
else
|
||||
newIndex = 0
|
||||
$container.data('searchifyIndex', newIndex)
|
||||
highlightItem($container, $matches, newIndex)
|
||||
|
||||
highlightPrev = ($container) ->
|
||||
$matches = getMatches($container)
|
||||
index = $container.data('searchifyIndex')
|
||||
return unless $matches.length
|
||||
if index?
|
||||
return if index == 0
|
||||
newIndex = index - 1
|
||||
else
|
||||
newIndex = 0
|
||||
$container.data('searchifyIndex', newIndex)
|
||||
highlightItem($container, $matches, newIndex)
|
||||
|
||||
getMatches = ($container) ->
|
||||
$items = getItems($container)
|
||||
$items.filter(':not(.is-hidden)')
|
||||
|
||||
searchify = (str) ->
|
||||
str.toLowerCase().replace(/\W/g, '')
|
||||
|
||||
selectHighlighted = ($container) ->
|
||||
index = $container.data('searchifyIndex')
|
||||
$matches = getMatches($container)
|
||||
return unless index? && $matches.length
|
||||
url = $matches.eq(index).data('url')
|
||||
Turbolinks.visit(url)
|
||||
|
||||
showAll = ($container) ->
|
||||
$items = getItems($container)
|
||||
index = $container.data('searchifyIndex')
|
||||
$items.removeClass('is-hidden')
|
||||
toggleState($container, true)
|
||||
if index?
|
||||
index = 0
|
||||
$container.data('searchifyIndex', index)
|
||||
highlightItem($container, $items, index)
|
||||
|
||||
toggleState = ($container, predicate) ->
|
||||
$empty = getEmpty($container)
|
||||
$list = getList($container)
|
||||
$empty.toggleClass('is-hidden', predicate)
|
||||
$list.toggleClass('is-hidden', !predicate)
|
||||
|
||||
# Event Handlers
|
||||
|
||||
handleInput = (event) ->
|
||||
$input = $(event.target)
|
||||
$container = getContainer($input)
|
||||
query = searchify($input.val())
|
||||
if query.length then filterList($container, query) else showAll($container)
|
||||
|
||||
handleKeydown = (event) ->
|
||||
$container = getContainer($(event.target))
|
||||
keyCode = event.keyCode
|
||||
if keyCode == DOWN_ARROW
|
||||
event.preventDefault()
|
||||
highlightNext($container)
|
||||
else if keyCode == ENTER
|
||||
event.preventDefault()
|
||||
selectHighlighted($container)
|
||||
else if keyCode == UP_ARROW
|
||||
event.preventDefault()
|
||||
highlightPrev($container)
|
||||
$ ->
|
||||
$(document)
|
||||
.on('input', '.js-searchable__input', handleInput)
|
||||
.on('keydown', '.js-searchable__input', handleKeydown)
|
||||
29
app/assets/javascripts/application/modules/helpers.coffee
Normal file
29
app/assets/javascripts/application/modules/helpers.coffee
Normal file
@@ -0,0 +1,29 @@
|
||||
Postal.Helpers =
|
||||
numberWithDelimiters: (number)->
|
||||
number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||||
|
||||
pluralize: (number, word)->
|
||||
if number == 1
|
||||
"1 #{word}"
|
||||
else
|
||||
"#{number} #{word}s"
|
||||
|
||||
numberToHumanSize: (sizeInBytes)->
|
||||
wholes = Math.floor(Math.log(sizeInBytes) / Math.log(1024))
|
||||
unit = ['bytes', 'KB', 'MB', 'GB', 'TB'][wholes]
|
||||
i = (sizeInBytes / Math.pow(1024, wholes))
|
||||
if unit
|
||||
i = if unit == 'bytes' then i.toFixed(0) else i.toFixed(2)
|
||||
"#{i} #{unit}"
|
||||
else
|
||||
"0 bytes"
|
||||
|
||||
styleWidth: (width)->
|
||||
width = 100 if width > 100.0
|
||||
width = 0 if width < 0
|
||||
string = "width:#{width}%;"
|
||||
if width >= 100
|
||||
string = string + "background-color:#e2383a;"
|
||||
else if width >= 90
|
||||
string = string + "background-color:#e8581f;"
|
||||
string
|
||||
4331
app/assets/javascripts/application/vendor/chartist.js
مباع
Normal file
4331
app/assets/javascripts/application/vendor/chartist.js
مباع
Normal file
تم حذف اختلاف الملف لأن الملف كبير جداً
تحميل الاختلاف
197
app/assets/javascripts/application/vendor/jquery.multibox.js
مباع
Normal file
197
app/assets/javascripts/application/vendor/jquery.multibox.js
مباع
Normal file
@@ -0,0 +1,197 @@
|
||||
(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
function Multibox($el, options) {
|
||||
this.$el = $el;
|
||||
this.options = options;
|
||||
this.draw();
|
||||
this.listen();
|
||||
}
|
||||
|
||||
Multibox.prototype.destroy = function destroy() {
|
||||
this.$inputs.off();
|
||||
this.$el.detach();
|
||||
this.$container.replaceWith(this.$el);
|
||||
|
||||
if (this.previousType) {
|
||||
this.$el.attr('type', this.previousType);
|
||||
}
|
||||
};
|
||||
|
||||
Multibox.prototype.draw = function draw() {
|
||||
var classNames = this.options.classNames;
|
||||
var inputAutofocus = this.$el.attr('autofocus');
|
||||
var inputType = this.$el.attr('type');
|
||||
var inputValue = this.$el.val();
|
||||
|
||||
var focusIndex;
|
||||
var inputIndex;
|
||||
var text;
|
||||
|
||||
if (inputType !== 'hidden') {
|
||||
this.previousType = inputType;
|
||||
this.$el.attr('type', 'hidden');
|
||||
}
|
||||
|
||||
this.$container = $('<div />', {
|
||||
'class': classNames.container
|
||||
});
|
||||
|
||||
var size = Array.apply(null, Array(this.options.inputCount));
|
||||
|
||||
this.$inputs = $();
|
||||
|
||||
$.each(size, function () {
|
||||
this.$inputs = this.$inputs.add($('<input />', {
|
||||
'class': classNames.input,
|
||||
maxlength: 1,
|
||||
size: 1,
|
||||
type: 'text'
|
||||
}));
|
||||
}.bind(this));
|
||||
|
||||
this.$container .append(this.$inputs);
|
||||
this.$el.replaceWith(this.$container);
|
||||
this.$container.append(this.$el);
|
||||
|
||||
text = this.filterString(inputValue);
|
||||
|
||||
if (text.length) {
|
||||
inputIndex = this.setFromString(0, text);
|
||||
}
|
||||
|
||||
if (inputAutofocus) {
|
||||
if (inputIndex === undefined) {
|
||||
focusIndex = 0;
|
||||
} else {
|
||||
focusIndex = (inputIndex == this.$inputs.length ? inputIndex - 1 : inputIndex);
|
||||
}
|
||||
this.$inputs.eq(focusIndex).focus();
|
||||
}
|
||||
};
|
||||
|
||||
Multibox.prototype.handleKeydown = function handleKeydown(event) {
|
||||
var $input = $(event.target);
|
||||
var $prev;
|
||||
|
||||
if (event.keyCode === 8) {
|
||||
event.preventDefault();
|
||||
|
||||
$prev = $input.prev();
|
||||
|
||||
if ($prev.length) {
|
||||
$prev.focus();
|
||||
}
|
||||
|
||||
if (event.target.value) {
|
||||
$input.val('');
|
||||
} else {
|
||||
$prev.val('');
|
||||
}
|
||||
}
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
Multibox.prototype.handleInput = function handleInput(event) {
|
||||
var $input = $(event.target);
|
||||
var $next = $input.next();
|
||||
var value = $input.val();
|
||||
var filtered = this.filterString(value);
|
||||
|
||||
$input.val(filtered);
|
||||
|
||||
if (filtered && $next.length) {
|
||||
$next.focus();
|
||||
}
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
Multibox.prototype.handlePaste = function handlePaste(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var $input = $(event.target);
|
||||
var clipboardData = event.originalEvent.clipboardData;
|
||||
var text = clipboardData.getData('text');
|
||||
|
||||
var filtered = this.filterString(text);
|
||||
|
||||
if (!filtered.length) return;
|
||||
|
||||
var inputIndex = this.setFromString(this.$inputs.index($input), filtered);
|
||||
var focusIndex = (inputIndex == this.$inputs.length ? inputIndex - 1 : inputIndex);
|
||||
|
||||
this.$inputs.eq(focusIndex).focus();
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
Multibox.prototype.listen = function listen() {
|
||||
this.$inputs.on('input', this.handleInput.bind(this));
|
||||
this.$inputs.on('keydown', this.handleKeydown.bind(this));
|
||||
this.$inputs.on('paste', this.handlePaste.bind(this));
|
||||
};
|
||||
|
||||
Multibox.prototype.filterString = function filterString(str) {
|
||||
return str.replace(this.options.regex, '');
|
||||
};
|
||||
|
||||
Multibox.prototype.setFromString = function setFromString(index, str) {
|
||||
var inputIndex = index;
|
||||
var strIndex = 0;
|
||||
|
||||
while (this.$inputs.eq(inputIndex).length && str[strIndex]) {
|
||||
this.$inputs.eq(inputIndex).val(str[strIndex]);
|
||||
inputIndex++;
|
||||
strIndex++;
|
||||
}
|
||||
|
||||
return inputIndex;
|
||||
};
|
||||
|
||||
Multibox.prototype.update = function update() {
|
||||
var values = [];
|
||||
var value;
|
||||
|
||||
this.$inputs.each(function(i, input) {
|
||||
values.push(input.value);
|
||||
});
|
||||
|
||||
value = values.join('');
|
||||
|
||||
this.$el
|
||||
.val(value)
|
||||
.trigger('change');
|
||||
};
|
||||
|
||||
$.fn.multibox = function multibox(options) {
|
||||
var instance;
|
||||
|
||||
if (typeof options === 'object' || options == undefined) {
|
||||
options = (options || {});
|
||||
|
||||
options = $.extend({}, {
|
||||
classNames: {
|
||||
container: 'multibox',
|
||||
input: 'multibox-input'
|
||||
},
|
||||
inputCount: 4,
|
||||
regex: /\D/g
|
||||
}, options);
|
||||
|
||||
if (this.length) {
|
||||
instance = new Multibox(this, options);
|
||||
this.data('multibox', instance);
|
||||
}
|
||||
} else if (options === 'destroy') {
|
||||
if (this.data('multibox')) {
|
||||
instance = this.data('multibox');
|
||||
instance.destroy();
|
||||
this.data('multibox', null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery));
|
||||
المرجع في مشكلة جديدة
حظر مستخدم