1
0
Fork 0

initial commit

This commit is contained in:
Sangbum Kim 2017-04-27 23:30:52 +09:00
commit 9e2a58cd4d
127 changed files with 23001 additions and 0 deletions

130
.gitignore vendored Normal file
View File

@ -0,0 +1,130 @@
# Created by https://www.gitignore.io/api/intellij,go,linux,osx,windows
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
.idea
*.iml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Go ###
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
### Linux ###
*~
*.swp
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
build/
bind/
vendor/
mecab/
*.log
.vscode/
changer
settings_test.yml

61
Makefile Normal file
View File

@ -0,0 +1,61 @@
# This how we want to name the binary output
BINARY=changer
# These are the values we want to pass for VERSION and BUILD
# git tag 1.0.1
# git commit -am "One more change after the tags"
VERSION=`git describe --always`
BUILD=`date +%FT%T%z`
# Setup the -ldflags option for go build here, interpolate the variable values
LDFLAGS=-ldflags "-w -s -X amuz.es/gogs/infra/changer/util.version=${VERSION} -X amuz.es/gogs/infra/changer/util.buildDate=${BUILD}"
CGO_ENABLED=0
# Builds the project
build:
go build ${LDFLAGS} -o ${BINARY}
strip -x ${BINARY}
# Builds the project
setup:
go get -u github.com/kardianos/govendor
go get -u github.com/jteeuwen/go-bindata/...
# go get -u github.com/gogo/protobuf/proto
# go get -u github.com/gogo/protobuf/protoc-gen-gogo
# go get -u github.com/gogo/protobuf/gogoproto
# go get -u github.com/gogo/protobuf/protoc-gen-gofast
# go get -u github.com/gogo/protobuf/protoc-gen-gogofaster
# go get -u github.com/golang/protobuf/proto
${GOPATH}/bin/govendor fetch -v +missing
#libjpeg-turbo
generate:
go get -u github.com/gogo/protobuf/proto
go get -u github.com/gogo/protobuf/protoc-gen-gogo
go get -u github.com/gogo/protobuf/gogoproto
go get -u github.com/gogo/protobuf/protoc-gen-gofast
go get -u github.com/gogo/protobuf/protoc-gen-gogofaster
go get -u github.com/golang/protobuf/proto
go get -u github.com/mailru/easyjson
protoc --gogofaster_out=. --proto_path=../../:. subsys/redis/app_token_data.proto
go-bindata -nomemcopy -ignore .DS_Store -prefix asset/static -o bind/static/data.go -pkg static asset/static/...
go-bindata -nomemcopy -ignore .DS_Store -prefix asset/template -o bind/template/data.go -pkg template asset/template/...
#${GOPATH}/bin/ffjson subsys/http/iface/rest.go
${GOPATH}/bin/easyjson subsys/http/iface/rest.go
# go-bindata -debug -nomemcopy -ignore .DS_Store -prefix asset/static -o bind/static/data.go -pkg static asset/static/...
# go-bindata -debug -nomemcopy -ignore .DS_Store -prefix asset/template -o bind/template/data.go -pkg template asset/template/...
strip:
upx rooibos
# Installs our project: copies binaries
install:
go install ${LDFLAGS}
# Cleans our project: deletes binaries
clean:
if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi
.PHONY: clean install

98
README.md Normal file
View File

@ -0,0 +1,98 @@
![alt tag](https://upload.wikimedia.org/wikipedia/commons/2/23/Golang.png)
[![Build Status](https://travis-ci.org/Massad/gin-boilerplate.svg?branch=master)](https://travis-ci.org/Massad/gin-boilerplate)
[![Join the chat at https://gitter.im/Massad/gin-boilerplate](https://badges.gitter.im/Massad/gin-boilerplate.svg)](https://gitter.im/Massad/gin-boilerplate?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Welcome to **Golang Gin boilerplate**!
The fastest way to deploy a restful api's with [Gin Framework](https://gin-gonic.github.io/gin/) with a structured project that defaults to **PostgreSQL** database and **Redis** as the session storage.
## Configured with
* [go-gorp](github.com/go-gorp/gorp): Go Relational Persistence
* [RedisStore](https://github.com/gin-gonic/contrib/tree/master/sessions): Gin middleware for session management with multi-backend support (currently cookie, Redis).
* Built-in **CORS Middleware**
* Feature **PostgreSQL 9.4** JSON queries
* Unit test
### Installation
```
$ go get github.com/Massad/gin-boilerplate
```
```
$ cd $GOPATH/src/github.com/Massad/gin-boilerplate
```
```
$ go get -t -v ./...
```
> Sometimes you need to get this package manually
```
$ go get github.com/bmizerany/assert
```
You will find the **database.sql** in `db/database.sql`
And you can import the postgres database using this command:
```
$ psql -U postgres -h localhost < ./db/database.sql
```
## Running Your Application
```
$ go run *.go
```
## Building Your Application
```
$ go build -v
```
```
$ ./gin-boilerplate
```
## Testing Your Application
```
$ go test -v ./tests/*
```
## Import Postman Collection (API's)
You can import from this [link](https://www.getpostman.com/collections/ac0680f90961bafd5de7). If you don't have **Postman**, check this link [https://www.getpostman.com](https://www.getpostman.com/)
## Contribution
You are welcome to contribute to keep it up to date and always improving!
If you have any question or need help, drop a message at [https://gitter.im/Massad/gin-boilerplate](https://gitter.im/Massad/gin-boilerplate)
---
## License
(The MIT License)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,12 @@
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
width="40px" height="40px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<path fill="#eb3c6a" d="M43.935,25.145c0-10.318-8.364-18.683-18.683-18.683c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615c8.072,0,14.615,6.543,14.615,14.615H43.935z">
<animateTransform attributeType="xml"
attributeName="transform"
type="rotate"
from="0 25 25"
to="360 25 25"
dur="0.6s"
repeatCount="indefinite"/>
</path>
</svg>

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
asset/static/images/bug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,33 @@
define([
'require',
'jquery',
'svg!../../images/ajax-loader.svg',
'jquery.blockui'
], function (require,
$,
loadimg) {
"use strict";
return {
blockUI: function (item) {
$(item).block({
message: loadimg,
css: {
border: 'none',
padding: '0px',
width: '40px',
height: '40px',
backgroundColor: 'transparent'
},
overlayCSS: {
backgroundColor: '#fff',
opacity: 0.9,
cursor: 'wait'
}
});
}
,
unblockUI: function (item) {
$(item).unblock();
}
}
});

View File

@ -0,0 +1,102 @@
'use strict';
function dep(baseUrl) {
// Require.js allows us to configure shortcut alias
return {
baseUrl: baseUrl,
bundles: {
// "javascripts/common/export.min": [
// 'javascripts/common/enums',
// 'javascripts/common/routes'
// ],
"javascripts/common/mixed": [
'javascripts/common/tether.mixed',
'javascripts/common/toastr.mixed'
]
},
map: {
'*': {
tpl: 'https://cdnjs.cloudflare.com/ajax/libs/requirejs-tpl/0.0.2/tpl.min.js',
toastr: 'javascripts/common/toastr.mixed',
tether: 'javascripts/common/tether.mixed',
postcode: 'javascripts/common/postcode.mixed!',
},
'javascripts/common/toastr.mixed': {
'toastr': 'toastr'
},
'javascripts/common/tether.mixed': {
'tether': 'tether'
},
},
paths: {
text: 'https://cdnjs.cloudflare.com/ajax/libs/require-text/2.0.12/text.min',
svg: 'libs/requirejs-svg-0.0.1/svg.min',
css: 'https://cdnjs.cloudflare.com/ajax/libs/require-css/0.1.8/css.min',
json: 'https://cdnjs.cloudflare.com/ajax/libs/requirejs-plugins/1.0.3/json.min',
noext: 'https://cdnjs.cloudflare.com/ajax/libs/requirejs-plugins/1.0.3/noext.min',
slimscroll: 'https://cdnjs.cloudflare.com/ajax/libs/jQuery-slimScroll/1.3.8/jquery.slimscroll.min',
jquery: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min',
'jquery.form': 'https://cdnjs.cloudflare.com/ajax/libs/jquery.form/3.51/jquery.form.min',
'jquery.blockui': 'https://cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min',
underscore: 'https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min',
'underscore.string': 'https://cdnjs.cloudflare.com/ajax/libs/underscore.string/3.3.4/underscore.string.min',
toastr: 'https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min',
bootstrap: 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/js/bootstrap',
domReady: 'https://cdnjs.cloudflare.com/ajax/libs/require-domReady/2.0.1/domReady.min',
backbone: 'https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min',
'backbone.paginator': 'https://cdnjs.cloudflare.com/ajax/libs/backbone.paginator/2.0.5/backbone.paginator.min',
'backbone.bootstrapModal': 'libs/backbone-bootstrap-modal/backbone-bootstrap-modal',
'backbone.computedfields': 'libs/backbone-computedfields-0.0.11/backbone.computedfields.min',
moment: 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.16.0/moment.min',
tether: 'https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether',
'autoNumeric': 'https://cdnjs.cloudflare.com/ajax/libs/autonumeric/1.9.46/autoNumeric.min',
'browser-update': 'https://browser-update.org/update.min',
},
// scripts that do not call define() to register a module
shim: {
bootstrap: {
deps: ['jquery', 'tether'],
// exports: "$.fn.tooltip"
},
slimscroll: {
deps: ['jquery'],
exports: "$.fn.slimScroll"
},
underscore: {
exports: '_'
},
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
'jquery.form': {
deps: ['jquery'],
exports: '$.fn.ajaxSubmit'
},
toastr: {
deps: [
'css!https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min'
]
},
'backbone.computedfields': {
deps: [
'backbone',
],
exports: 'Backbone.ComputedFields',
},
'ion.rangeSlider': {
deps: [
'jquery',
'css!https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/css/ion.rangeSlider.min',
'css!https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.1.4/css/ion.rangeSlider.skinFlat.min'
],
exports: '$.fn.ionRangeSlider'
}
}
};
};

View File

@ -0,0 +1,34 @@
define(['require', 'domReady', 'jquery', 'bootstrap', 'slimscroll'], function (require, domReady, $, bootstrap) {
"use strict";
// Slimscroll
$('.slimscroll').slimscroll({
height: "100%",
color: "#ccc",
distance: "0",
opacity: 0.3,
allowPageScroll: true,
borderRadius: "0",
railBorderRadius: "0"
});
// Makes .page-inner height same as .page-sidebar height
var sidebarAndContentHeight = function () {
var content = $('.page-inner'),
sidebar = $('.page-sidebar'),
body = $('body'),
height = $(window).height();
if (height >= content.height()) {
content.attr('style', 'min-height:' + height + 'px !important');
}
};
domReady(function () {
sidebarAndContentHeight();
$(window).on('resize', sidebarAndContentHeight);
window.$buoop = {vs: {i: 9, f: -2, o: -2, s: 6, c: -2}, unsecure: true, api: 4};
require(['browser-update']);
});
});

View File

@ -0,0 +1,26 @@
define('javascripts/common/tether.mixed',['tether'], function (tether) {
"use strict";
window.Tether=tether;
return tether;
});
define('javascripts/common/toastr.mixed',['toastr'], function (toastr) {
"use strict";
toastr.options = {
closeButton: true,
debug: false,
newestOnTop: true,
progressBar: false,
positionClass: 'toast-top-right',
preventDuplicates: false,
onclick: null,
showDuration: '300',
hideDuration: '1000',
timeOut: '5000',
extendedTimeOut: '1000',
showEasing: 'swing',
hideEasing: 'linear',
showMethod: 'fadeIn',
hideMethod: 'fadeOut'
};
return toastr;
});

View File

@ -0,0 +1,20 @@
define(['jquery', 'underscore', 'backbone.bootstrapModal', 'tpl!views/widgets/modal'], function ($, _, Modal, template) {
"use strict";
return Modal.extend({
initialize: function (options) {
this.options = _.extend({
title: null,
okText: 'OK',
focusOk: true,
okCloses: false,
cancelText: 'Cancel',
showFooter: true,
allowCancel: true,
escape: true,
animate: true,
template: template,
enterTriggersOk: false
}, options);
}
});
});

View File

@ -0,0 +1,284 @@
/**
* Bootstrap Modal wrapper for use with Backbone.
*
* Takes care of instantiation, manages multiple modals,
* adds several options and removes the element from the DOM when closed
*
* @author Charles Davison <charlie@powmedia.co.uk>
*
* Events:
* shown: Fired when the modal has finished animating in
* hidden: Fired when the modal has finished animating out
* cancel: The user dismissed the modal
* ok: The user clicked OK
*/
define(['jquery', 'underscore', 'backbone'], function ($, _, Backbone) {
//Set custom template settings
var _interpolateBackup = _.templateSettings;
_.templateSettings = {
interpolate: /\{\{(.+?)\}\}/g,
evaluate: /<%([\s\S]+?)%>/g
};
var template = _.template('\
<div class="modal-dialog"><div class="modal-content">\
<% if (title) { %>\
<div class="modal-header">\
<% if (allowCancel) { %>\
<a class="close">&times;</a>\
<% } %>\
<h4>{{title}}</h4>\
</div>\
<% } %>\
<div class="modal-body">{{content}}</div>\
<% if (showFooter) { %>\
<div class="modal-footer">\
<% if (allowCancel) { %>\
<% if (cancelText) { %>\
<a href="#" class="btn cancel">{{cancelText}}</a>\
<% } %>\
<% } %>\
<a href="#" class="btn ok btn-primary">{{okText}}</a>\
</div>\
<% } %>\
</div></div>\
');
//Reset to users' template settings
_.templateSettings = _interpolateBackup;
var Modal = Backbone.View.extend({
className: 'modal',
events: {
'click .close': function(event) {
event.preventDefault();
this.trigger('cancel');
if (this.options.content && this.options.content.trigger) {
this.options.content.trigger('cancel', this);
}
},
'click .cancel': function(event) {
event.preventDefault();
this.trigger('cancel');
if (this.options.content && this.options.content.trigger) {
this.options.content.trigger('cancel', this);
}
},
'click .ok': function(event) {
event.preventDefault();
this.trigger('ok');
if (this.options.content && this.options.content.trigger) {
this.options.content.trigger('ok', this);
}
if (this.options.okCloses) {
this.close();
}
},
'keypress': function(event) {
if (this.options.enterTriggersOk && event.which == 13) {
event.preventDefault();
this.trigger('ok');
if (this.options.content && this.options.content.trigger) {
this.options.content.trigger('ok', this);
}
if (this.options.okCloses) {
this.close();
}
}
}
},
/**
* Creates an instance of a Bootstrap Modal
*
* @see http://twitter.github.com/bootstrap/javascript.html#modals
*
* @param {Object} options
* @param {String|View} [options.content] Modal content. Default: none
* @param {String} [options.title] Title. Default: none
* @param {String} [options.okText] Text for the OK button. Default: 'OK'
* @param {String} [options.cancelText] Text for the cancel button. Default: 'Cancel'. If passed a falsey value, the button will be removed
* @param {Boolean} [options.allowCancel Whether the modal can be closed, other than by pressing OK. Default: true
* @param {Boolean} [options.escape] Whether the 'esc' key can dismiss the modal. Default: true, but false if options.cancellable is true
* @param {Boolean} [options.animate] Whether to animate in/out. Default: false
* @param {Function} [options.template] Compiled underscore template to override the default one
* @param {Boolean} [options.enterTriggersOk] Whether the 'enter' key will trigger OK. Default: false
*/
initialize: function(options) {
this.options = _.extend({
title: null,
okText: 'OK',
focusOk: true,
okCloses: true,
cancelText: 'Cancel',
showFooter: true,
allowCancel: true,
escape: true,
animate: false,
template: template,
enterTriggersOk: false
}, options);
},
/**
* Creates the DOM element
*
* @api private
*/
render: function() {
var $el = this.$el,
options = this.options,
content = options.content;
//Create the modal container
$el.html(options.template(options));
var $content = this.$content = $el.find('.modal-body')
//Insert the main content if it's a view
if (content && content.$el) {
content.render();
$el.find('.modal-body').html(content.$el);
}
if (options.animate) $el.addClass('fade');
this.isRendered = true;
return this;
},
/**
* Renders and shows the modal
*
* @param {Function} [cb] Optional callback that runs only when OK is pressed.
*/
open: function(cb) {
if (!this.isRendered) this.render();
var self = this,
$el = this.$el;
//Create it
$el.modal(_.extend({
keyboard: this.options.allowCancel,
backdrop: this.options.allowCancel ? true : 'static'
}, this.options.modalOptions));
//Focus OK button
$el.one('shown.bs.modal', function() {
if (self.options.focusOk) {
$el.find('.btn.ok').focus();
}
if (self.options.content && self.options.content.trigger) {
self.options.content.trigger('shown', self);
}
self.trigger('shown');
});
//Adjust the modal and backdrop z-index; for dealing with multiple modals
var numModals = Modal.count,
$backdrop = $('.modal-backdrop:eq('+numModals+')'),
backdropIndex = parseInt($backdrop.css('z-index'),10),
elIndex = parseInt($backdrop.css('z-index'), 10);
$backdrop.css('z-index', backdropIndex + numModals);
this.$el.css('z-index', elIndex + numModals);
if (this.options.allowCancel) {
$backdrop.one('click', function() {
if (self.options.content && self.options.content.trigger) {
self.options.content.trigger('cancel', self);
}
self.trigger('cancel');
});
$(document).one('keyup.dismiss.modal', function (e) {
e.which == 27 && self.trigger('cancel');
if (self.options.content && self.options.content.trigger) {
e.which == 27 && self.options.content.trigger('shown', self);
}
});
}
this.on('cancel', function() {
self.close();
});
Modal.count++;
//Run callback on OK if provided
if (cb) {
self.on('ok', cb);
}
return this;
},
/**
* Closes the modal
*/
close: function() {
var self = this,
$el = this.$el;
//Check if the modal should stay open
if (this._preventClose) {
this._preventClose = false;
return;
}
$el.one('hidden.bs.modal', function onHidden(e) {
// Ignore events propagated from interior objects, like bootstrap tooltips
if(e.target !== e.currentTarget){
return $el.one('hidden', onHidden);
}
self.remove();
if (self.options.content && self.options.content.trigger) {
self.options.content.trigger('hidden', self);
}
self.trigger('hidden');
});
$el.modal('hide');
Modal.count--;
},
/**
* Stop the modal from closing.
* Can be called from within a 'close' or 'ok' event listener.
*/
preventClose: function() {
this._preventClose = true;
}
}, {
//STATICS
//The number of modals on display
count: 0
});
return Modal;
});

View File

@ -0,0 +1,136 @@
// Backbone.ComputedFields, v0.0.10
// Copyright (c)2014 alexander.beletsky@gmail.com
// Distributed under MIT license
// https://github.com/alexanderbeletsky/backbone-computedfields
Backbone.ComputedFields = (function(Backbone, _){
var ComputedFields = function (model) {
this.model = model;
this._computedFields = [];
this.initialize();
};
_.extend(ComputedFields.prototype, {
initialize: function () {
_.bindAll(
this,
'_bindModelEvents',
'_computeFieldValue',
'_dependentFields',
'_isModelInitialized',
'_lookUpComputedFields',
'_thenComputedChanges',
'_thenDependentChanges',
'_toJSON',
'_wrapJSON',
'initialize'
);
this._lookUpComputedFields();
this._bindModelEvents();
this._wrapJSON();
},
_lookUpComputedFields: function () {
for (var obj in this.model.computed) {
var field = this.model.computed[obj];
if (field && (field.set || field.get)) {
this._computedFields.push({name: obj, field: field});
}
}
},
_bindModelEvents: function () {
_.each(this._computedFields, function (computedField) {
var fieldName = computedField.name;
var field = computedField.field;
var updateComputed = _.bind(function () {
var value = this._computeFieldValue(field);
this.model.set(fieldName, value, { skipChangeEvent: true });
}, this);
var updateDependent = _.bind(function (model, value, options) {
if (options && options.skipChangeEvent) {
return;
}
if (field.set) {
var fields = this._dependentFields(field.depends);
value = value || this.model.get(fieldName);
field.set.call(this.model, value, fields);
this.model.set(fields, options);
}
}, this);
this._thenDependentChanges(field.depends, updateComputed);
this._thenComputedChanges(fieldName, updateDependent);
if (this._isModelInitialized()) {
updateComputed();
}
}, this);
},
_isModelInitialized: function () {
return !_.isEmpty(this.model.attributes);
},
_thenDependentChanges: function (depends, callback) {
_.each(depends, function (name) {
if (typeof (name) === 'string') {
this.model.on('change:' + name, callback);
}
if (typeof (name) === 'function') {
name.call(this.model, callback);
}
}, this);
},
_thenComputedChanges: function (fieldName, callback) {
this.model.on('change:' + fieldName, callback);
},
_wrapJSON: function () {
this.model.toJSON = _.wrap(this.model.toJSON, this._toJSON);
},
_toJSON: function (toJSON) {
var args = Array.prototype.slice.call(arguments, 1),
attributes = toJSON.apply(this.model, args),
strip = !!(args[0] || {}).computedFields;
var stripped = strip ? {} : _.reduce(this._computedFields, function (memo, computed) {
if (computed.field.toJSON === false) {
memo.push(computed.name);
}
return memo;
},[]);
return _.omit(attributes, stripped);
},
_computeFieldValue: function (computedField) {
if (computedField && computedField.get) {
var fields = this._dependentFields(computedField.depends);
return computedField.get.call(this.model, fields);
}
},
_dependentFields: function (depends) {
return _.reduce(depends, function (memo, field) {
memo[field] = this.model.get(field);
return memo;
}, {}, this);
}
});
return ComputedFields;
})(Backbone, _);

View File

@ -0,0 +1 @@
Backbone.ComputedFields=function(a,b){var c=function(a){this.model=a,this._computedFields=[],this.initialize()};return b.extend(c.prototype,{initialize:function(){b.bindAll(this,"_bindModelEvents","_computeFieldValue","_dependentFields","_isModelInitialized","_lookUpComputedFields","_thenComputedChanges","_thenDependentChanges","_toJSON","_wrapJSON","initialize"),this._lookUpComputedFields(),this._bindModelEvents(),this._wrapJSON()},_lookUpComputedFields:function(){for(var a in this.model.computed){var b=this.model.computed[a];b&&(b.set||b.get)&&this._computedFields.push({name:a,field:b})}},_bindModelEvents:function(){b.each(this._computedFields,function(a){var c=a.name,d=a.field,e=b.bind(function(){var a=this._computeFieldValue(d);this.model.set(c,a,{skipChangeEvent:!0})},this),f=b.bind(function(a,b,e){if((!e||!e.skipChangeEvent)&&d.set){var f=this._dependentFields(d.depends);b=b||this.model.get(c),d.set.call(this.model,b,f),this.model.set(f,e)}},this);this._thenDependentChanges(d.depends,e),this._thenComputedChanges(c,f),this._isModelInitialized()&&e()},this)},_isModelInitialized:function(){return!b.isEmpty(this.model.attributes)},_thenDependentChanges:function(a,c){b.each(a,function(a){"string"==typeof a&&this.model.on("change:"+a,c),"function"==typeof a&&a.call(this.model,c)},this)},_thenComputedChanges:function(a,b){this.model.on("change:"+a,b)},_wrapJSON:function(){this.model.toJSON=b.wrap(this.model.toJSON,this._toJSON)},_toJSON:function(a){var c=Array.prototype.slice.call(arguments,1),d=a.apply(this.model,c),e=!!(c[0]||{}).computedFields,f=e?{}:b.reduce(this._computedFields,function(a,b){return b.field.toJSON===!1&&a.push(b.name),a},[]);return b.omit(d,f)},_computeFieldValue:function(a){if(a&&a.get){var b=this._dependentFields(a.depends);return a.get.call(this.model,b)}},_dependentFields:function(a){return b.reduce(a,function(a,b){return a[b]=this.model.get(b),a},{},this)}}),c}(Backbone,_);

View File

@ -0,0 +1,75 @@
/*jslint regexp: true */
/*global define */
define(['module', 'text'], function (module, textPlugin) {
'use strict';
var svg = {
buildMap: {},
sprite_id: 'requirejs-svg-sprite',
sprite: null,
extractGraphicAsSymbol: function(document, svgText) {
var div = document.createElement('div');
div.innerHTML = svgText;
var element = div.querySelector('svg');
var id = element.getAttribute('id');
var viewBox = element.getAttribute('viewbox') || element.getAttribute('viewBox');
return svg.createSymbol(document, id, element, viewBox);
},
createSymbol: function(document, id, element, viewBox) {
var symbol = document.createElementNS('http://www.w3.org/2000/svg', 'symbol');
while (element.firstChild) {
symbol.appendChild(element.firstChild);
}
typeof id === 'string' && symbol.setAttribute('id', id);
typeof viewBox === 'string' && symbol.setAttribute('viewBox', viewBox);
return symbol;
},
createSprite: function(document) {
svg.sprite = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.sprite.setAttribute('style', 'display: none');
svg.sprite.setAttribute('id', this.sprite_id);
return svg.sprite;
},
appendSprite: function(content) {
if (! svg.sprite) {
svg.createSprite(document);
document.body.appendChild(svg.sprite);
}
svg.sprite.appendChild( svg.extractGraphicAsSymbol(document, content) );
},
finishLoad: function(name, onLoad, config) {
return function(content) {
if (config && !config.isBuild) {
svg.appendSprite(content);
}
else {
svg.buildMap[name] = content;
}
return onLoad(content);
};
},
load: function(name, req, onLoad, config) {
textPlugin.load(name + '!strip', req, svg.finishLoad(name, onLoad, config), config);
},
write: function (pluginName, moduleName, write/*, config*/) {
if (svg.buildMap.hasOwnProperty(moduleName)) {
var content = textPlugin.jsEscape(svg.buildMap[moduleName]);
write.asModule(pluginName + "!" + moduleName,
"define(['" + pluginName + "'], function (svg) { svg.appendSprite('" + content + "');});\n"
);
}
}
};
return svg;
});

View File

@ -0,0 +1,3 @@
define(["module","text"],function(g,f){var b={b:{},j:"requirejs-svg-sprite",a:null,h:function(a,e){var c=a.createElement("div");c.innerHTML=e;var c=c.querySelector("svg"),d=c.getAttribute("id"),f=c.getAttribute("viewbox")||c.getAttribute("viewBox");return b.g(a,d,c,f)},g:function(a,b,c,d){for(a=a.createElementNS("http://www.w3.org/2000/svg","symbol");c.firstChild;)a.appendChild(c.firstChild);"string"===typeof b&&a.setAttribute("id",b);"string"===typeof d&&a.setAttribute("viewBox",d);return a},f:function(a){b.a=
a.createElementNS("http://www.w3.org/2000/svg","svg");b.a.setAttribute("style","display: none");b.a.setAttribute("id",this.j);return b.a},c:function(a){b.a||(b.f(document),document.body.appendChild(b.a));b.a.appendChild(b.h(document,a))},i:function(a,e,c){return function(d){c&&!c.m?b.c(d):b.b[a]=d;return e(d)}},load:function(a,e,c,d){f.load(a+"!strip",e,b.i(a,c,d),d)},write:function(a,e,c){if(b.b.hasOwnProperty(e)){var d=f.o(b.b[e]);c.l(a+"!"+e,"define(['"+a+"'], function (svg) { svg.appendSprite('"+
d+"');});\n")}}};return b});

View File

@ -0,0 +1,484 @@
.select2-container {
box-sizing: border-box;
display: inline-block;
margin: 0;
position: relative;
vertical-align: middle; }
.select2-container .select2-selection--single {
box-sizing: border-box;
cursor: pointer;
display: block;
height: 28px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--single .select2-selection__rendered {
display: block;
padding-left: 8px;
padding-right: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-selection--single .select2-selection__clear {
position: relative; }
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
padding-right: 8px;
padding-left: 20px; }
.select2-container .select2-selection--multiple {
box-sizing: border-box;
cursor: pointer;
display: block;
min-height: 32px;
user-select: none;
-webkit-user-select: none; }
.select2-container .select2-selection--multiple .select2-selection__rendered {
display: inline-block;
overflow: hidden;
padding-left: 8px;
text-overflow: ellipsis;
white-space: nowrap; }
.select2-container .select2-search--inline {
float: left; }
.select2-container .select2-search--inline .select2-search__field {
box-sizing: border-box;
border: none;
font-size: 100%;
margin-top: 5px;
padding: 0; }
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-dropdown {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
box-sizing: border-box;
display: block;
position: absolute;
left: -100000px;
width: 100%;
z-index: 1051; }
.select2-results {
display: block; }
.select2-results__options {
list-style: none;
margin: 0;
padding: 0; }
.select2-results__option {
padding: 6px;
user-select: none;
-webkit-user-select: none; }
.select2-results__option[aria-selected] {
cursor: pointer; }
.select2-container--open .select2-dropdown {
left: 0; }
.select2-container--open .select2-dropdown--above {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--open .select2-dropdown--below {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-search--dropdown {
display: block;
padding: 4px; }
.select2-search--dropdown .select2-search__field {
padding: 4px;
width: 100%;
box-sizing: border-box; }
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
-webkit-appearance: none; }
.select2-search--dropdown.select2-search--hide {
display: none; }
.select2-close-mask {
border: 0;
margin: 0;
padding: 0;
display: block;
position: fixed;
left: 0;
top: 0;
min-height: 100%;
min-width: 100%;
height: auto;
width: auto;
opacity: 0;
z-index: 99;
background-color: #fff;
filter: alpha(opacity=0); }
.select2-hidden-accessible {
border: 0 !important;
clip: rect(0 0 0 0) !important;
height: 1px !important;
margin: -1px !important;
overflow: hidden !important;
padding: 0 !important;
position: absolute !important;
width: 1px !important; }
.select2-container--default .select2-selection--single {
background-color: #fff;
border: 1px solid #aaa;
border-radius: 4px; }
.select2-container--default .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--default .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold; }
.select2-container--default .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--default .select2-selection--single .select2-selection__arrow {
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px; }
.select2-container--default .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
left: 1px;
right: auto; }
.select2-container--default.select2-container--disabled .select2-selection--single {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
display: none; }
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--default .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
box-sizing: border-box;
list-style: none;
margin: 0;
padding: 0 5px;
width: 100%; }
.select2-container--default .select2-selection--multiple .select2-selection__rendered li {
list-style: none; }
.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
color: #999;
margin-top: 5px;
float: left; }
.select2-container--default .select2-selection--multiple .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-top: 5px;
margin-right: 10px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #999;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #333; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
float: right; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: solid black 1px;
outline: 0; }
.select2-container--default.select2-container--disabled .select2-selection--multiple {
background-color: #eee;
cursor: default; }
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
display: none; }
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--default .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa; }
.select2-container--default .select2-search--inline .select2-search__field {
background: transparent;
border: none;
outline: 0;
box-shadow: none;
-webkit-appearance: textfield; }
.select2-container--default .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--default .select2-results__option[role=group] {
padding: 0; }
.select2-container--default .select2-results__option[aria-disabled=true] {
color: #999; }
.select2-container--default .select2-results__option[aria-selected=true] {
background-color: #ddd; }
.select2-container--default .select2-results__option .select2-results__option {
padding-left: 1em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
padding-left: 0; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
margin-left: -1em;
padding-left: 2em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -2em;
padding-left: 3em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -3em;
padding-left: 4em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -4em;
padding-left: 5em; }
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
margin-left: -5em;
padding-left: 6em; }
.select2-container--default .select2-results__option--highlighted[aria-selected] {
background-color: #5897fb;
color: white; }
.select2-container--default .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic .select2-selection--single {
background-color: #f7f7f7;
border: 1px solid #aaa;
border-radius: 4px;
outline: 0;
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic .select2-selection--single:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--single .select2-selection__rendered {
color: #444;
line-height: 28px; }
.select2-container--classic .select2-selection--single .select2-selection__clear {
cursor: pointer;
float: right;
font-weight: bold;
margin-right: 10px; }
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
color: #999; }
.select2-container--classic .select2-selection--single .select2-selection__arrow {
background-color: #ddd;
border: none;
border-left: 1px solid #aaa;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
height: 26px;
position: absolute;
top: 1px;
right: 1px;
width: 20px;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
border-color: #888 transparent transparent transparent;
border-style: solid;
border-width: 5px 4px 0 4px;
height: 0;
left: 50%;
margin-left: -4px;
margin-top: -2px;
position: absolute;
top: 50%;
width: 0; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
float: left; }
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
border: none;
border-right: 1px solid #aaa;
border-radius: 0;
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
left: 1px;
right: auto; }
.select2-container--classic.select2-container--open .select2-selection--single {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
background: transparent;
border: none; }
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
border-color: transparent transparent #888 transparent;
border-width: 0 4px 5px 4px; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
.select2-container--classic .select2-selection--multiple {
background-color: white;
border: 1px solid #aaa;
border-radius: 4px;
cursor: text;
outline: 0; }
.select2-container--classic .select2-selection--multiple:focus {
border: 1px solid #5897fb; }
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
list-style: none;
margin: 0;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
display: none; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
background-color: #e4e4e4;
border: 1px solid #aaa;
border-radius: 4px;
cursor: default;
float: left;
margin-right: 5px;
margin-top: 5px;
padding: 0 5px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
color: #888;
cursor: pointer;
display: inline-block;
font-weight: bold;
margin-right: 2px; }
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
color: #555; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
float: right; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
margin-left: 5px;
margin-right: auto; }
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
margin-left: 2px;
margin-right: auto; }
.select2-container--classic.select2-container--open .select2-selection--multiple {
border: 1px solid #5897fb; }
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0; }
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0; }
.select2-container--classic .select2-search--dropdown .select2-search__field {
border: 1px solid #aaa;
outline: 0; }
.select2-container--classic .select2-search--inline .select2-search__field {
outline: 0;
box-shadow: none; }
.select2-container--classic .select2-dropdown {
background-color: white;
border: 1px solid transparent; }
.select2-container--classic .select2-dropdown--above {
border-bottom: none; }
.select2-container--classic .select2-dropdown--below {
border-top: none; }
.select2-container--classic .select2-results > .select2-results__options {
max-height: 200px;
overflow-y: auto; }
.select2-container--classic .select2-results__option[role=group] {
padding: 0; }
.select2-container--classic .select2-results__option[aria-disabled=true] {
color: grey; }
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
background-color: #3875d7;
color: white; }
.select2-container--classic .select2-results__group {
cursor: default;
display: block;
padding: 6px; }
.select2-container--classic.select2-container--open .select2-dropdown {
border-color: #5897fb; }

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="الرجاء حذف "+t+" عناصر";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="الرجاء إضافة "+t+" عناصر";return n},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){var t="تستطيع إختيار "+e.maximum+" بنود فقط";return t},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Angiv venligst "+t+" tegn mindre";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Angiv venligst "+t+" tegn mere";return n},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/de",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Bitte "+t+" Zeichen weniger eingeben"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Bitte "+t+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:function(e){var t="Sie können nur "+e.maximum+" Eintr";return e.maximum===1?t+="ag":t+="äge",t+=" auswählen",t},noResults:function(){return"Keine Übereinstimmungen gefunden"},searching:function(){return"Suche…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/el",[],function(){return{errorLoading:function(){return"Τα αποτελέσματα δεν μπόρεσαν να φορτώσουν."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Παρακαλώ διαγράψτε "+t+" χαρακτήρ";return t==1&&(n+="α"),t!=1&&(n+="ες"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Παρακαλώ συμπληρώστε "+t+" ή περισσότερους χαρακτήρες";return n},loadingMore:function(){return"Φόρτωση περισσότερων αποτελεσμάτων…"},maximumSelected:function(e){var t="Μπορείτε να επιλέξετε μόνο "+e.maximum+" επιλογ";return e.maximum==1&&(t+="ή"),e.maximum!=1&&(t+="ές"),t},noResults:function(){return"Δεν βρέθηκαν αποτελέσματα"},searching:function(){return"Αναζήτηση…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Please enter "+t+" or more characters";return n},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"La carga falló"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor, elimine "+t+" car";return t==1?n+="ácter":n+="acteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Por favor, introduzca "+t+" car";return t==1?n+="ácter":n+="acteres",n},loadingMore:function(){return"Cargando más resultados…"},maximumSelected:function(e){var t="Sólo puede seleccionar "+e.maximum+" elemento";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No se encontraron resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" vähem",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" rohkem",n},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var t="Saad vaid "+e.maximum+" tulemus";return e.maximum==1?t+="e":t+="t",t+=" valida",t},noResults:function(){return"Tulemused puuduvad"},searching:function(){return"Otsin…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gutxiago",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gehiago",n},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:function(e){return e.maximum===1?"Elementu bakarra hauta dezakezu":e.maximum+" elementu hauta ditzakezu soilik"},noResults:function(){return"Ez da bat datorrenik aurkitu"},searching:function(){return"Bilatzen…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="لطفاً "+t+" کاراکتر را حذف نمایید";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="لطفاً تعداد "+t+" کاراکتر یا بیشتر وارد نمایید";return n},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelected:function(e){var t="شما تنها می‌توانید "+e.maximum+" آیتم را انتخاب نمایید";return t},noResults:function(){return"هیچ نتیجه‌ای یافت نشد"},searching:function(){return"در حال جستجو..."}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Ole hyvä ja anna "+t+" merkkiä vähemmän"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Ole hyvä ja anna "+t+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(e){return"Voit valita ainoastaan "+e.maximum+" kpl"},noResults:function(){return"Ei tuloksia"},searching:function(){}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/gl",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Elimine ";return t===1?n+="un carácter":n+=t+" caracteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Engada ";return t===1?n+="un carácter":n+=t+" caracteres",n},loadingMore:function(){return"Cargando máis resultados…"},maximumSelected:function(e){var t="Só pode ";return e.maximum===1?t+="un elemento":t+=e.maximum+" elementos",t},noResults:function(){return"Non se atoparon resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/he",[],function(){return{errorLoading:function(){return"שגיאה בטעינת התוצאות"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="נא למחוק ";return t===1?n+="תו אחד":n+=t+" תווים",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="נא להכניס ";return t===1?n+="תו אחד":n+=t+" תווים",n+=" או יותר",n},loadingMore:function(){return"טוען תוצאות נוספות…"},maximumSelected:function(e){var t="באפשרותך לבחור עד ";return e.maximum===1?t+="פריט אחד":t+=e.maximum+" פריטים",t},noResults:function(){return"לא נמצאו תוצאות"},searching:function(){return"מחפש…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" अक्षर को हटा दें";return t>1&&(n=t+" अक्षरों को हटा दें "),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="कृपया "+t+" या अधिक अक्षर दर्ज करें";return n},loadingMore:function(){return"अधिक परिणाम लोड हो रहे है..."},maximumSelected:function(e){var t="आप केवल "+e.maximum+" आइटम का चयन कर सकते हैं";return t},noResults:function(){return"कोई परिणाम नहीं मिला"},searching:function(){return"खोज रहा है..."}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hr",[],function(){function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100>19)?e%10>1&&(t+="a"):t+="ova",t}return{errorLoading:function(){return"Preuzimanje nije uspjelo."},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Unesite "+e(n)},inputTooShort:function(t){var n=t.minimum-t.input.length;return"Unesite još "+e(n)},loadingMore:function(){return"Učitavanje rezultata…"},maximumSelected:function(e){return"Maksimalan broj odabranih stavki je "+e.maximum},noResults:function(){return"Nema rezultata"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Túl hosszú. "+t+" karakterrel több, mint kellene."},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Túl rövid. Még "+t+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:function(e){return"Csak "+e.maximum+" elemet lehet kiválasztani."},noResults:function(){return"Nincs találat."},searching:function(){return"Keresés…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/id",[],function(){return{errorLoading:function(){return"Data tidak boleh diambil."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Hapuskan "+t+" huruf"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Masukkan "+t+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(e){return"Anda hanya dapat memilih "+e.maximum+" pilihan"},noResults:function(){return"Tidak ada data yang sesuai"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/is",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vinsamlegast styttið texta um "+t+" staf";return t<=1?n:n+"i"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vinsamlegast skrifið "+t+" staf";return t>1&&(n+="i"),n+=" í viðbót",n},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(e){return"Þú getur aðeins valið "+e.maximum+" atriði"},noResults:function(){return"Ekkert fannst"},searching:function(){return"Leita…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Per favore cancella "+t+" caratter";return t!==1?n+="i":n+="e",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Per favore inserisci "+t+" o più caratteri";return n},loadingMore:function(){return"Caricando più risultati…"},maximumSelected:function(e){var t="Puoi selezionare solo "+e.maximum+" element";return e.maximum!==1?t+="i":t+="o",t},noResults:function(){return"Nessun risultato trovato"},searching:function(){return"Sto cercando…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ja",[],function(){return{errorLoading:function(){return"結果が読み込まれませんでした"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" 文字を削除してください";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="少なくとも "+t+" 文字を入力してください";return n},loadingMore:function(){return"読み込み中…"},maximumSelected:function(e){var t=e.maximum+" 件しか選択できません";return t},noResults:function(){return"対象が見つかりません"},searching:function(){return"検索しています…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/km",[],function(){return{errorLoading:function(){return"មិនអាចទាញយកទិន្នន័យ"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="សូមលុបចេញ "+t+" អក្សរ";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="សូមបញ្ចូល"+t+" អក្សរ រឺ ច្រើនជាងនេះ";return n},loadingMore:function(){return"កំពុងទាញយកទិន្នន័យបន្ថែម..."},maximumSelected:function(e){var t="អ្នកអាចជ្រើសរើសបានតែ "+e.maximum+" ជម្រើសប៉ុណ្ណោះ";return t},noResults:function(){return"មិនមានលទ្ធផល"},searching:function(){return"កំពុងស្វែងរក..."}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="너무 깁니다. "+t+" 글자 지워주세요.";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="너무 짧습니다. "+t+" 글자 더 입력해주세요.";return n},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(e){var t="최대 "+e.maximum+"개까지만 선택 가능합니다.";return t},noResults:function(){return"결과가 없습니다."},searching:function(){return"검색 중…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lt",[],function(){function e(e,t,n,r){return e%10===1&&(e%100<11||e%100>19)?t:e%10>=2&&e%10<=9&&(e%100<11||e%100>19)?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Pašalinkite "+n+" simbol";return r+=e(n,"į","ius","ių"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Įrašykite dar "+n+" simbol";return r+=e(n,"į","ius","ių"),r},loadingMore:function(){return"Kraunama daugiau rezultatų…"},maximumSelected:function(t){var n="Jūs galite pasirinkti tik "+t.maximum+" element";return n+=e(t.maximum,"ą","us","ų"),n},noResults:function(){return"Atitikmenų nerasta"},searching:function(){return"Ieškoma…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lv",[],function(){function e(e,t,n,r){return e===11?t:e%10===1?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Lūdzu ievadiet par "+n;return r+=" simbol"+e(n,"iem","u","iem"),r+" mazāk"},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Lūdzu ievadiet vēl "+n;return r+=" simbol"+e(n,"us","u","us"),r},loadingMore:function(){return"Datu ielāde…"},maximumSelected:function(t){var n="Jūs varat izvēlēties ne vairāk kā "+t.maximum;return n+=" element"+e(t.maximum,"us","u","us"),n},noResults:function(){return"Sakritību nav"},searching:function(){return"Meklēšana…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/mk",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Ве молиме внесете "+e.maximum+" помалку карактер";return e.maximum!==1&&(n+="и"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Ве молиме внесете уште "+e.maximum+" карактер";return e.maximum!==1&&(n+="и"),n},loadingMore:function(){return"Вчитување резултати…"},maximumSelected:function(e){var t="Можете да изберете само "+e.maximum+" ставк";return e.maximum===1?t+="а":t+="и",t},noResults:function(){return"Нема пронајдено совпаѓања"},searching:function(){return"Пребарување…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ms",[],function(){return{errorLoading:function(){return"Keputusan tidak berjaya dimuatkan."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Sila hapuskan "+t+" aksara"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Sila masukkan "+t+" atau lebih aksara"},loadingMore:function(){return"Sedang memuatkan keputusan…"},maximumSelected:function(e){return"Anda hanya boleh memilih "+e.maximum+" pilihan"},noResults:function(){return"Tiada padanan yang ditemui"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nb",[],function(){return{errorLoading:function(){return"Kunne ikke hente resultater."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Vennligst fjern "+t+" tegn"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vennligst skriv inn ";return t>1?n+=" flere tegn":n+=" tegn til",n},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan velge maks "+e.maximum+" elementer"},noResults:function(){return"Ingen treff"},searching:function(){return"Søker…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Gelieve "+t+" karakters te verwijderen";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Gelieve "+t+" of meer karakters in te voeren";return n},loadingMore:function(){return"Meer resultaten laden…"},maximumSelected:function(e){var t=e.maximum==1?"kan":"kunnen",n="Er "+t+" maar "+e.maximum+" item";return e.maximum!=1&&(n+="s"),n+=" worden geselecteerd",n},noResults:function(){return"Geen resultaten gevonden…"},searching:function(){return"Zoeken…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pl",[],function(){var e=["znak","znaki","znaków"],t=["element","elementy","elementów"],n=function(t,n){if(t===1)return n[0];if(t>1&&t<=4)return n[1];if(t>=5)return n[2]};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(t){var r=t.input.length-t.maximum;return"Usuń "+r+" "+n(r,e)},inputTooShort:function(t){var r=t.minimum-t.input.length;return"Podaj przynajmniej "+r+" "+n(r,e)},loadingMore:function(){return"Trwa ładowanie…"},maximumSelected:function(e){return"Możesz zaznaczyć tylko "+e.maximum+" "+n(e.maximum,t)},noResults:function(){return"Brak wyników"},searching:function(){return"Trwa wyszukiwanie…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Apague "+t+" caracter";return t!=1&&(n+="es"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Digite "+t+" ou mais caracteres";return n},loadingMore:function(){return"Carregando mais resultados…"},maximumSelected:function(e){var t="Você só pode selecionar "+e.maximum+" ite";return e.maximum==1?t+="m":t+="ns",t},noResults:function(){return"Nenhum resultado encontrado"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor apague "+t+" ";return n+=t!=1?"caracteres":"carácter",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Introduza "+t+" ou mais caracteres";return n},loadingMore:function(){return"A carregar mais resultados…"},maximumSelected:function(e){var t="Apenas pode seleccionar "+e.maximum+" ";return t+=e.maximum!=1?"itens":"item",t},noResults:function(){return"Sem resultados"},searching:function(){return"A procurar…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ro",[],function(){return{errorLoading:function(){return"Rezultatele nu au putut fi incărcate."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vă rugăm să ștergeți"+t+" caracter";return t!==1&&(n+="e"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vă rugăm să introduceți "+t+"sau mai multe caractere";return n},loadingMore:function(){return"Se încarcă mai multe rezultate…"},maximumSelected:function(e){var t="Aveți voie să selectați cel mult "+e.maximum;return t+=" element",e.maximum!==1&&(t+="e"),t},noResults:function(){return"Nu au fost găsite rezultate"},searching:function(){return"Căutare…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ru",[],function(){function e(e,t,n,r){return e%10<5&&e%10>0&&e%100<5||e%100>20?e%10>1?n:t:r}return{errorLoading:function(){return"Невозможно загрузить результаты"},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Пожалуйста, введите на "+n+" символ";return r+=e(n,"","a","ов"),r+=" меньше",r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Пожалуйста, введите еще хотя бы "+n+" символ";return r+=e(n,"","a","ов"),r},loadingMore:function(){return"Загрузка данных…"},maximumSelected:function(t){var n="Вы можете выбрать не более "+t.maximum+" элемент";return n+=e(t.maximum,"","a","ов"),n},noResults:function(){return"Совпадений не найдено"},searching:function(){return"Поиск…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sk",[],function(){var e={2:function(e){return e?"dva":"dve"},3:function(){return"tri"},4:function(){return"štyri"}};return{inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadajte o jeden znak menej":n>=2&&n<=4?"Prosím, zadajte o "+e[n](!0)+" znaky menej":"Prosím, zadajte o "+n+" znakov menej"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadajte ešte jeden znak":n<=4?"Prosím, zadajte ešte ďalšie "+e[n](!0)+" znaky":"Prosím, zadajte ešte ďalších "+n+" znakov"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(t){return t.maximum==1?"Môžete zvoliť len jednu položku":t.maximum>=2&&t.maximum<=4?"Môžete zvoliť najviac "+e[t.maximum](!1)+" položky":"Môžete zvoliť najviac "+t.maximum+" položiek"},noResults:function(){return"Nenašli sa žiadne položky"},searching:function(){return"Vyhľadávanie…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr-Cyrl",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Преузимање није успело."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Обришите "+n+" симбол";return r+=e(n,"","а","а"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Укуцајте бар још "+n+" симбол";return r+=e(n,"","а","а"),r},loadingMore:function(){return"Преузимање још резултата…"},maximumSelected:function(t){var n="Можете изабрати само "+t.maximum+" ставк";return n+=e(t.maximum,"у","е","и"),n},noResults:function(){return"Ништа није пронађено"},searching:function(){return"Претрага…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vänligen sudda ut "+t+" tecken";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vänligen skriv in "+t+" eller fler tecken";return n},loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(e){var t="Du kan max välja "+e.maximum+" element";return t},noResults:function(){return"Inga träffar"},searching:function(){return"Söker…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/th",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="โปรดลบออก "+t+" ตัวอักษร";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="โปรดพิมพ์เพิ่มอีก "+t+" ตัวอักษร";return n},loadingMore:function(){return"กำลังค้นข้อมูลเพิ่ม…"},maximumSelected:function(e){var t="คุณสามารถเลือกได้ไม่เกิน "+e.maximum+" รายการ";return t},noResults:function(){return"ไม่พบข้อมูล"},searching:function(){return"กำลังค้นข้อมูล…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/tr",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" karakter daha girmelisiniz";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="En az "+t+" karakter daha girmelisiniz";return n},loadingMore:function(){return"Daha fazla…"},maximumSelected:function(e){var t="Sadece "+e.maximum+" seçim yapabilirsiniz";return t},noResults:function(){return"Sonuç bulunamadı"},searching:function(){return"Aranıyor…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/uk",[],function(){function e(e,t,n,r){return e%100>10&&e%100<15?r:e%10===1?t:e%10>1&&e%10<5?n:r}return{errorLoading:function(){return"Неможливо завантажити результати"},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Будь ласка, видаліть "+n+" "+e(t.maximum,"літеру","літери","літер")},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Будь ласка, введіть "+t+" або більше літер"},loadingMore:function(){return"Завантаження інших результатів…"},maximumSelected:function(t){return"Ви можете вибрати лише "+t.maximum+" "+e(t.maximum,"пункт","пункти","пунктів")},noResults:function(){return"Нічого не знайдено"},searching:function(){return"Пошук…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/vi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vui lòng nhập ít hơn "+t+" ký tự";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vui lòng nhập nhiều hơn "+t+' ký tự"';return n},loadingMore:function(){return"Đang lấy thêm kết quả…"},maximumSelected:function(e){var t="Chỉ có thể chọn được "+e.maximum+" lựa chọn";return t},noResults:function(){return"Không tìm thấy kết quả"},searching:function(){return"Đang tìm…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="请删除"+t+"个字符";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="请再输入至少"+t+"个字符";return n},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(e){var t="最多只能选择"+e.maximum+"个项目";return t},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"}}}),{define:e.define,require:e.require}})();

View File

@ -0,0 +1,3 @@
/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="請刪掉"+t+"個字元";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="請再輸入"+t+"個字元";return n},loadingMore:function(){return"載入中…"},maximumSelected:function(e){var t="你只能選擇最多"+e.maximum+"項";return t},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"}}}),{define:e.define,require:e.require}})();

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,217 @@
/* Write your custom CSS here */
/*no pad style*/
.no-padding {
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
padding-bottom: 0px;
border: 0px;
-webkit-box-shadow: 0;
box-shadow: 0;
}
/* status related color row */
table .basket_status_price_ready {
color: #eb3c6a;
}
table .basket_status_priced {
color: #2bab2b;
}
table .basket_status_delivered {
color: #2bab2b;
}
table .basket_status_canceled {
color: #ababab;
}
table .basket_status_refund_after_adjust {
color: #ababab;
}
table .basket_status_refund_before_adjust {
color: #ababab;
}
table .upload_status_initial {
color: #eb3c6a;
}
table .upload_status_handled {
color: #2bab2b;
}
table .upload_status_discarded {
color: #ababab;
}
/* label related style */
.basket-item-grid table .net_price {
white-space: nowrap;
background-color: #eb3c6a;
color: #fff;
}
.basket-item-grid table .rest_area {
text-align: right;
}
.basket-item-grid table .rest_area dl {
display: inline-block;
min-width: 100px;
padding-right: 15px;
}
.basket-item-grid table dt {
text-align: center;
}
.basket-item-grid table dd {
text-align: right;
font-size: 15pt;
}
.basket-item-grid table .net_price a {
color: #fff;
text-decoration: none;
}
.basket-confirm-bar-widget .btn-pricing,
.basket-confirm-bar-widget .btn-delivering {
background-color: #eb3c6a;
color: #fff;
}
.upload-confirm-bar-widget .btn-handle {
background-color: #eb3c6a;
color: #fff;
}
.header-status-progress .nav-item.active .nav-link {
color: #eb3c6a;
background: none;
font-weight: 600;
}
.company-description .important-info * {
font-size: 27px;
}
.positive * {
color: #2bab2b;
}
.negative * {
color: #eb3c6a;
}
.card-positive {
background: #2bab2b;
}
.card-positive .card-header {
color: #fff;
}
.card-positive .card-block {
color: #fff;
}
.card-positive .card-header .card-control a {
color: #fff;
}
.card-positive .card-header .card-control a:hover {
color: #f1f1f1;
}
.tx-registration-bar .btn-registration {
background-color: #eb3c6a;
color: #fff;
}
.image-cell .preview {
display: none;
position: absolute;
border: 1px solid #000;
width: 400px;
height: 400px;
}
/*justified-gallery figure css definition*/
.justified-gallery > figure {
position: absolute;
display: inline-block;
overflow: hidden;
/* background: #888888; To have gray placeholders while the gallery is loading with waitThumbnailsLoad = false */
filter: "alpha(opacity=10)";
opacity: 0.1;
}
.justified-gallery > figure > img,
.justified-gallery > figure > a > img {
position: absolute;
top: 50%;
left: 50%;
margin: 0;
padding: 0;
border: none;
filter: "alpha(opacity=0)";
opacity: 0;
}
.justified-gallery > figure > .caption {
display: none;
position: absolute;
bottom: 0;
padding: 5px;
background-color: #000000;
left: 0;
right: 0;
margin: 0;
color: white;
font-size: 12px;
font-weight: 300;
font-family: sans-serif;
}
.justified-gallery > figure > .caption.caption-visible {
display: initial;
filter: "alpha(opacity=70)";
opacity: 0.7;
-webkit-transition: opacity 500ms ease-in;
-moz-transition: opacity 500ms ease-in;
-o-transition: opacity 500ms ease-in;
transition: opacity 500ms ease-in;
}
/*justified-gallery figure css definition end */
/* PhotoSwipe preview load glitch */
.pswp__img {
height: auto !important;
}
.top-menu .navbar-nav .nav-item + .nav-item {
margin: 0;
}
.top-menu.collapse.show {
background-color: #fff;
}
button.navbar-toggler{
line-height: 60px;
padding: 0 15px 0 15px;
vertical-align: middle;
display: table-cell;
text-align: center;
position: absolute;
right: 0rem;
}
.dropdown-toggle::after {
content: none;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
<div class="modal-dialog">
<div class="modal-content">
<% if (title) { %>
<div class="modal-header">
<% if (allowCancel) { %>
<a class="close">&times;</a>
<% } %>
<h4><%= title %></h4>
</div>
<% } %>
<div class="modal-body"><%= content %></div>
<% if (showFooter) { %>
<div class="modal-footer">
<% if (allowCancel &&cancelText) { %>
<a href="javascript:void(0);" class="btn cancel btn-default"><%= cancelText %></a>
<% } %>
<a href="javascript:void(0);" class="btn ok btn-success"><%= okText %></a>
</div>
<% } %>
</div>
</div>

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<!-- Title -->
<title>In-house service</title>
<meta http-equiv="Content-type" content="text/html">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, width=device-width">
<meta name="description" content="we have been able to make amazing things.">
<meta name="author" content="Sangbum Kim">
<meta name="keywords">
<!-- IE6-10 -->
<link rel="shortcut icon" href="./static/images/favicon.ico'?v=1">
<!-- Everybody else -->
<link rel="icon" sizes="16x16 24x24 32x32 48x48 64x64"
href="./static/images/favicon.ico?v=1">
<!-- ms tile favicon -->
<meta name="msapplication-TileColor" content="#e74e1c">
<meta name="msapplication-TileImage" content="./static/images/favicon.144.png?v=1">
<!-- apple touch icon -->
<link rel="apple-touch-icon-precomposed" href="./.static/images/favicon.152.png?v=1">
<!-- chrome frame theme -->
<meta name="theme-color" content="#e74e1c">
<!-- ios web app fullscreen -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
<!-- android web app fullscreen -->
<meta name="mobile-web-app-capable" content="yes">
<!-- initial font loading -->
<script>
'use strict';
var WebFontConfig = {
custom: {
families: ['Spoqa Han Sans:100,300,400,700', 'FontAwesome'],
urls: [
'https://cdnjs.cloudflare.com/ajax/libs/spoqa-han-sans/2.1.0/css/SpoqaHanSans-kr.css',
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'
]
},
timeout: 60000,
active: function () {
sessionStorage.fonts = true;
}
};
</script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/webfont/1.6.27/webfontloader.js"></script>
{% block style_area %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
<link rel="stylesheet" href="static/stylesheets/common/modern.css">
<link rel="stylesheet" href="static/stylesheets/common/custom.css">
<script type="text/javascript" src="static/javascripts/common/dep.js"></script>
<script>
'use strict';
var require = dep("static");
</script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.2/require.min.js"></script>
{% endblock %}
</head>
<body class="page-header-fixed page-horizontal-bar">
{% block body_area %}{% endblock %}
{% block script_area %}
<script>
require(['javascripts/common/initial']);
</script>
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,83 @@
{% extends "/layout/base.html" %} {% block body_area %}
<!-- Search Form -->
<main class="page-content content-wrap ">
<nav class="navbar navbar-toggleable-sm bg-faded">
<div class="logo-box">
<a class="logo-text" href="./">
<i class="fa fa-gamepad" aria-hidden="true"></i>
<span>in-house</span></a>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse"
data-target="#gnb" aria-controls="gnb" aria-expanded="false"
aria-label="Toggle navigation">
<i class="fa fa-bars" aria-hidden="true"></i>
</button>
<div class="top-menu collapse navbar-collapse" id="gnb">
<ul class="nav navbar-nav mr-auto navbar-left">
<li class="nav-item active">
<a class="nav-link" href="./" title="홈">
<span>
<i class="menu-icon fa fa-home"></i>
</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./change_password" title="비밀번호 변경">
<span>
<i class="menu-icon fa fa-key"></i>비밀번호 변경
</span>
</a>
</li>
</ul>
<ul class="nav navbar-nav mr-auto navbar-right">
<li class="nav-item">
{% if user_role=="ROLE_ANONYMOUS"%}
<a href="./login"
class="log-out nav-link ">
<span><i class="fa fa-sign-out m-r-xs"></i>Log in</span>
</a>
{% else %}
<a href="./logout"
class="log-in nav-link">
<span><i class="fa fa-sign-in m-r-xs"></i>Log out</span>
</a>
{% endif %}
</li>
</ul>
<!-- Nav -->
</div>
</nav>
<!-- Page Sidebar -->
<div class="page-inner">
<div class="page-title">
<div class="row">
<div class="col-md-5 col-sm-4 col-xs-7">
<h3 class="page-name">{{ title }}</h3>
<div class="page-breadcrumb">
{% block breadcrumb %}{% endblock %}
</div>
</div>
<div class="col-md-7 col-sm-8 col-xs-5">
{% block extra_head_content %}{% endblock %}
</div>
</div>
</div>
<div id="main-wrapper">
{% block content_area %}{% endblock %}
<!-- Row -->
</div>
{% block footer_area %} {% endblock %}
<!-- Main Wrapper -->
<div class="page-footer">
<p class="no-s">2017 &copy; Sangbum Kim.</p>
</div>
</div>
<!-- Page Inner -->
</main>
<!-- Page Content -->
{% endblock %}

View File

@ -0,0 +1,116 @@
{% extends "base.html" %}
{% block script_area %}
{{ block.Super|safe }}
<script type="text/javascript">
require([
'jquery',
'underscore',
'underscore.string',
'toastr',
'javascripts/common/blockui'
], function ($,
_,
_s,
toastr,
blockui) {
"use strict";
$(".submit_change_password").on("click", function (event) {
var form = $(event.target).closest("form");
event.preventDefault();
event.stopPropagation();
var data = _.chain(form)
.result('serializeArray', [])
.map(function (n) {
return [n.name, n.value];
})
.object()
.value();
blockui.blockUI(form);
$.ajax({
type: 'POST',
url: location.pathname,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify(data),
success: function (responseText, statusText) {
toastr['success']('변경완료.');
},
error: function (response) {
var errObj, message;
message = '변경오류';
if (!_.has(response, 'responseJSON')) {
} else {
errObj = _.property('responseJSON')(response);
if (_.has(errObj, 'message')) {
message = message + '<br/>' + errObj.message;
}
}
toastr['error'](message);
},
complete: function () {
form[0].reset();
blockui.unblockUI(form);
}
});
});
});
</script>
{% endblock %}
{% block content_area %}
<div class="row">
<div class="col-md-8 col-md-offset-2 mx-auto change_password_form">
<form onsubmit="javascript:void(0);" class="form-horizontal">
<div class="card card-white">
<div class="card-block">
<div class="form-group">
<label class="col-sm-2 form-control-label">ID</label>
<div class="col-sm-10">
<p class="form-control-static">{{user_id}}</p>
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 form-control-label">이전비밀번호</label>
<div class="col-sm-10">
<input type="password" name="old_password" class="form-control" placeholder="입력해주세요"
required="true">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 form-control-label">비밀번호</label>
<div class="col-sm-10">
<input type="password" name="password" class="form-control" placeholder="입력해주세요"
required="true">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 form-control-label">비밀번호확인</label>
<div class="col-sm-10">
<input type="password" name="password_confirm" class="form-control" placeholder="입력해주세요"
required="true">
</div>
</div>
</div>
<div class="card-block">
<div class="row">
<div class="col-md-12 confirm-bar">
<span class="float-right">
<button type="button" class="submit_change_password btn btn-primary btn-lg">변경</button>
</span>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content_area %}
<div class="row">
<div class="col-md-4 center">
<h1 class="text-xxl text-primary text-center"><i class="{{ icon }}" aria-hidden="true"></i>{{ code }}
</h1>
<div class="details">
<h3>{{ message }}</h3>
<p>오류가 발생하거나 허용되지않은 동작입니다.</p>
<p>뒤로 가시거나
<a href="./">
<i class="fa fa-cogs" aria-hidden="true"></i></a>으로 이동해주세요.</p>
</div>
</div>
</div>
<!-- Row -->
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block content_area %}
<div class="row">
<div class="col-md-4 col-md-offset-4 mx-auto">
<div class="card text-center">
<div class="card-block">
<h4 class="card-title"><i class="fa fa-wrench
" aria-hidden="true" style="font-size:12em"></i></h4>
<p class="card-text">홈홈</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,111 @@
{% extends "base.html" %}
{% block script_area %}
{{ block.Super|safe }}
<script type="text/javascript">
require([
'jquery',
'underscore',
'underscore.string',
'toastr',
'javascripts/common/blockui'
], function ($,
_,
_s,
toastr,
blockui) {
"use strict";
$('.login_form form').on("submit", function (event) {
var form = $(event.target).closest("form");
event.preventDefault();
event.stopPropagation();
var data = _.chain(form)
.result('serializeArray', [])
.map(function (n) {
return [n.name, n.value];
})
.object()
.value();
blockui.blockUI(form);
$.ajax({
type: 'POST',
url: location.pathname,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
data: JSON.stringify(data),
success: function (response, statusText) {
var respObj, message, redirectTo = "./";
message = '로그인 완료!';
if (_.has(response, 'responseJSON')) {
respObj = _.property('responseJSON')(response);
if (_.has(respObj, 'message')) {
message = message + '<br/>' + respObj.message;
}
if (_.has(respObj, 'redirectTo')) {
redirectTo = respObj.message;
}
}
toastr['success'](message);
document.location = redirectTo;
},
error: function (response) {
var errObj, message;
message = '로그인 오류';
if (!_.has(response, 'responseJSON')) {
} else {
errObj = _.property('responseJSON')(response);
if (_.has(errObj, 'message')) {
message = message + '<br/>' + errObj.message;
}
}
toastr['error'](message);
},
complete: function () {
form[0].reset();
blockui.unblockUI(form);
}
});
});
});
</script>
{% endblock %}
{% block content_area %}
<div class="row">
<div class="col-md-8 col-md-offset-2 mx-auto login_form">
<form onsubmit="javascript:void(0);" class="form-horizontal">
<div class="card card-white">
<div class="card-block">
<input type="hidden" name="redirect_to" value="{{redirectTo}}">
<div class="form-group">
<label class="col-sm-2 form-control-label">ID</label>
<div class="col-sm-10">
<input type="text" name="id" class="form-control" placeholder="입력해주세요" required="true">
<p class="help-block"></p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 form-control-label">비밀번호</label>
<div class="col-sm-10">
<input type="password" name="password" class="form-control" placeholder="입력해주세요"
required="true">
</div>
</div>
</div>
<div class="card-block">
<div class="row">
<div class="col-md-12 confirm-bar">
<span class="float-right">
<button type="submit" onclick="javascript:void(0);" class="submit_login btn btn-primary btn-lg">로그인</button>
</span>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

36
bench.py Executable file
View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
import os
import re
import time
def report(c, lines):
rtv = []
for str in lines:
str=re.sub("^[ \t]+",'', str)
if str.find("Latency") == 0 or str.find("Requests/sec") == 0:
token = re.split("[ \t]+", str)
value = re.sub("[^0-9\.]", '', token[1])
if re.match("[0-9\.]+us", token[1]):
rtv.append(float(value)/1000)
elif re.match("[0-9\.]+s", token[1]):
rtv.append(float(value)*1000)
else:
rtv.append(float(value))
print c,"\t", rtv[0],"\t",rtv[1]
def run(cmd):
stdin, stdout, stderr = os.popen3(cmd)
return stdout.readlines()
offset = 5
maxConcurrency = offset * 61
duration = 10
for num in range(1, maxConcurrency/offset):
concurrency = num * offset
thread = 16 if concurrency > 16 else concurrency
cmd = (
'''wrk --timeout 7 -t %d -c %d -d %d http://localhost:8080/api/v1/media/admin/2/thumb'''
% (thread, concurrency, duration))
result =run(cmd)
report(concurrency, result)
time.sleep(5)

28
changer-dev.service Normal file
View File

@ -0,0 +1,28 @@
[Unit]
Description=Inhouse internal user mgnt server
Documentation=https://amuz.es
After=syslog.target network-online.target
Wants=network-online.target
[Service]
Type=notify
Restart=always
RestartSec=15
ExecStart=/usr/bin/changer -C /etc/changer/settings.yml -L /var/logs/changer
WorkingDirectory=/var/logs/changer
#ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=5
# Disable timeout logic and wait until process is stopped
TimeoutStopSec=0
# SIGTERM signal is used to stop Minio
KillSignal=SIGTERM
SendSIGKILL=no
SuccessExitStatus=0
# kill only the docker process, not all processes in the cgroup
KillMode=process
[Install]
WantedBy=default.target

47
enums/user_type.go Normal file
View File

@ -0,0 +1,47 @@
package enums
import (
"errors"
"github.com/mailru/easyjson/jlexer"
"github.com/mailru/easyjson/jwriter"
)
type UserType string
const (
UserTypeAnon UserType = "ROLE_ANONYMOUS"
UserTypeUser UserType = "ROLE_USER"
UserTypeAdmin UserType = "ROLE_ADMIN"
)
func UserTypeNameOf(value string) (UserType, error) {
switch value {
case "ROLE_USER":
return UserTypeUser, nil
case "ROLE_ADMIN":
return UserTypeAdmin, nil
}
return UserTypeAnon, errors.New("unable to find UserType")
}
func (status UserType) Name() string {
return string(status)
}
func (status UserType) String() string {
switch status {
case UserTypeUser:
return "사용자"
case UserTypeAdmin:
return "관리자"
}
return "로그인 안함"
}
func (status UserType) MarshalEasyJSON(w *jwriter.Writer) {
w.String(string(status))
}
func (status *UserType) UnmarshalEasyJSON(w *jlexer.Lexer) {
*status, _ = UserTypeNameOf(w.String())
}

View File

@ -0,0 +1,64 @@
package iface
import (
"strconv"
"time"
)
//turns a new initialized CounterMetric object.
func NewCounterMetric() *CounterMetric {
ca := &CounterMetric{}
ca.Inc = make(chan Tuple)
ca.internalRequestsSum = 0
ca.internalRequests = make(map[string]uint64, 0)
ca.internalRequestCodes = make(map[string]uint64, 0)
return ca
}
// StartTimer will call a forever loop in a goroutine to calculate
// metrics for measurements every d ticks. The parameter of this
// function should normally be 1 * time.Minute, if not it will expose
// unintuive JSON keys (requests_per_minute and
// request_sum_per_minute).
func (ca *CounterMetric) StartTimer(d time.Duration) {
timer := time.Tick(d)
go func() {
for {
select {
case tup := <-ca.Inc:
ca.internalRequestsSum++
ca.internalRequests[tup.Path]++
ca.internalRequestCodes[strconv.FormatInt(int64(tup.Code), 10)]++
case <-timer:
ca.reset()
}
}
}()
}
// GetStats to fulfill aspects.Aspect interface, it returns the data
// that will be served as JSON.
func (ca *CounterMetric) GetStats() interface{} {
return *ca
}
// Name to fulfill aspects.Aspect interface, it will return the name
// of the JSON object that will be served.
func (ca *CounterMetric) Name() string {
return "Counter"
}
// InRoot to fulfill aspects.Aspect interface, it will return where to
// put the JSON object into the monitoring endpoint.
func (ca *CounterMetric) InRoot() bool {
return false
}
func (ca *CounterMetric) reset() {
ca.RequestsSum = ca.internalRequestsSum
ca.Requests = ca.internalRequests
ca.RequestCodes = ca.internalRequestCodes
ca.internalRequestsSum = 0
ca.internalRequests = make(map[string]uint64, ca.RequestsSum)
ca.internalRequestCodes = make(map[string]uint64, len(ca.RequestCodes))
}

View File

@ -0,0 +1,118 @@
package iface
import (
"math"
"sort"
"time"
)
type RequestDurationAggr struct {
lastMinuteRequestTimes []float64
Min float64 `json:"min"`
Max float64 `json:"max"`
Mean float64 `json:"mean"`
Stdev float64 `json:"stdev"`
P90 float64 `json:"p90"`
P95 float64 `json:"p95"`
P99 float64 `json:"p99"`
Timestamp time.Time `json:"timestamp"`
}
// NewRequestDurationAggr returns a new initialized RequestDurationAggr
// object.
func NewRequestDurationAggr() *RequestDurationAggr {
rt := &RequestDurationAggr{}
rt.lastMinuteRequestTimes = make([]float64, 0)
rt.Timestamp = time.Now()
return rt
}
// StartTimer will call a forever loop in a goroutine to calculate
// metrics for measurements every d ticks.
func (rt *RequestDurationAggr) StartTimer(d time.Duration) {
timer := time.Tick(d)
go func() {
for {
<-timer
rt.calculate()
}
}()
}
// GetStats to fulfill Metrics.Metric interface, it returns the data
// that will be served as JSON.
func (rt *RequestDurationAggr) GetStats() interface{} {
return rt
}
// Name to fulfill Metrics.Metric interface, it will return the name
// of the JSON object that will be served.
func (rt *RequestDurationAggr) Name() string {
return "RequestTime"
}
// InRoot to fulfill Metrics.Metric interface, it will return where to
// put the JSON object into the monitoring endpoint.
func (rt *RequestDurationAggr) InRoot() bool {
return false
}
func (rt *RequestDurationAggr) Add(n float64) {
rt.lastMinuteRequestTimes = append(rt.lastMinuteRequestTimes, n)
}
func (rt *RequestDurationAggr) calculate() {
sortedSlice := rt.lastMinuteRequestTimes[:]
rt.lastMinuteRequestTimes = make([]float64, 0)
l := len(sortedSlice)
if l <= 1 {
return
}
sort.Float64s(sortedSlice)
rt.Timestamp = time.Now()
rt.Min = sortedSlice[0]
rt.Max = sortedSlice[l - 1]
rt.Mean = mean(sortedSlice, l)
rt.Stdev = correctedStdev(sortedSlice, rt.Mean, l)
rt.P90 = p90(sortedSlice, l)
rt.P95 = p95(sortedSlice, l)
rt.P99 = p99(sortedSlice, l)
}
func mean(orderedObservations []float64, l int) float64 {
res := 0.0
for i := 0; i < l; i++ {
res += orderedObservations[i]
}
return res / float64(l)
}
func p90(orderedObservations []float64, l int) float64 {
return percentile(orderedObservations, l, 0.9)
}
func p95(orderedObservations []float64, l int) float64 {
return percentile(orderedObservations, l, 0.95)
}
func p99(orderedObservations []float64, l int) float64 {
return percentile(orderedObservations, l, 0.99)
}
// percentile with argument p \in (0,1), l is the length of given orderedObservations
// It does a simple apporximation of an ordered list of observations.
// Formula: sortedSlice[0.95*length(sortedSlice)]
func percentile(orderedObservations []float64, l int, p float64) float64 {
return orderedObservations[int(p * float64(l))]
}
func correctedStdev(observations []float64, mean float64, l int) float64 {
var omega float64
for i := 0; i < l; i++ {
omega += math.Pow(observations[i]-mean, 2)
}
stdev := math.Sqrt(1 / (float64(l) - 1) * omega)
return stdev
}

236
http/iface/rest.go Normal file
View File

@ -0,0 +1,236 @@
//go:generate ${GOPATH}/bin/easyjson $GOFILE
package iface
import (
"bytes"
"time"
"amuz.es/gogs/infra/changer/enums"
)
//easyjson:json
type Login struct {
Id string `form:"id" json:"id" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
RedirectTo string `form:"redirect_to" json:"redirect_to,omitempty"`
}
//easyjson:json
type ChangePassword struct {
OldPassword string `form:"old_password" json:"old_password" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
PasswordConfirm string `form:"password_confirm" json:"password_confirm" binding:"required"`
}
//easyjson:json
type Token struct {
Token string `json:"token"`
Expire string `json:"expire"`
}
//easyjson:json
type LoginResponse struct {
Message string `json:"message"`
RedirectTo string `json:"redirectTo,omitempty"`
}
//easyjson:json
type WrappedResponse struct {
Message string `json:"message"`
Content interface{} `json:"content,omitempty"`
}
//easyjson:json
type NewId struct {
Id interface{} `json:"id" binding:"required"`
}
//easyjson:json
type NameCodePair struct {
Name string `json:"name" binding:"required"`
Code string `json:"code" binding:"required"`
}
//easyjson:json
type KeyValuePair struct {
Key string `json:"key" binding:"required"`
Value string `json:"value" binding:"required"`
}
//easyjson:json
type Tuple struct {
Path string
Code int
}
//easyjson:json
type CounterMetric struct {
Inc chan Tuple `json:"-"`
internalRequestsSum uint64
internalRequests map[string]uint64
internalRequestCodes map[string]uint64
RequestsSum uint64 `json:"request_sum_per_minute"`
Requests map[string]uint64 `json:"requests_per_minute"`
RequestCodes map[string]uint64 `json:"request_codes_per_minute"`
}
//easyjson:json
type DurationMetricReadable struct {
Min string `json:"min"`
Max string `json:"max"`
Mean string `json:"mean"`
Stdev string `json:"stdev"`
P90 string `json:"p90"`
P95 string `json:"p95"`
P99 string `json:"p99"`
Timestamp time.Time `json:"timestamp"`
}
//easyjson:json
type DurationMetric struct {
Min uint64 `json:"min"`
Max uint64 `json:"max"`
Mean uint64 `json:"mean"`
Stdev uint64 `json:"stdev"`
P90 uint64 `json:"p90"`
P95 uint64 `json:"p95"`
P99 uint64 `json:"p99"`
Timestamp time.Time `json:"timestamp"`
}
//easyjson:json
type RequestMetricReadable struct {
Duration DurationMetricReadable `json:"duration"`
Count CounterMetric `json:"count"`
}
//easyjson:json
type RequestMetric struct {
Duration DurationMetric `json:"duration"`
Count CounterMetric `json:"count"`
}
//easyjson:json
type RuntimeMetric struct {
GoVersion string `json:"go_version"`
GoOs string `json:"go_os"`
GoArch string `json:"go_arch"`
CpuNum int `json:"cpu_num"`
GoroutineNum int `json:"goroutine_num"`
Gomaxprocs int `json:"go_maxprocs"`
CgoCallNum int64 `json:"cgo_call_num"`
}
//easyjson:json
type MemoryMetric struct {
MemAllocated uint64 `json:"mem_allocated"`
MemTotal uint64 `json:"mem_total"`
MemSys uint64 `json:"mem_sys"`
Lookups uint64 `json:"lookups"`
MemMallocs uint64 `json:"mem_mallocs"`
MemFrees uint64 `json:"mem_frees"`
HeapAlloc uint64 `json:"heap_alloc"`
HeapSys uint64 `json:"heap_sys"`
HeapIdle uint64 `json:"heap_idle"`
HeapInuse uint64 `json:"heap_inuse"`
HeapReleased uint64 `json:"heap_released"`
HeapObjects uint64 `json:"heap_objects"`
StackInuse uint64 `json:"stack_inuse"`
StackSys uint64 `json:"stack_sys"`
MSpanInuse uint64 `json:"m_span_inuse"`
MSpanSys uint64 `json:"m_span_sys"`
MCacheInuse uint64 `json:"m_cache_inuse"`
MCacheSys uint64 `json:"m_cache_sys"`
BuckHashSys uint64 `json:"buck_hash_sys"`
GCSys uint64 `json:"gc_sys"`
OtherSys uint64 `json:"other_sys"`
NextGC uint64 `json:"next_gc"`
LastGC uint64 `json:"last_gc"`
PauseTotalNs uint64 `json:"pause_total_ns"`
PauseNs uint64 `json:"pause_ns"`
NumGC uint32 `json:"num_gc"`
}
//easyjson:json
type Metric struct {
Cmdline []string `json:"cmd_line"`
Uptime uint64 `json:"uptime"`
Request RequestMetric `json:"request"`
Runtime RuntimeMetric `json:"runtime"`
Memory MemoryMetric `json:"memory"`
}
//easyjson:json
type PermRes struct {
code int `json:"-" binding:"required"`
Message string `json:"message" binding:"required"`
}
//easyjson:json
type MemoryMetricReadable struct {
MemAllocated string `json:"mem_allocated"`
MemTotal string `json:"mem_total"`
MemSys string `json:"mem_sys"`
Lookups uint64 `json:"lookups"`
MemMallocs uint64 `json:"mem_mallocs"`
MemFrees uint64 `json:"mem_frees"`
HeapAlloc string `json:"heap_alloc"`
HeapSys string `json:"heap_sys"`
HeapIdle string `json:"heap_idle"`
HeapInuse string `json:"heap_inuse"`
HeapReleased string `json:"heap_released"`
HeapObjects uint64 `json:"heap_objects"`
StackInuse string `json:"stack_inuse"`
StackSys string `json:"stack_sys"`
MSpanInuse string `json:"m_span_inuse"`
MSpanSys string `json:"m_span_sys"`
MCacheInuse string `json:"m_cache_inuse"`
MCacheSys string `json:"m_cache_sys"`
BuckHashSys string `json:"buck_hash_sys"`
GCSys string `json:"gc_sys"`
OtherSys string `json:"other_sys"`
NextGC string `json:"next_gc"`
LastGC string `json:"last_gc"`
PauseTotalNs string `json:"pause_total_ns"`
PauseNs string `json:"pause_ns"`
NumGC uint32 `json:"num_gc"`
}
//easyjson:json
type MetricReadable struct {
Cmdline []string `json:"cmd_line"`
Uptime string `json:"uptime"`
Request RequestMetricReadable `json:"request"`
Runtime RuntimeMetric `json:"runtime"`
Memory MemoryMetricReadable `json:"memory"`
}
//easyjson:json
type UserData struct {
Id string `json:"id" binding:"required"`
Name string `json:"name" binding:"required"`
Mail string `json:"mail" binding:"required"`
Role enums.UserType `json:"role" binding:"required"`
}
func (detail *UserData) String() string {
var buffer bytes.Buffer
buffer.WriteString("UserData(")
if detail != nil {
buffer.WriteString("id:")
buffer.WriteString(detail.Id)
} else {
buffer.WriteString("nil")
}
buffer.WriteString(")")
return buffer.String()
}
// -vars="mem:memory.mem_allocated,duration:request.duration.min,duration:request.duration.max,duration:request.duration.mean,duration:request.duration.stdev,duration:request.duration.p90,duration:request.duration.p95,duration:request.duration.p99,mem:memory.mem_allocated,mem:memory.heap_alloc,mem:memory.heap_inuse,mem:memory.stack_inuse,duration:memory.next_gc,duration:memory.last_gc,duration:memory.pause_total_ns,duration:memory.pause_ns"

2330
http/iface/rest_easyjson.go Normal file

File diff suppressed because it is too large Load Diff

116
http/init.go Normal file
View File

@ -0,0 +1,116 @@
package http
import (
"net/http"
"time"
"amuz.es/gogs/infra/changer/http/middleware/logging"
"amuz.es/gogs/infra/changer/http/middleware/recovery"
"amuz.es/gogs/infra/changer/http/middleware/session"
"amuz.es/gogs/infra/changer/http/route"
"amuz.es/gogs/infra/changer/http/template"
"amuz.es/gogs/infra/changer/util"
"github.com/gin-gonic/gin"
errs "github.com/pkg/errors"
graceful "gopkg.in/tylerb/graceful.v1"
)
var (
logger = util.NewLogger("http")
server *graceful.Server
)
func InitHttp(
bindAddress string,
prefix string,
accessLogConfig *util.LogConfig,
ldapConfig *util.LdapConfig,
sessionConfig *util.SessionConfig,
profile bool,
errorSignal chan error, closeSignal chan struct{}) {
gin.SetMode(gin.ReleaseMode)
engine := gin.New()
session.InitRole()
initMiddleware(engine, accessLogConfig,ldapConfig, sessionConfig)
initRoutes(engine, prefix, profile)
server = &graceful.Server{
Timeout: 10 * time.Second,
Server: &http.Server{
Addr: bindAddress,
Handler: engine,
},
TCPKeepAlive: 3 * time.Second,
NoSignalHandling: true,
LogFunc: logger.Debugf,
}
go bind(server, errorSignal, closeSignal)
logger.Info(bindAddress + " bound")
}
func CloseHttp() {
logger.Info("HTTP closing..")
server.Stop(10 * time.Second)
logger.Info("HTTP closed")
}
func bind(server *graceful.Server, errorSignal chan error, closeSignal chan struct{}) {
if err := server.ListenAndServe(); err != nil {
errorSignal <- errs.Wrap(err, "failed to listen or serve http")
} else {
close(closeSignal)
}
}
func initRoutes(engine *gin.Engine, prefix string, profile bool) {
suburl := prefix
path := engine.Group(suburl)
path.StaticFS("/static", route.Static(""))
if profile {
logger.Info("enable Pprof handler ")
path.GET("/debug/pprof/", IndexHandler())
path.GET("/debug/pprof/heap", HeapHandler())
path.GET("/debug/pprof/goroutine", GoroutineHandler())
path.GET("/debug/pprof/block", BlockHandler())
path.GET("/debug/pprof/threadcreate", ThreadCreateHandler())
path.GET("/debug/pprof/cmdline", CmdlineHandler())
path.GET("/debug/pprof/profile", ProfileHandler())
path.GET("/debug/pprof/symbol", SymbolHandler())
path.POST("/debug/pprof/symbol", SymbolHandler())
path.GET("/debug/pprof/trace", TraceHandler())
}
path.GET("/favicon.ico", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/static/favicon/images/favicon.ico")
})
path.GET("/manifest.json", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/static/favicon/manifest.json")
})
path.GET("/browserconfig.xml", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "/static/favicon/browserconfig.xml")
})
path.GET("/", route.Index)
path.GET("/change_password", route.ChangePasswordView)
path.POST("/change_password", route.ChangePassword)
path.GET("/login", route.LoginView)
path.POST("/login", route.Login)
path.GET("/logout", route.Logout)
}
func initMiddleware(engine *gin.Engine, accessLogConfig *util.LogConfig,ldapConfig *util.LdapConfig, sessionConfig *util.SessionConfig) {
engine.Use(logging.AccessLog(accessLogConfig))
engine.Use(recovery.RecoveryJSON())
engine.Use(session.New(ldapConfig, sessionConfig))
engine.HTMLRender = template.DefaultRender()
// not found handler
engine.NoRoute(recovery.RecoveryHttpHTMLError(http.StatusNotFound))
engine.NoMethod(recovery.RecoveryHttpHTMLError(http.StatusMethodNotAllowed))
}

View File

@ -0,0 +1,154 @@
// middleware
package logging
import (
"bytes"
"io"
"net"
"net/http"
"strconv"
"time"
"amuz.es/gogs/infra/changer/http/iface"
"amuz.es/gogs/infra/changer/util"
"github.com/gin-gonic/gin"
)
const (
dateFormat = "02/Jan/2006:15:04:05 -0700"
)
var DurationAspect = iface.NewRequestDurationAggr()
var CounterAspect = iface.NewCounterMetric()
type logItem struct {
host string
method string
uri string
proto string
referer string
userAgent string
requestedHost string
requestedPath string
status int
responseSize int
requestTime time.Time
duration time.Duration
}
func AccessLog(config *util.LogConfig) gin.HandlerFunc {
_, logWriter := util.NewLogWriter(config)
DurationAspect.StartTimer(10 * time.Second)
CounterAspect.StartTimer(1 * time.Minute)
logChan := make(chan logItem)
go sendLogging(logWriter, logChan)
return func(c *gin.Context) {
now := time.Now()
req := c.Request
defer func() {
dur := time.Now().Sub(now)
logItemData := logItem{
status: c.Writer.Status(),
responseSize: c.Writer.Size(),
requestTime: now,
duration: dur,
}
if req != nil {
logItemData.host = remoteHost(req)
logItemData.method = req.Method
logItemData.uri = req.RequestURI
logItemData.proto = req.Proto
logItemData.referer = req.Referer()
logItemData.userAgent = req.UserAgent()
logItemData.requestedHost = req.Host
logItemData.requestedPath = req.URL.Path
} else {
logItemData.host = "-"
logItemData.method = ""
logItemData.uri = ""
logItemData.proto = ""
logItemData.referer = ""
logItemData.userAgent = ""
logItemData.requestedHost = ""
logItemData.requestedPath = ""
}
logChan <- logItemData
}()
c.Next()
}
}
func sendLogging(accesslog io.Writer, logChan chan logItem) {
for {
select {
case logItemData := <-logChan:
DurationAspect.Add(float64(logItemData.duration.Nanoseconds()))
CounterAspect.Inc <- iface.Tuple{
Path: logItemData.requestedPath,
Code: logItemData.status,
}
// Logs an access event in Apache combined log format (with a minor customization with the duration).
buf := bytes.NewBufferString("")
buf.WriteString(logItemData.host)
buf.WriteString(` - - [`)
buf.WriteString(logItemData.requestTime.Format(dateFormat))
buf.WriteString(`] "`)
buf.WriteString(logItemData.method)
buf.WriteByte(' ')
buf.WriteString(logItemData.uri)
buf.WriteByte(' ')
buf.WriteString(logItemData.proto)
buf.WriteString(`" `)
buf.WriteString(strconv.Itoa(logItemData.status))
buf.WriteByte(' ')
buf.WriteString(strconv.Itoa(logItemData.responseSize))
buf.WriteString(` "`)
buf.WriteString(logItemData.referer)
buf.WriteString(`" "`)
buf.WriteString(logItemData.userAgent)
buf.WriteString(`" `)
buf.WriteString(strconv.FormatInt((logItemData.duration.Nanoseconds() / time.Millisecond.Nanoseconds()), 10))
buf.WriteByte(' ')
buf.WriteString(logItemData.requestedHost)
buf.WriteByte('\n')
accesslog.Write(buf.Bytes())
}
}
}
// strip port from addresses with hostname, ipv4 or ipv6
func stripPort(address string) string {
if h, _, err := net.SplitHostPort(address); err == nil {
return h
}
return address
}
// The remote address of the client. When the 'X-Forwarded-For'
// header is set, then it is used instead.
func remoteAddr(r *http.Request) string {
ff := r.Header.Get("X-Forwarded-For")
if ff != "" {
return ff
}
return r.RemoteAddr
}
func remoteHost(r *http.Request) string {
a := remoteAddr(r)
h := stripPort(a)
if h != "" {
return h
}
return "-"
}

View File

@ -0,0 +1,203 @@
package recovery
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"runtime"
"strings"
"amuz.es/gogs/infra/changer/http/iface"
"amuz.es/gogs/infra/changer/util"
"github.com/Sirupsen/logrus"
"github.com/flosch/pongo2"
"github.com/gin-gonic/gin"
)
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
logger = util.NewLogger("recovery")
color = "danger"
preText = "루이보스 API 오류 :bomb:"
title = "API 요청을 처리하는 중에 의도하지 않은 오류가 발행하였습니다."
footer = "확인해 주세요! :hugging_face:"
)
func recoveryInternal(stack []byte, code int, req *http.Request, err *string) {
httprequest, _ := httputil.DumpRequest(req, false)
detailInfo := map[string]interface{}{
"method": req.Method,
"statusCode": code,
"path": req.URL.Path,
"message": err,
"request": string(httprequest),
"stack": string(stack),
}
// message := slack.Payload{
// Attachments: []slack.Attachment{
// convertSlackMessage(code, err, &stack, req, &httprequest),
// },
// }
// slack.Send(util.Config.Slack.Url, "", message)
logger.WithFields(logrus.Fields(detailInfo)).Error("[Recovery] panic recovered:\n")
}
func recoveryError(code int, _ *gin.Context) *iface.WrappedResponse {
return &iface.WrappedResponse{
Message: http.StatusText(code),
}
}
func RecoveryHttpJSONError(code int) gin.HandlerFunc {
return func(c *gin.Context) {
info := recoveryError(code, c)
util.DumpJSON(c, code, info)
}
}
func RecoveryHttpHTMLError(code int) gin.HandlerFunc {
return func(c *gin.Context) {
info := recoveryError(code, c)
c.HTML(code, "views/error.html", pongo2.Context{
"code": code,
"message": info.Message,
})
}
}
func RecoveryJSON() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
code := http.StatusInternalServerError
stack := stack(5)
systemErrMessage := err.(error).Error()
go recoveryInternal(stack, code, c.Copy().Request, &systemErrMessage)
util.DumpJSON(c, code, &iface.WrappedResponse{
Message: systemErrMessage,
})
c.Abort()
}
}()
c.Next()
}
}
func RecoveryHTML() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
code := http.StatusInternalServerError
systemErrMessage := err.(error).Error()
c.HTML(code, "views/error.html", pongo2.Context{
"code": code,
"message": systemErrMessage,
})
c.Abort()
}
}()
c.Next()
}
}
// stack returns a nicely formated stack frame, skipping skip frames
func stack(skip int) []byte {
buf := new(bytes.Buffer) // the returned data
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := skip; ; i++ {
// Skip the expected number of frames
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
if paths := strings.SplitN(file, "src/", 2); len(paths) == 1 {
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
} else if vendors := strings.SplitN(paths[1], "vendor/", 2); len(vendors) == 1 {
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", paths[1], line, pc)
} else {
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", vendors[1], line, pc)
}
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.TrimSpace(lines[n])
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Also the package path might contains dot (e.g. code.google.com/...),
// so first eliminate the path prefix
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
}
name = bytes.Replace(name, centerDot, dot, -1)
return name
}
// func convertSlackMessage(
// code int, err *string, stack *[]byte,
// req *http.Request, httpdumpreq *[]byte) (attachment slack.Attachment) {
// errmsg := "N/A"
// if err != nil {
// errmsg = *err
// }
// return slack.Attachment{
// Color: &color,
// PreText: &preText,
// Title: &title,
// Footer: &footer,
// Text: err,
// Fields: []*slack.Field{
// {Title: "status", Value: fmt.Sprintf("%s(%d)", http.StatusText(code), code), Short: true},
// {Title: "method", Value: req.Method, Short: true},
// {Title: "path", Value: req.URL.Path, Short: true},
// {Title: "phase", Value: util.Config.Phase, Short: true},
// {Title: "version", Value: util.Config.Version(), Short: true},
// {Title: "message", Value: errmsg, Short: true},
// {Title: "request", Value: string(*httpdumpreq), Short: false},
// {Title: "stack", Value: string(*stack), Short: false},
// },
// }
// }

View File

@ -0,0 +1,179 @@
package secure
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
const (
stsHeader = "Strict-Transport-Security"
stsSubdomainString = "; includeSubdomains"
frameOptionsHeader = "X-Frame-Options"
frameOptionsValue = "DENY"
contentTypeHeader = "X-Content-Type-Options"
contentTypeValue = "nosniff"
xssProtectionHeader = "X-XSS-Protection"
xssProtectionValue = "1; mode=block"
cspHeader = "Content-Security-Policy"
)
func defaultBadHostHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad Host", http.StatusInternalServerError)
}
// Options is a struct for specifying configuration options for the secure.Secure middleware.
type Options struct {
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
AllowedHosts []string
// If SSLRedirect is set to true, then only allow https requests. Default is false.
SSLRedirect bool
// If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
SSLTemporaryRedirect bool
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
SSLHost string
// SSLProxyHeaders is set of header keys with associated values that would indicate a valid https request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
SSLProxyHeaders map[string]string
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
STSSeconds int64
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
STSIncludeSubdomains bool
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
FrameDeny bool
// CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
CustomFrameOptionsValue string
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
ContentTypeNosniff bool
// If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
BrowserXssFilter bool
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
ContentSecurityPolicy string
// When developing, the AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain... so set this to true for dev environment.
// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. Default if false.
IsDevelopment bool
// Handlers for when an error occurs (ie bad host).
BadHostHandler http.Handler
}
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
// provided to configure which features should be enabled, and the ability to override a few of the default values.
type secure struct {
// Customize Secure with an Options struct.
opt Options
}
// Constructs a new Secure instance with supplied options.
func New(options Options) *secure {
if options.BadHostHandler == nil {
options.BadHostHandler = http.HandlerFunc(defaultBadHostHandler)
}
return &secure{
opt: options,
}
}
func (s *secure) process(w http.ResponseWriter, r *http.Request) error {
// Allowed hosts check.
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
isGoodHost := false
for _, allowedHost := range s.opt.AllowedHosts {
if strings.EqualFold(allowedHost, r.Host) {
isGoodHost = true
break
}
}
if !isGoodHost {
s.opt.BadHostHandler.ServeHTTP(w, r)
return fmt.Errorf("Bad host name: %s", r.Host)
}
}
// SSL check.
if s.opt.SSLRedirect && s.opt.IsDevelopment == false {
isSSL := false
if strings.EqualFold(r.URL.Scheme, "https") || r.TLS != nil {
isSSL = true
} else {
for k, v := range s.opt.SSLProxyHeaders {
if r.Header.Get(k) == v {
isSSL = true
break
}
}
}
if isSSL == false {
url := r.URL
url.Scheme = "https"
url.Host = r.Host
if len(s.opt.SSLHost) > 0 {
url.Host = s.opt.SSLHost
}
status := http.StatusMovedPermanently
if s.opt.SSLTemporaryRedirect {
status = http.StatusTemporaryRedirect
}
http.Redirect(w, r, url.String(), status)
return fmt.Errorf("Redirecting to HTTPS")
}
}
// Strict Transport Security header.
if s.opt.STSSeconds != 0 && !s.opt.IsDevelopment {
stsSub := ""
if s.opt.STSIncludeSubdomains {
stsSub = stsSubdomainString
}
w.Header().Add(stsHeader, fmt.Sprintf("max-age=%d%s", s.opt.STSSeconds, stsSub))
}
// Frame Options header.
if len(s.opt.CustomFrameOptionsValue) > 0 {
w.Header().Add(frameOptionsHeader, s.opt.CustomFrameOptionsValue)
} else if s.opt.FrameDeny {
w.Header().Add(frameOptionsHeader, frameOptionsValue)
}
// Content Type Options header.
if s.opt.ContentTypeNosniff {
w.Header().Add(contentTypeHeader, contentTypeValue)
}
// XSS Protection header.
if s.opt.BrowserXssFilter {
w.Header().Add(xssProtectionHeader, xssProtectionValue)
}
// Content Security Policy header.
if len(s.opt.ContentSecurityPolicy) > 0 {
w.Header().Add(cspHeader, s.opt.ContentSecurityPolicy)
}
return nil
}
func Secure(options Options) gin.HandlerFunc {
s := New(options)
return func(c *gin.Context) {
err := s.process(c.Writer, c.Request)
if err != nil {
if c.Writer.Written() {
c.AbortWithStatus(c.Writer.Status())
} else {
c.AbortWithError(http.StatusInternalServerError, err)
}
}
}
}

View File

@ -0,0 +1,60 @@
package session
import "bytes"
type userDetail struct {
id string
name string
mail string
role Role
}
type UserDetail interface {
Id() string
Name() string
Mail() string
Role() Role
ToModelContext() map[string]interface{}
}
func (detail *userDetail) Id() string {
return detail.id
}
func (detail *userDetail) Name() string {
return detail.name
}
func (detail *userDetail) Mail() string {
return detail.mail
}
func (detail *userDetail) Role() Role {
return detail.role
}
func (detail *userDetail) ToModelContext()map[string]interface{} {
context:=make(map[string]interface{})
context["user_id"] = detail.id
context["user_name"] = detail.name
context["user_mail"] = detail.mail
context["user_role"] = detail.role.Type().Name()
return context
}
func (detail *userDetail) String() string {
var buffer bytes.Buffer
buffer.WriteString("UserData(")
if detail != nil {
buffer.WriteString("id:")
buffer.WriteString(detail.id)
buffer.WriteString("name:")
buffer.WriteString(detail.name)
buffer.WriteString("role:")
if detail.role != nil {
buffer.WriteString(detail.role.String())
} else {
buffer.WriteString("nil")
}
} else {
buffer.WriteString("nil")
}
buffer.WriteString(")")
return buffer.String()
}

Some files were not shown because too many files have changed in this diff Show More