summaryrefslogtreecommitdiff
path: root/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable
diff options
context:
space:
mode:
Diffstat (limited to 'views/js/bootstrap3-editable-1.5.1/bootstrap3-editable')
-rw-r--r--views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/css/bootstrap-editable.css663
-rw-r--r--views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/clear.pngbin0 -> 509 bytes
-rw-r--r--views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/loading.gifbin0 -> 1849 bytes
-rw-r--r--views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.js6807
-rw-r--r--views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js7
5 files changed, 7477 insertions, 0 deletions
diff --git a/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/css/bootstrap-editable.css b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/css/bootstrap-editable.css
new file mode 100644
index 0000000..eaef0de
--- /dev/null
+++ b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/css/bootstrap-editable.css
@@ -0,0 +1,663 @@
+/*! X-editable - v1.5.1
+* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
+* http://github.com/vitalets/x-editable
+* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
+.editableform {
+ margin-bottom: 0; /* overwrites bootstrap margin */
+}
+
+.editableform .control-group {
+ margin-bottom: 0; /* overwrites bootstrap margin */
+ white-space: nowrap; /* prevent wrapping buttons on new line */
+ line-height: 20px; /* overwriting bootstrap line-height. See #133 */
+}
+
+/*
+ BS3 width:1005 for inputs breaks editable form in popup
+ See: https://github.com/vitalets/x-editable/issues/393
+*/
+.editableform .form-control {
+ width: auto;
+}
+
+.editable-buttons {
+ display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
+ vertical-align: top;
+ margin-left: 7px;
+ /* inline-block emulation for IE7*/
+ zoom: 1;
+ *display: inline;
+}
+
+.editable-buttons.editable-buttons-bottom {
+ display: block;
+ margin-top: 7px;
+ margin-left: 0;
+}
+
+.editable-input {
+ vertical-align: top;
+ display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */
+ width: auto; /* bootstrap-responsive has width: 100% that breakes layout */
+ white-space: normal; /* reset white-space decalred in parent*/
+ /* display-inline emulation for IE7*/
+ zoom: 1;
+ *display: inline;
+}
+
+.editable-buttons .editable-cancel {
+ margin-left: 7px;
+}
+
+/*for jquery-ui buttons need set height to look more pretty*/
+.editable-buttons button.ui-button-icon-only {
+ height: 24px;
+ width: 30px;
+}
+
+.editableform-loading {
+ background: url('../img/loading.gif') center center no-repeat;
+ height: 25px;
+ width: auto;
+ min-width: 25px;
+}
+
+.editable-inline .editableform-loading {
+ background-position: left 5px;
+}
+
+ .editable-error-block {
+ max-width: 300px;
+ margin: 5px 0 0 0;
+ width: auto;
+ white-space: normal;
+}
+
+/*add padding for jquery ui*/
+.editable-error-block.ui-state-error {
+ padding: 3px;
+}
+
+.editable-error {
+ color: red;
+}
+
+/* ---- For specific types ---- */
+
+.editableform .editable-date {
+ padding: 0;
+ margin: 0;
+ float: left;
+}
+
+/* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */
+.editable-inline .add-on .icon-th {
+ margin-top: 3px;
+ margin-left: 1px;
+}
+
+
+/* checklist vertical alignment */
+.editable-checklist label input[type="checkbox"],
+.editable-checklist label span {
+ vertical-align: middle;
+ margin: 0;
+}
+
+.editable-checklist label {
+ white-space: nowrap;
+}
+
+/* set exact width of textarea to fit buttons toolbar */
+.editable-wysihtml5 {
+ width: 566px;
+ height: 250px;
+}
+
+/* clear button shown as link in date inputs */
+.editable-clear {
+ clear: both;
+ font-size: 0.9em;
+ text-decoration: none;
+ text-align: right;
+}
+
+/* IOS-style clear button for text inputs */
+.editable-clear-x {
+ background: url('../img/clear.png') center center no-repeat;
+ display: block;
+ width: 13px;
+ height: 13px;
+ position: absolute;
+ opacity: 0.6;
+ z-index: 100;
+
+ top: 50%;
+ right: 6px;
+ margin-top: -6px;
+
+}
+
+.editable-clear-x:hover {
+ opacity: 1;
+}
+
+.editable-pre-wrapped {
+ white-space: pre-wrap;
+}
+.editable-container.editable-popup {
+ max-width: none !important; /* without this rule poshytip/tooltip does not stretch */
+}
+
+.editable-container.popover {
+ width: auto; /* without this rule popover does not stretch */
+}
+
+.editable-container.editable-inline {
+ display: inline-block;
+ vertical-align: middle;
+ width: auto;
+ /* inline-block emulation for IE7*/
+ zoom: 1;
+ *display: inline;
+}
+
+.editable-container.ui-widget {
+ font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */
+ z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */
+}
+.editable-click,
+a.editable-click,
+a.editable-click:hover {
+ text-decoration: none;
+ border-bottom: dashed 1px #0088cc;
+}
+
+.editable-click.editable-disabled,
+a.editable-click.editable-disabled,
+a.editable-click.editable-disabled:hover {
+ color: #585858;
+ cursor: default;
+ border-bottom: none;
+}
+
+.editable-empty, .editable-empty:hover, .editable-empty:focus{
+ font-style: italic;
+ color: #DD1144;
+ /* border-bottom: none; */
+ text-decoration: none;
+}
+
+.editable-unsaved {
+ font-weight: bold;
+}
+
+.editable-unsaved:after {
+/* content: '*'*/
+}
+
+.editable-bg-transition {
+ -webkit-transition: background-color 1400ms ease-out;
+ -moz-transition: background-color 1400ms ease-out;
+ -o-transition: background-color 1400ms ease-out;
+ -ms-transition: background-color 1400ms ease-out;
+ transition: background-color 1400ms ease-out;
+}
+
+/*see https://github.com/vitalets/x-editable/issues/139 */
+.form-horizontal .editable
+{
+ padding-top: 5px;
+ display:inline-block;
+}
+
+
+/*!
+ * Datepicker for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ */
+.datepicker {
+ padding: 4px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ direction: ltr;
+ /*.dow {
+ border-top: 1px solid #ddd !important;
+ }*/
+
+}
+.datepicker-inline {
+ width: 220px;
+}
+.datepicker.datepicker-rtl {
+ direction: rtl;
+}
+.datepicker.datepicker-rtl table tr td span {
+ float: right;
+}
+.datepicker-dropdown {
+ top: 0;
+ left: 0;
+}
+.datepicker-dropdown:before {
+ content: '';
+ display: inline-block;
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #ccc;
+ border-bottom-color: rgba(0, 0, 0, 0.2);
+ position: absolute;
+ top: -7px;
+ left: 6px;
+}
+.datepicker-dropdown:after {
+ content: '';
+ display: inline-block;
+ border-left: 6px solid transparent;
+ border-right: 6px solid transparent;
+ border-bottom: 6px solid #ffffff;
+ position: absolute;
+ top: -6px;
+ left: 7px;
+}
+.datepicker > div {
+ display: none;
+}
+.datepicker.days div.datepicker-days {
+ display: block;
+}
+.datepicker.months div.datepicker-months {
+ display: block;
+}
+.datepicker.years div.datepicker-years {
+ display: block;
+}
+.datepicker table {
+ margin: 0;
+}
+.datepicker td,
+.datepicker th {
+ text-align: center;
+ width: 20px;
+ height: 20px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ border: none;
+}
+.table-striped .datepicker table tr td,
+.table-striped .datepicker table tr th {
+ background-color: transparent;
+}
+.datepicker table tr td.day:hover {
+ background: #eeeeee;
+ cursor: pointer;
+}
+.datepicker table tr td.old,
+.datepicker table tr td.new {
+ color: #999999;
+}
+.datepicker table tr td.disabled,
+.datepicker table tr td.disabled:hover {
+ background: none;
+ color: #999999;
+ cursor: default;
+}
+.datepicker table tr td.today,
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today.disabled:hover {
+ background-color: #fde19a;
+ background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
+ background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
+ background-image: linear-gradient(top, #fdd49a, #fdf59a);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
+ border-color: #fdf59a #fdf59a #fbed50;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #000;
+}
+.datepicker table tr td.today:hover,
+.datepicker table tr td.today:hover:hover,
+.datepicker table tr td.today.disabled:hover,
+.datepicker table tr td.today.disabled:hover:hover,
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active,
+.datepicker table tr td.today.disabled,
+.datepicker table tr td.today:hover.disabled,
+.datepicker table tr td.today.disabled.disabled,
+.datepicker table tr td.today.disabled:hover.disabled,
+.datepicker table tr td.today[disabled],
+.datepicker table tr td.today:hover[disabled],
+.datepicker table tr td.today.disabled[disabled],
+.datepicker table tr td.today.disabled:hover[disabled] {
+ background-color: #fdf59a;
+}
+.datepicker table tr td.today:active,
+.datepicker table tr td.today:hover:active,
+.datepicker table tr td.today.disabled:active,
+.datepicker table tr td.today.disabled:hover:active,
+.datepicker table tr td.today.active,
+.datepicker table tr td.today:hover.active,
+.datepicker table tr td.today.disabled.active,
+.datepicker table tr td.today.disabled:hover.active {
+ background-color: #fbf069 \9;
+}
+.datepicker table tr td.today:hover:hover {
+ color: #000;
+}
+.datepicker table tr td.today.active:hover {
+ color: #fff;
+}
+.datepicker table tr td.range,
+.datepicker table tr td.range:hover,
+.datepicker table tr td.range.disabled,
+.datepicker table tr td.range.disabled:hover {
+ background: #eeeeee;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.datepicker table tr td.range.today,
+.datepicker table tr td.range.today:hover,
+.datepicker table tr td.range.today.disabled,
+.datepicker table tr td.range.today.disabled:hover {
+ background-color: #f3d17a;
+ background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
+ background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
+ background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
+ background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
+ background-image: linear-gradient(top, #f3c17a, #f3e97a);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
+ border-color: #f3e97a #f3e97a #edde34;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+}
+.datepicker table tr td.range.today:hover,
+.datepicker table tr td.range.today:hover:hover,
+.datepicker table tr td.range.today.disabled:hover,
+.datepicker table tr td.range.today.disabled:hover:hover,
+.datepicker table tr td.range.today:active,
+.datepicker table tr td.range.today:hover:active,
+.datepicker table tr td.range.today.disabled:active,
+.datepicker table tr td.range.today.disabled:hover:active,
+.datepicker table tr td.range.today.active,
+.datepicker table tr td.range.today:hover.active,
+.datepicker table tr td.range.today.disabled.active,
+.datepicker table tr td.range.today.disabled:hover.active,
+.datepicker table tr td.range.today.disabled,
+.datepicker table tr td.range.today:hover.disabled,
+.datepicker table tr td.range.today.disabled.disabled,
+.datepicker table tr td.range.today.disabled:hover.disabled,
+.datepicker table tr td.range.today[disabled],
+.datepicker table tr td.range.today:hover[disabled],
+.datepicker table tr td.range.today.disabled[disabled],
+.datepicker table tr td.range.today.disabled:hover[disabled] {
+ background-color: #f3e97a;
+}
+.datepicker table tr td.range.today:active,
+.datepicker table tr td.range.today:hover:active,
+.datepicker table tr td.range.today.disabled:active,
+.datepicker table tr td.range.today.disabled:hover:active,
+.datepicker table tr td.range.today.active,
+.datepicker table tr td.range.today:hover.active,
+.datepicker table tr td.range.today.disabled.active,
+.datepicker table tr td.range.today.disabled:hover.active {
+ background-color: #efe24b \9;
+}
+.datepicker table tr td.selected,
+.datepicker table tr td.selected:hover,
+.datepicker table tr td.selected.disabled,
+.datepicker table tr td.selected.disabled:hover {
+ background-color: #9e9e9e;
+ background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
+ background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
+ background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
+ background-image: -o-linear-gradient(top, #b3b3b3, #808080);
+ background-image: linear-gradient(top, #b3b3b3, #808080);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
+ border-color: #808080 #808080 #595959;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td.selected:hover,
+.datepicker table tr td.selected:hover:hover,
+.datepicker table tr td.selected.disabled:hover,
+.datepicker table tr td.selected.disabled:hover:hover,
+.datepicker table tr td.selected:active,
+.datepicker table tr td.selected:hover:active,
+.datepicker table tr td.selected.disabled:active,
+.datepicker table tr td.selected.disabled:hover:active,
+.datepicker table tr td.selected.active,
+.datepicker table tr td.selected:hover.active,
+.datepicker table tr td.selected.disabled.active,
+.datepicker table tr td.selected.disabled:hover.active,
+.datepicker table tr td.selected.disabled,
+.datepicker table tr td.selected:hover.disabled,
+.datepicker table tr td.selected.disabled.disabled,
+.datepicker table tr td.selected.disabled:hover.disabled,
+.datepicker table tr td.selected[disabled],
+.datepicker table tr td.selected:hover[disabled],
+.datepicker table tr td.selected.disabled[disabled],
+.datepicker table tr td.selected.disabled:hover[disabled] {
+ background-color: #808080;
+}
+.datepicker table tr td.selected:active,
+.datepicker table tr td.selected:hover:active,
+.datepicker table tr td.selected.disabled:active,
+.datepicker table tr td.selected.disabled:hover:active,
+.datepicker table tr td.selected.active,
+.datepicker table tr td.selected:hover.active,
+.datepicker table tr td.selected.disabled.active,
+.datepicker table tr td.selected.disabled:hover.active {
+ background-color: #666666 \9;
+}
+.datepicker table tr td.active,
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active.disabled:hover {
+ background-color: #006dcc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td.active:hover,
+.datepicker table tr td.active:hover:hover,
+.datepicker table tr td.active.disabled:hover,
+.datepicker table tr td.active.disabled:hover:hover,
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active,
+.datepicker table tr td.active.disabled,
+.datepicker table tr td.active:hover.disabled,
+.datepicker table tr td.active.disabled.disabled,
+.datepicker table tr td.active.disabled:hover.disabled,
+.datepicker table tr td.active[disabled],
+.datepicker table tr td.active:hover[disabled],
+.datepicker table tr td.active.disabled[disabled],
+.datepicker table tr td.active.disabled:hover[disabled] {
+ background-color: #0044cc;
+}
+.datepicker table tr td.active:active,
+.datepicker table tr td.active:hover:active,
+.datepicker table tr td.active.disabled:active,
+.datepicker table tr td.active.disabled:hover:active,
+.datepicker table tr td.active.active,
+.datepicker table tr td.active:hover.active,
+.datepicker table tr td.active.disabled.active,
+.datepicker table tr td.active.disabled:hover.active {
+ background-color: #003399 \9;
+}
+.datepicker table tr td span {
+ display: block;
+ width: 23%;
+ height: 54px;
+ line-height: 54px;
+ float: left;
+ margin: 1%;
+ cursor: pointer;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+.datepicker table tr td span:hover {
+ background: #eeeeee;
+}
+.datepicker table tr td span.disabled,
+.datepicker table tr td span.disabled:hover {
+ background: none;
+ color: #999999;
+ cursor: default;
+}
+.datepicker table tr td span.active,
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active.disabled:hover {
+ background-color: #006dcc;
+ background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
+ background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
+ background-image: -o-linear-gradient(top, #0088cc, #0044cc);
+ background-image: linear-gradient(top, #0088cc, #0044cc);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
+ border-color: #0044cc #0044cc #002a80;
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ color: #fff;
+ text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+.datepicker table tr td span.active:hover,
+.datepicker table tr td span.active:hover:hover,
+.datepicker table tr td span.active.disabled:hover,
+.datepicker table tr td span.active.disabled:hover:hover,
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active,
+.datepicker table tr td span.active.disabled,
+.datepicker table tr td span.active:hover.disabled,
+.datepicker table tr td span.active.disabled.disabled,
+.datepicker table tr td span.active.disabled:hover.disabled,
+.datepicker table tr td span.active[disabled],
+.datepicker table tr td span.active:hover[disabled],
+.datepicker table tr td span.active.disabled[disabled],
+.datepicker table tr td span.active.disabled:hover[disabled] {
+ background-color: #0044cc;
+}
+.datepicker table tr td span.active:active,
+.datepicker table tr td span.active:hover:active,
+.datepicker table tr td span.active.disabled:active,
+.datepicker table tr td span.active.disabled:hover:active,
+.datepicker table tr td span.active.active,
+.datepicker table tr td span.active:hover.active,
+.datepicker table tr td span.active.disabled.active,
+.datepicker table tr td span.active.disabled:hover.active {
+ background-color: #003399 \9;
+}
+.datepicker table tr td span.old,
+.datepicker table tr td span.new {
+ color: #999999;
+}
+.datepicker th.datepicker-switch {
+ width: 145px;
+}
+.datepicker thead tr:first-child th,
+.datepicker tfoot tr th {
+ cursor: pointer;
+}
+.datepicker thead tr:first-child th:hover,
+.datepicker tfoot tr th:hover {
+ background: #eeeeee;
+}
+.datepicker .cw {
+ font-size: 10px;
+ width: 12px;
+ padding: 0 2px 0 5px;
+ vertical-align: middle;
+}
+.datepicker thead tr:first-child th.cw {
+ cursor: default;
+ background-color: transparent;
+}
+.input-append.date .add-on i,
+.input-prepend.date .add-on i {
+ display: block;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+}
+.input-daterange input {
+ text-align: center;
+}
+.input-daterange input:first-child {
+ -webkit-border-radius: 3px 0 0 3px;
+ -moz-border-radius: 3px 0 0 3px;
+ border-radius: 3px 0 0 3px;
+}
+.input-daterange input:last-child {
+ -webkit-border-radius: 0 3px 3px 0;
+ -moz-border-radius: 0 3px 3px 0;
+ border-radius: 0 3px 3px 0;
+}
+.input-daterange .add-on {
+ display: inline-block;
+ width: auto;
+ min-width: 16px;
+ height: 18px;
+ padding: 4px 5px;
+ font-weight: normal;
+ line-height: 18px;
+ text-align: center;
+ text-shadow: 0 1px 0 #ffffff;
+ vertical-align: middle;
+ background-color: #eeeeee;
+ border: 1px solid #ccc;
+ margin-left: -5px;
+ margin-right: -5px;
+}
diff --git a/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/clear.png b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/clear.png
new file mode 100644
index 0000000..580b52a
--- /dev/null
+++ b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/clear.png
Binary files differ
diff --git a/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/loading.gif b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/loading.gif
new file mode 100644
index 0000000..5b33f7e
--- /dev/null
+++ b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/img/loading.gif
Binary files differ
diff --git a/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.js b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.js
new file mode 100644
index 0000000..dd81385
--- /dev/null
+++ b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.js
@@ -0,0 +1,6807 @@
+/*! X-editable - v1.5.1
+* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
+* http://github.com/vitalets/x-editable
+* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
+/**
+Form with single input element, two buttons and two states: normal/loading.
+Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
+Editableform is linked with one of input types, e.g. 'text', 'select' etc.
+
+@class editableform
+@uses text
+@uses textarea
+**/
+(function ($) {
+ "use strict";
+
+ var EditableForm = function (div, options) {
+ this.options = $.extend({}, $.fn.editableform.defaults, options);
+ this.$div = $(div); //div, containing form. Not form tag. Not editable-element.
+ if(!this.options.scope) {
+ this.options.scope = this;
+ }
+ //nothing shown after init
+ };
+
+ EditableForm.prototype = {
+ constructor: EditableForm,
+ initInput: function() { //called once
+ //take input from options (as it is created in editable-element)
+ this.input = this.options.input;
+
+ //set initial value
+ //todo: may be add check: typeof str === 'string' ?
+ this.value = this.input.str2value(this.options.value);
+
+ //prerender: get input.$input
+ this.input.prerender();
+ },
+ initTemplate: function() {
+ this.$form = $($.fn.editableform.template);
+ },
+ initButtons: function() {
+ var $btn = this.$form.find('.editable-buttons');
+ $btn.append($.fn.editableform.buttons);
+ if(this.options.showbuttons === 'bottom') {
+ $btn.addClass('editable-buttons-bottom');
+ }
+ },
+ /**
+ Renders editableform
+
+ @method render
+ **/
+ render: function() {
+ //init loader
+ this.$loading = $($.fn.editableform.loading);
+ this.$div.empty().append(this.$loading);
+
+ //init form template and buttons
+ this.initTemplate();
+ if(this.options.showbuttons) {
+ this.initButtons();
+ } else {
+ this.$form.find('.editable-buttons').remove();
+ }
+
+ //show loading state
+ this.showLoading();
+
+ //flag showing is form now saving value to server.
+ //It is needed to wait when closing form.
+ this.isSaving = false;
+
+ /**
+ Fired when rendering starts
+ @event rendering
+ @param {Object} event event object
+ **/
+ this.$div.triggerHandler('rendering');
+
+ //init input
+ this.initInput();
+
+ //append input to form
+ this.$form.find('div.editable-input').append(this.input.$tpl);
+
+ //append form to container
+ this.$div.append(this.$form);
+
+ //render input
+ $.when(this.input.render())
+ .then($.proxy(function () {
+ //setup input to submit automatically when no buttons shown
+ if(!this.options.showbuttons) {
+ this.input.autosubmit();
+ }
+
+ //attach 'cancel' handler
+ this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
+
+ if(this.input.error) {
+ this.error(this.input.error);
+ this.$form.find('.editable-submit').attr('disabled', true);
+ this.input.$input.attr('disabled', true);
+ //prevent form from submitting
+ this.$form.submit(function(e){ e.preventDefault(); });
+ } else {
+ this.error(false);
+ this.input.$input.removeAttr('disabled');
+ this.$form.find('.editable-submit').removeAttr('disabled');
+ var value = (this.value === null || this.value === undefined || this.value === '') ? this.options.defaultValue : this.value;
+ this.input.value2input(value);
+ //attach submit handler
+ this.$form.submit($.proxy(this.submit, this));
+ }
+
+ /**
+ Fired when form is rendered
+ @event rendered
+ @param {Object} event event object
+ **/
+ this.$div.triggerHandler('rendered');
+
+ this.showForm();
+
+ //call postrender method to perform actions required visibility of form
+ if(this.input.postrender) {
+ this.input.postrender();
+ }
+ }, this));
+ },
+ cancel: function() {
+ /**
+ Fired when form was cancelled by user
+ @event cancel
+ @param {Object} event event object
+ **/
+ this.$div.triggerHandler('cancel');
+ },
+ showLoading: function() {
+ var w, h;
+ if(this.$form) {
+ //set loading size equal to form
+ w = this.$form.outerWidth();
+ h = this.$form.outerHeight();
+ if(w) {
+ this.$loading.width(w);
+ }
+ if(h) {
+ this.$loading.height(h);
+ }
+ this.$form.hide();
+ } else {
+ //stretch loading to fill container width
+ w = this.$loading.parent().width();
+ if(w) {
+ this.$loading.width(w);
+ }
+ }
+ this.$loading.show();
+ },
+
+ showForm: function(activate) {
+ this.$loading.hide();
+ this.$form.show();
+ if(activate !== false) {
+ this.input.activate();
+ }
+ /**
+ Fired when form is shown
+ @event show
+ @param {Object} event event object
+ **/
+ this.$div.triggerHandler('show');
+ },
+
+ error: function(msg) {
+ var $group = this.$form.find('.control-group'),
+ $block = this.$form.find('.editable-error-block'),
+ lines;
+
+ if(msg === false) {
+ $group.removeClass($.fn.editableform.errorGroupClass);
+ $block.removeClass($.fn.editableform.errorBlockClass).empty().hide();
+ } else {
+ //convert newline to <br> for more pretty error display
+ if(msg) {
+ lines = (''+msg).split('\n');
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = $('<div>').text(lines[i]).html();
+ }
+ msg = lines.join('<br>');
+ }
+ $group.addClass($.fn.editableform.errorGroupClass);
+ $block.addClass($.fn.editableform.errorBlockClass).html(msg).show();
+ }
+ },
+
+ submit: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ //get new value from input
+ var newValue = this.input.input2value();
+
+ //validation: if validate returns string or truthy value - means error
+ //if returns object like {newValue: '...'} => submitted value is reassigned to it
+ var error = this.validate(newValue);
+ if ($.type(error) === 'object' && error.newValue !== undefined) {
+ newValue = error.newValue;
+ this.input.value2input(newValue);
+ if(typeof error.msg === 'string') {
+ this.error(error.msg);
+ this.showForm();
+ return;
+ }
+ } else if (error) {
+ this.error(error);
+ this.showForm();
+ return;
+ }
+
+ //if value not changed --> trigger 'nochange' event and return
+ /*jslint eqeq: true*/
+ if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) {
+ /*jslint eqeq: false*/
+ /**
+ Fired when value not changed but form is submitted. Requires savenochange = false.
+ @event nochange
+ @param {Object} event event object
+ **/
+ this.$div.triggerHandler('nochange');
+ return;
+ }
+
+ //convert value for submitting to server
+ var submitValue = this.input.value2submit(newValue);
+
+ this.isSaving = true;
+
+ //sending data to server
+ $.when(this.save(submitValue))
+ .done($.proxy(function(response) {
+ this.isSaving = false;
+
+ //run success callback
+ var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
+
+ //if success callback returns false --> keep form open and do not activate input
+ if(res === false) {
+ this.error(false);
+ this.showForm(false);
+ return;
+ }
+
+ //if success callback returns string --> keep form open, show error and activate input
+ if(typeof res === 'string') {
+ this.error(res);
+ this.showForm();
+ return;
+ }
+
+ //if success callback returns object like {newValue: <something>} --> use that value instead of submitted
+ //it is usefull if you want to chnage value in url-function
+ if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
+ newValue = res.newValue;
+ }
+
+ //clear error message
+ this.error(false);
+ this.value = newValue;
+ /**
+ Fired when form is submitted
+ @event save
+ @param {Object} event event object
+ @param {Object} params additional params
+ @param {mixed} params.newValue raw new value
+ @param {mixed} params.submitValue submitted value as string
+ @param {Object} params.response ajax response
+
+ @example
+ $('#form-div').on('save'), function(e, params){
+ if(params.newValue === 'username') {...}
+ });
+ **/
+ this.$div.triggerHandler('save', {newValue: newValue, submitValue: submitValue, response: response});
+ }, this))
+ .fail($.proxy(function(xhr) {
+ this.isSaving = false;
+
+ var msg;
+ if(typeof this.options.error === 'function') {
+ msg = this.options.error.call(this.options.scope, xhr, newValue);
+ } else {
+ msg = typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!';
+ }
+
+ this.error(msg);
+ this.showForm();
+ }, this));
+ },
+
+ save: function(submitValue) {
+ //try parse composite pk defined as json string in data-pk
+ this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true);
+
+ var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk,
+ /*
+ send on server in following cases:
+ 1. url is function
+ 2. url is string AND (pk defined OR send option = always)
+ */
+ send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk !== null && pk !== undefined)))),
+ params;
+
+ if (send) { //send to server
+ this.showLoading();
+
+ //standard params
+ params = {
+ name: this.options.name || '',
+ value: submitValue,
+ pk: pk
+ };
+
+ //additional params
+ if(typeof this.options.params === 'function') {
+ params = this.options.params.call(this.options.scope, params);
+ } else {
+ //try parse json in single quotes (from data-params attribute)
+ this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true);
+ $.extend(params, this.options.params);
+ }
+
+ if(typeof this.options.url === 'function') { //user's function
+ return this.options.url.call(this.options.scope, params);
+ } else {
+ //send ajax to server and return deferred object
+ return $.ajax($.extend({
+ url : this.options.url,
+ data : params,
+ type : 'POST'
+ }, this.options.ajaxOptions));
+ }
+ }
+ },
+
+ validate: function (value) {
+ if (value === undefined) {
+ value = this.value;
+ }
+ if (typeof this.options.validate === 'function') {
+ return this.options.validate.call(this.options.scope, value);
+ }
+ },
+
+ option: function(key, value) {
+ if(key in this.options) {
+ this.options[key] = value;
+ }
+
+ if(key === 'value') {
+ this.setValue(value);
+ }
+
+ //do not pass option to input as it is passed in editable-element
+ },
+
+ setValue: function(value, convertStr) {
+ if(convertStr) {
+ this.value = this.input.str2value(value);
+ } else {
+ this.value = value;
+ }
+
+ //if form is visible, update input
+ if(this.$form && this.$form.is(':visible')) {
+ this.input.value2input(this.value);
+ }
+ }
+ };
+
+ /*
+ Initialize editableform. Applied to jQuery object.
+
+ @method $().editableform(options)
+ @params {Object} options
+ @example
+ var $form = $('&lt;div&gt;').editableform({
+ type: 'text',
+ name: 'username',
+ url: '/post',
+ value: 'vitaliy'
+ });
+
+ //to display form you should call 'render' method
+ $form.editableform('render');
+ */
+ $.fn.editableform = function (option) {
+ var args = arguments;
+ return this.each(function () {
+ var $this = $(this),
+ data = $this.data('editableform'),
+ options = typeof option === 'object' && option;
+ if (!data) {
+ $this.data('editableform', (data = new EditableForm(this, options)));
+ }
+
+ if (typeof option === 'string') { //call method
+ data[option].apply(data, Array.prototype.slice.call(args, 1));
+ }
+ });
+ };
+
+ //keep link to constructor to allow inheritance
+ $.fn.editableform.Constructor = EditableForm;
+
+ //defaults
+ $.fn.editableform.defaults = {
+ /* see also defaults for input */
+
+ /**
+ Type of input. Can be <code>text|textarea|select|date|checklist</code>
+
+ @property type
+ @type string
+ @default 'text'
+ **/
+ type: 'text',
+ /**
+ Url for submit, e.g. <code>'/post'</code>
+ If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks.
+
+ @property url
+ @type string|function
+ @default null
+ @example
+ url: function(params) {
+ var d = new $.Deferred;
+ if(params.value === 'abc') {
+ return d.reject('error message'); //returning error via deferred object
+ } else {
+ //async saving data in js model
+ someModel.asyncSaveMethod({
+ ...,
+ success: function(){
+ d.resolve();
+ }
+ });
+ return d.promise();
+ }
+ }
+ **/
+ url:null,
+ /**
+ Additional params for submit. If defined as <code>object</code> - it is **appended** to original ajax data (pk, name and value).
+ If defined as <code>function</code> - returned object **overwrites** original ajax data.
+ @example
+ params: function(params) {
+ //originally params contain pk, name and value
+ params.a = 1;
+ return params;
+ }
+
+ @property params
+ @type object|function
+ @default null
+ **/
+ params:null,
+ /**
+ Name of field. Will be submitted on server. Can be taken from <code>id</code> attribute
+
+ @property name
+ @type string
+ @default null
+ **/
+ name: null,
+ /**
+ Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. <code>{id: 1, lang: 'en'}</code>.
+ Can be calculated dynamically via function.
+
+ @property pk
+ @type string|object|function
+ @default null
+ **/
+ pk: null,
+ /**
+ Initial value. If not defined - will be taken from element's content.
+ For __select__ type should be defined (as it is ID of shown text).
+
+ @property value
+ @type string|object
+ @default null
+ **/
+ value: null,
+ /**
+ Value that will be displayed in input if original field value is empty (`null|undefined|''`).
+
+ @property defaultValue
+ @type string|object
+ @default null
+ @since 1.4.6
+ **/
+ defaultValue: null,
+ /**
+ Strategy for sending data on server. Can be `auto|always|never`.
+ When 'auto' data will be sent on server **only if pk and url defined**, otherwise new value will be stored locally.
+
+ @property send
+ @type string
+ @default 'auto'
+ **/
+ send: 'auto',
+ /**
+ Function for client-side validation. If returns string - means validation not passed and string showed as error.
+ Since 1.5.1 you can modify submitted value by returning object from `validate`:
+ `{newValue: '...'}` or `{newValue: '...', msg: '...'}`
+
+ @property validate
+ @type function
+ @default null
+ @example
+ validate: function(value) {
+ if($.trim(value) == '') {
+ return 'This field is required';
+ }
+ }
+ **/
+ validate: null,
+ /**
+ Success callback. Called when value successfully sent on server and **response status = 200**.
+ Usefull to work with json response. For example, if your backend response can be <code>{success: true}</code>
+ or <code>{success: false, msg: "server error"}</code> you can check it inside this callback.
+ If it returns **string** - means error occured and string is shown as error message.
+ If it returns **object like** <code>{newValue: &lt;something&gt;}</code> - it overwrites value, submitted by user.
+ Otherwise newValue simply rendered into element.
+
+ @property success
+ @type function
+ @default null
+ @example
+ success: function(response, newValue) {
+ if(!response.success) return response.msg;
+ }
+ **/
+ success: null,
+ /**
+ Error callback. Called when request failed (response status != 200).
+ Usefull when you want to parse error response and display a custom message.
+ Must return **string** - the message to be displayed in the error block.
+
+ @property error
+ @type function
+ @default null
+ @since 1.4.4
+ @example
+ error: function(response, newValue) {
+ if(response.status === 500) {
+ return 'Service unavailable. Please try later.';
+ } else {
+ return response.responseText;
+ }
+ }
+ **/
+ error: null,
+ /**
+ Additional options for submit ajax request.
+ List of values: http://api.jquery.com/jQuery.ajax
+
+ @property ajaxOptions
+ @type object
+ @default null
+ @since 1.1.1
+ @example
+ ajaxOptions: {
+ type: 'put',
+ dataType: 'json'
+ }
+ **/
+ ajaxOptions: null,
+ /**
+ Where to show buttons: left(true)|bottom|false
+ Form without buttons is auto-submitted.
+
+ @property showbuttons
+ @type boolean|string
+ @default true
+ @since 1.1.1
+ **/
+ showbuttons: true,
+ /**
+ Scope for callback methods (success, validate).
+ If <code>null</code> means editableform instance itself.
+
+ @property scope
+ @type DOMElement|object
+ @default null
+ @since 1.2.0
+ @private
+ **/
+ scope: null,
+ /**
+ Whether to save or cancel value when it was not changed but form was submitted
+
+ @property savenochange
+ @type boolean
+ @default false
+ @since 1.2.0
+ **/
+ savenochange: false
+ };
+
+ /*
+ Note: following params could redefined in engine: bootstrap or jqueryui:
+ Classes 'control-group' and 'editable-error-block' must always present!
+ */
+ $.fn.editableform.template = '<form class="form-inline editableform">'+
+ '<div class="control-group">' +
+ '<div><div class="editable-input"></div><div class="editable-buttons"></div></div>'+
+ '<div class="editable-error-block"></div>' +
+ '</div>' +
+ '</form>';
+
+ //loading div
+ $.fn.editableform.loading = '<div class="editableform-loading"></div>';
+
+ //buttons
+ $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+
+ '<button type="button" class="editable-cancel">cancel</button>';
+
+ //error class attached to control-group
+ $.fn.editableform.errorGroupClass = null;
+
+ //error class attached to editable-error-block
+ $.fn.editableform.errorBlockClass = 'editable-error';
+
+ //engine
+ $.fn.editableform.engine = 'jquery';
+}(window.jQuery));
+
+/**
+* EditableForm utilites
+*/
+(function ($) {
+ "use strict";
+
+ //utils
+ $.fn.editableutils = {
+ /**
+ * classic JS inheritance function
+ */
+ inherit: function (Child, Parent) {
+ var F = function() { };
+ F.prototype = Parent.prototype;
+ Child.prototype = new F();
+ Child.prototype.constructor = Child;
+ Child.superclass = Parent.prototype;
+ },
+
+ /**
+ * set caret position in input
+ * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
+ */
+ setCursorPosition: function(elem, pos) {
+ if (elem.setSelectionRange) {
+ elem.setSelectionRange(pos, pos);
+ } else if (elem.createTextRange) {
+ var range = elem.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', pos);
+ range.moveStart('character', pos);
+ range.select();
+ }
+ },
+
+ /**
+ * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes)
+ * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}">
+ * safe = true --> means no exception will be thrown
+ * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
+ */
+ tryParseJson: function(s, safe) {
+ if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
+ if (safe) {
+ try {
+ /*jslint evil: true*/
+ s = (new Function('return ' + s))();
+ /*jslint evil: false*/
+ } catch (e) {} finally {
+ return s;
+ }
+ } else {
+ /*jslint evil: true*/
+ s = (new Function('return ' + s))();
+ /*jslint evil: false*/
+ }
+ }
+ return s;
+ },
+
+ /**
+ * slice object by specified keys
+ */
+ sliceObj: function(obj, keys, caseSensitive /* default: false */) {
+ var key, keyLower, newObj = {};
+
+ if (!$.isArray(keys) || !keys.length) {
+ return newObj;
+ }
+
+ for (var i = 0; i < keys.length; i++) {
+ key = keys[i];
+ if (obj.hasOwnProperty(key)) {
+ newObj[key] = obj[key];
+ }
+
+ if(caseSensitive === true) {
+ continue;
+ }
+
+ //when getting data-* attributes via $.data() it's converted to lowercase.
+ //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery
+ //workaround is code below.
+ keyLower = key.toLowerCase();
+ if (obj.hasOwnProperty(keyLower)) {
+ newObj[key] = obj[keyLower];
+ }
+ }
+
+ return newObj;
+ },
+
+ /*
+ exclude complex objects from $.data() before pass to config
+ */
+ getConfigData: function($element) {
+ var data = {};
+ $.each($element.data(), function(k, v) {
+ if(typeof v !== 'object' || (v && typeof v === 'object' && (v.constructor === Object || v.constructor === Array))) {
+ data[k] = v;
+ }
+ });
+ return data;
+ },
+
+ /*
+ returns keys of object
+ */
+ objectKeys: function(o) {
+ if (Object.keys) {
+ return Object.keys(o);
+ } else {
+ if (o !== Object(o)) {
+ throw new TypeError('Object.keys called on a non-object');
+ }
+ var k=[], p;
+ for (p in o) {
+ if (Object.prototype.hasOwnProperty.call(o,p)) {
+ k.push(p);
+ }
+ }
+ return k;
+ }
+
+ },
+
+ /**
+ method to escape html.
+ **/
+ escape: function(str) {
+ return $('<div>').text(str).html();
+ },
+
+ /*
+ returns array items from sourceData having value property equal or inArray of 'value'
+ */
+ itemsByValue: function(value, sourceData, valueProp) {
+ if(!sourceData || value === null) {
+ return [];
+ }
+
+ if (typeof(valueProp) !== "function") {
+ var idKey = valueProp || 'value';
+ valueProp = function (e) { return e[idKey]; };
+ }
+
+ var isValArray = $.isArray(value),
+ result = [],
+ that = this;
+
+ $.each(sourceData, function(i, o) {
+ if(o.children) {
+ result = result.concat(that.itemsByValue(value, o.children, valueProp));
+ } else {
+ /*jslint eqeq: true*/
+ if(isValArray) {
+ if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? valueProp(o) : o); }).length) {
+ result.push(o);
+ }
+ } else {
+ var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o;
+ if(value == itemValue) {
+ result.push(o);
+ }
+ }
+ /*jslint eqeq: false*/
+ }
+ });
+
+ return result;
+ },
+
+ /*
+ Returns input by options: type, mode.
+ */
+ createInput: function(options) {
+ var TypeConstructor, typeOptions, input,
+ type = options.type;
+
+ //`date` is some kind of virtual type that is transformed to one of exact types
+ //depending on mode and core lib
+ if(type === 'date') {
+ //inline
+ if(options.mode === 'inline') {
+ if($.fn.editabletypes.datefield) {
+ type = 'datefield';
+ } else if($.fn.editabletypes.dateuifield) {
+ type = 'dateuifield';
+ }
+ //popup
+ } else {
+ if($.fn.editabletypes.date) {
+ type = 'date';
+ } else if($.fn.editabletypes.dateui) {
+ type = 'dateui';
+ }
+ }
+
+ //if type still `date` and not exist in types, replace with `combodate` that is base input
+ if(type === 'date' && !$.fn.editabletypes.date) {
+ type = 'combodate';
+ }
+ }
+
+ //`datetime` should be datetimefield in 'inline' mode
+ if(type === 'datetime' && options.mode === 'inline') {
+ type = 'datetimefield';
+ }
+
+ //change wysihtml5 to textarea for jquery UI and plain versions
+ if(type === 'wysihtml5' && !$.fn.editabletypes[type]) {
+ type = 'textarea';
+ }
+
+ //create input of specified type. Input will be used for converting value, not in form
+ if(typeof $.fn.editabletypes[type] === 'function') {
+ TypeConstructor = $.fn.editabletypes[type];
+ typeOptions = this.sliceObj(options, this.objectKeys(TypeConstructor.defaults));
+ input = new TypeConstructor(typeOptions);
+ return input;
+ } else {
+ $.error('Unknown type: '+ type);
+ return false;
+ }
+ },
+
+ //see http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
+ supportsTransitions: function () {
+ var b = document.body || document.documentElement,
+ s = b.style,
+ p = 'transition',
+ v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
+
+ if(typeof s[p] === 'string') {
+ return true;
+ }
+
+ // Tests for vendor specific prop
+ p = p.charAt(0).toUpperCase() + p.substr(1);
+ for(var i=0; i<v.length; i++) {
+ if(typeof s[v[i] + p] === 'string') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ };
+}(window.jQuery));
+
+/**
+Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
+This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
+Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br>
+Applied as jQuery method.
+
+@class editableContainer
+@uses editableform
+**/
+(function ($) {
+ "use strict";
+
+ var Popup = function (element, options) {
+ this.init(element, options);
+ };
+
+ var Inline = function (element, options) {
+ this.init(element, options);
+ };
+
+ //methods
+ Popup.prototype = {
+ containerName: null, //method to call container on element
+ containerDataName: null, //object name in element's .data()
+ innerCss: null, //tbd in child class
+ containerClass: 'editable-container editable-popup', //css class applied to container element
+ defaults: {}, //container itself defaults
+
+ init: function(element, options) {
+ this.$element = $(element);
+ //since 1.4.1 container do not use data-* directly as they already merged into options.
+ this.options = $.extend({}, $.fn.editableContainer.defaults, options);
+ this.splitOptions();
+
+ //set scope of form callbacks to element
+ this.formOptions.scope = this.$element[0];
+
+ this.initContainer();
+
+ //flag to hide container, when saving value will finish
+ this.delayedHide = false;
+
+ //bind 'destroyed' listener to destroy container when element is removed from dom
+ this.$element.on('destroyed', $.proxy(function(){
+ this.destroy();
+ }, this));
+
+ //attach document handler to close containers on click / escape
+ if(!$(document).data('editable-handlers-attached')) {
+ //close all on escape
+ $(document).on('keyup.editable', function (e) {
+ if (e.which === 27) {
+ $('.editable-open').editableContainer('hide');
+ //todo: return focus on element
+ }
+ });
+
+ //close containers when click outside
+ //(mousedown could be better than click, it closes everything also on drag drop)
+ $(document).on('click.editable', function(e) {
+ var $target = $(e.target), i,
+ exclude_classes = ['.editable-container',
+ '.ui-datepicker-header',
+ '.datepicker', //in inline mode datepicker is rendered into body
+ '.modal-backdrop',
+ '.bootstrap-wysihtml5-insert-image-modal',
+ '.bootstrap-wysihtml5-insert-link-modal'
+ ];
+
+ //check if element is detached. It occurs when clicking in bootstrap datepicker
+ if (!$.contains(document.documentElement, e.target)) {
+ return;
+ }
+
+ //for some reason FF 20 generates extra event (click) in select2 widget with e.target = document
+ //we need to filter it via construction below. See https://github.com/vitalets/x-editable/issues/199
+ //Possibly related to http://stackoverflow.com/questions/10119793/why-does-firefox-react-differently-from-webkit-and-ie-to-click-event-on-selec
+ if($target.is(document)) {
+ return;
+ }
+
+ //if click inside one of exclude classes --> no nothing
+ for(i=0; i<exclude_classes.length; i++) {
+ if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
+ return;
+ }
+ }
+
+ //close all open containers (except one - target)
+ Popup.prototype.closeOthers(e.target);
+ });
+
+ $(document).data('editable-handlers-attached', true);
+ }
+ },
+
+ //split options on containerOptions and formOptions
+ splitOptions: function() {
+ this.containerOptions = {};
+ this.formOptions = {};
+
+ if(!$.fn[this.containerName]) {
+ throw new Error(this.containerName + ' not found. Have you included corresponding js file?');
+ }
+
+ //keys defined in container defaults go to container, others go to form
+ for(var k in this.options) {
+ if(k in this.defaults) {
+ this.containerOptions[k] = this.options[k];
+ } else {
+ this.formOptions[k] = this.options[k];
+ }
+ }
+ },
+
+ /*
+ Returns jquery object of container
+ @method tip()
+ */
+ tip: function() {
+ return this.container() ? this.container().$tip : null;
+ },
+
+ /* returns container object */
+ container: function() {
+ var container;
+ //first, try get it by `containerDataName`
+ if(this.containerDataName) {
+ if(container = this.$element.data(this.containerDataName)) {
+ return container;
+ }
+ }
+ //second, try `containerName`
+ container = this.$element.data(this.containerName);
+ return container;
+ },
+
+ /* call native method of underlying container, e.g. this.$element.popover('method') */
+ call: function() {
+ this.$element[this.containerName].apply(this.$element, arguments);
+ },
+
+ initContainer: function(){
+ this.call(this.containerOptions);
+ },
+
+ renderForm: function() {
+ this.$form
+ .editableform(this.formOptions)
+ .on({
+ save: $.proxy(this.save, this), //click on submit button (value changed)
+ nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
+ cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
+ show: $.proxy(function() {
+ if(this.delayedHide) {
+ this.hide(this.delayedHide.reason);
+ this.delayedHide = false;
+ } else {
+ this.setPosition();
+ }
+ }, this), //re-position container every time form is shown (occurs each time after loading state)
+ rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
+ resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
+ rendered: $.proxy(function(){
+ /**
+ Fired when container is shown and form is rendered (for select will wait for loading dropdown options).
+ **Note:** Bootstrap popover has own `shown` event that now cannot be separated from x-editable's one.
+ The workaround is to check `arguments.length` that is always `2` for x-editable.
+
+ @event shown
+ @param {Object} event event object
+ @example
+ $('#username').on('shown', function(e, editable) {
+ editable.input.$input.val('overwriting value of input..');
+ });
+ **/
+ /*
+ TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events.
+ */
+ this.$element.triggerHandler('shown', $(this.options.scope).data('editable'));
+ }, this)
+ })
+ .editableform('render');
+ },
+
+ /**
+ Shows container with form
+ @method show()
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
+ **/
+ /* Note: poshytip owerwrites this method totally! */
+ show: function (closeAll) {
+ this.$element.addClass('editable-open');
+ if(closeAll !== false) {
+ //close all open containers (except this)
+ this.closeOthers(this.$element[0]);
+ }
+
+ //show container itself
+ this.innerShow();
+ this.tip().addClass(this.containerClass);
+
+ /*
+ Currently, form is re-rendered on every show.
+ The main reason is that we dont know, what will container do with content when closed:
+ remove(), detach() or just hide() - it depends on container.
+
+ Detaching form itself before hide and re-insert before show is good solution,
+ but visually it looks ugly --> container changes size before hide.
+ */
+
+ //if form already exist - delete previous data
+ if(this.$form) {
+ //todo: destroy prev data!
+ //this.$form.destroy();
+ }
+
+ this.$form = $('<div>');
+
+ //insert form into container body
+ if(this.tip().is(this.innerCss)) {
+ //for inline container
+ this.tip().append(this.$form);
+ } else {
+ this.tip().find(this.innerCss).append(this.$form);
+ }
+
+ //render form
+ this.renderForm();
+ },
+
+ /**
+ Hides container with form
+ @method hide()
+ @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
+ **/
+ hide: function(reason) {
+ if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
+ return;
+ }
+
+ //if form is saving value, schedule hide
+ if(this.$form.data('editableform').isSaving) {
+ this.delayedHide = {reason: reason};
+ return;
+ } else {
+ this.delayedHide = false;
+ }
+
+ this.$element.removeClass('editable-open');
+ this.innerHide();
+
+ /**
+ Fired when container was hidden. It occurs on both save or cancel.
+ **Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
+ The workaround is to check `arguments.length` that is always `2` for x-editable.
+
+ @event hidden
+ @param {object} event event object
+ @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|manual</code>
+ @example
+ $('#username').on('hidden', function(e, reason) {
+ if(reason === 'save' || reason === 'cancel') {
+ //auto-open next editable
+ $(this).closest('tr').next().find('.editable').editable('show');
+ }
+ });
+ **/
+ this.$element.triggerHandler('hidden', reason || 'manual');
+ },
+
+ /* internal show method. To be overwritten in child classes */
+ innerShow: function () {
+
+ },
+
+ /* internal hide method. To be overwritten in child classes */
+ innerHide: function () {
+
+ },
+
+ /**
+ Toggles container visibility (show / hide)
+ @method toggle()
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
+ **/
+ toggle: function(closeAll) {
+ if(this.container() && this.tip() && this.tip().is(':visible')) {
+ this.hide();
+ } else {
+ this.show(closeAll);
+ }
+ },
+
+ /*
+ Updates the position of container when content changed.
+ @method setPosition()
+ */
+ setPosition: function() {
+ //tbd in child class
+ },
+
+ save: function(e, params) {
+ /**
+ Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
+
+ @event save
+ @param {Object} event event object
+ @param {Object} params additional params
+ @param {mixed} params.newValue submitted value
+ @param {Object} params.response ajax response
+ @example
+ $('#username').on('save', function(e, params) {
+ //assuming server response: '{success: true}'
+ var pk = $(this).data('editableContainer').options.pk;
+ if(params.response && params.response.success) {
+ alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
+ } else {
+ alert('error!');
+ }
+ });
+ **/
+ this.$element.triggerHandler('save', params);
+
+ //hide must be after trigger, as saving value may require methods of plugin, applied to input
+ this.hide('save');
+ },
+
+ /**
+ Sets new option
+
+ @method option(key, value)
+ @param {string} key
+ @param {mixed} value
+ **/
+ option: function(key, value) {
+ this.options[key] = value;
+ if(key in this.containerOptions) {
+ this.containerOptions[key] = value;
+ this.setContainerOption(key, value);
+ } else {
+ this.formOptions[key] = value;
+ if(this.$form) {
+ this.$form.editableform('option', key, value);
+ }
+ }
+ },
+
+ setContainerOption: function(key, value) {
+ this.call('option', key, value);
+ },
+
+ /**
+ Destroys the container instance
+ @method destroy()
+ **/
+ destroy: function() {
+ this.hide();
+ this.innerDestroy();
+ this.$element.off('destroyed');
+ this.$element.removeData('editableContainer');
+ },
+
+ /* to be overwritten in child classes */
+ innerDestroy: function() {
+
+ },
+
+ /*
+ Closes other containers except one related to passed element.
+ Other containers can be cancelled or submitted (depends on onblur option)
+ */
+ closeOthers: function(element) {
+ $('.editable-open').each(function(i, el){
+ //do nothing with passed element and it's children
+ if(el === element || $(el).find(element).length) {
+ return;
+ }
+
+ //otherwise cancel or submit all open containers
+ var $el = $(el),
+ ec = $el.data('editableContainer');
+
+ if(!ec) {
+ return;
+ }
+
+ if(ec.options.onblur === 'cancel') {
+ $el.data('editableContainer').hide('onblur');
+ } else if(ec.options.onblur === 'submit') {
+ $el.data('editableContainer').tip().find('form').submit();
+ }
+ });
+
+ },
+
+ /**
+ Activates input of visible container (e.g. set focus)
+ @method activate()
+ **/
+ activate: function() {
+ if(this.tip && this.tip().is(':visible') && this.$form) {
+ this.$form.data('editableform').input.activate();
+ }
+ }
+
+ };
+
+ /**
+ jQuery method to initialize editableContainer.
+
+ @method $().editableContainer(options)
+ @params {Object} options
+ @example
+ $('#edit').editableContainer({
+ type: 'text',
+ url: '/post',
+ pk: 1,
+ value: 'hello'
+ });
+ **/
+ $.fn.editableContainer = function (option) {
+ var args = arguments;
+ return this.each(function () {
+ var $this = $(this),
+ dataKey = 'editableContainer',
+ data = $this.data(dataKey),
+ options = typeof option === 'object' && option,
+ Constructor = (options.mode === 'inline') ? Inline : Popup;
+
+ if (!data) {
+ $this.data(dataKey, (data = new Constructor(this, options)));
+ }
+
+ if (typeof option === 'string') { //call method
+ data[option].apply(data, Array.prototype.slice.call(args, 1));
+ }
+ });
+ };
+
+ //store constructors
+ $.fn.editableContainer.Popup = Popup;
+ $.fn.editableContainer.Inline = Inline;
+
+ //defaults
+ $.fn.editableContainer.defaults = {
+ /**
+ Initial value of form input
+
+ @property value
+ @type mixed
+ @default null
+ @private
+ **/
+ value: null,
+ /**
+ Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container.
+
+ @property placement
+ @type string
+ @default 'top'
+ **/
+ placement: 'top',
+ /**
+ Whether to hide container on save/cancel.
+
+ @property autohide
+ @type boolean
+ @default true
+ @private
+ **/
+ autohide: true,
+ /**
+ Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.
+ Setting <code>ignore</code> allows to have several containers open.
+
+ @property onblur
+ @type string
+ @default 'cancel'
+ @since 1.1.1
+ **/
+ onblur: 'cancel',
+
+ /**
+ Animation speed (inline mode only)
+ @property anim
+ @type string
+ @default false
+ **/
+ anim: false,
+
+ /**
+ Mode of editable, can be `popup` or `inline`
+
+ @property mode
+ @type string
+ @default 'popup'
+ @since 1.4.0
+ **/
+ mode: 'popup'
+ };
+
+ /*
+ * workaround to have 'destroyed' event to destroy popover when element is destroyed
+ * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
+ */
+ jQuery.event.special.destroyed = {
+ remove: function(o) {
+ if (o.handler) {
+ o.handler();
+ }
+ }
+ };
+
+}(window.jQuery));
+
+/**
+* Editable Inline
+* ---------------------
+*/
+(function ($) {
+ "use strict";
+
+ //copy prototype from EditableContainer
+ //extend methods
+ $.extend($.fn.editableContainer.Inline.prototype, $.fn.editableContainer.Popup.prototype, {
+ containerName: 'editableform',
+ innerCss: '.editable-inline',
+ containerClass: 'editable-container editable-inline', //css class applied to container element
+
+ initContainer: function(){
+ //container is <span> element
+ this.$tip = $('<span></span>');
+
+ //convert anim to miliseconds (int)
+ if(!this.options.anim) {
+ this.options.anim = 0;
+ }
+ },
+
+ splitOptions: function() {
+ //all options are passed to form
+ this.containerOptions = {};
+ this.formOptions = this.options;
+ },
+
+ tip: function() {
+ return this.$tip;
+ },
+
+ innerShow: function () {
+ this.$element.hide();
+ this.tip().insertAfter(this.$element).show();
+ },
+
+ innerHide: function () {
+ this.$tip.hide(this.options.anim, $.proxy(function() {
+ this.$element.show();
+ this.innerDestroy();
+ }, this));
+ },
+
+ innerDestroy: function() {
+ if(this.tip()) {
+ this.tip().empty().remove();
+ }
+ }
+ });
+
+}(window.jQuery));
+/**
+Makes editable any HTML element on the page. Applied as jQuery method.
+
+@class editable
+@uses editableContainer
+**/
+(function ($) {
+ "use strict";
+
+ var Editable = function (element, options) {
+ this.$element = $(element);
+ //data-* has more priority over js options: because dynamically created elements may change data-*
+ this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element));
+ if(this.options.selector) {
+ this.initLive();
+ } else {
+ this.init();
+ }
+
+ //check for transition support
+ if(this.options.highlight && !$.fn.editableutils.supportsTransitions()) {
+ this.options.highlight = false;
+ }
+ };
+
+ Editable.prototype = {
+ constructor: Editable,
+ init: function () {
+ var isValueByText = false,
+ doAutotext, finalize;
+
+ //name
+ this.options.name = this.options.name || this.$element.attr('id');
+
+ //create input of specified type. Input needed already here to convert value for initial display (e.g. show text by id for select)
+ //also we set scope option to have access to element inside input specific callbacks (e. g. source as function)
+ this.options.scope = this.$element[0];
+ this.input = $.fn.editableutils.createInput(this.options);
+ if(!this.input) {
+ return;
+ }
+
+ //set value from settings or by element's text
+ if (this.options.value === undefined || this.options.value === null) {
+ this.value = this.input.html2value($.trim(this.$element.html()));
+ isValueByText = true;
+ } else {
+ /*
+ value can be string when received from 'data-value' attribute
+ for complext objects value can be set as json string in data-value attribute,
+ e.g. data-value="{city: 'Moscow', street: 'Lenina'}"
+ */
+ this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true);
+ if(typeof this.options.value === 'string') {
+ this.value = this.input.str2value(this.options.value);
+ } else {
+ this.value = this.options.value;
+ }
+ }
+
+ //add 'editable' class to every editable element
+ this.$element.addClass('editable');
+
+ //specifically for "textarea" add class .editable-pre-wrapped to keep linebreaks
+ if(this.input.type === 'textarea') {
+ this.$element.addClass('editable-pre-wrapped');
+ }
+
+ //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
+ if(this.options.toggle !== 'manual') {
+ this.$element.addClass('editable-click');
+ this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
+ //prevent following link if editable enabled
+ if(!this.options.disabled) {
+ e.preventDefault();
+ }
+
+ //stop propagation not required because in document click handler it checks event target
+ //e.stopPropagation();
+
+ if(this.options.toggle === 'mouseenter') {
+ //for hover only show container
+ this.show();
+ } else {
+ //when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
+ var closeAll = (this.options.toggle !== 'click');
+ this.toggle(closeAll);
+ }
+ }, this));
+ } else {
+ this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
+ }
+
+ //if display is function it's far more convinient to have autotext = always to render correctly on init
+ //see https://github.com/vitalets/x-editable-yii/issues/34
+ if(typeof this.options.display === 'function') {
+ this.options.autotext = 'always';
+ }
+
+ //check conditions for autotext:
+ switch(this.options.autotext) {
+ case 'always':
+ doAutotext = true;
+ break;
+ case 'auto':
+ //if element text is empty and value is defined and value not generated by text --> run autotext
+ doAutotext = !$.trim(this.$element.text()).length && this.value !== null && this.value !== undefined && !isValueByText;
+ break;
+ default:
+ doAutotext = false;
+ }
+
+ //depending on autotext run render() or just finilize init
+ $.when(doAutotext ? this.render() : true).then($.proxy(function() {
+ if(this.options.disabled) {
+ this.disable();
+ } else {
+ this.enable();
+ }
+ /**
+ Fired when element was initialized by `$().editable()` method.
+ Please note that you should setup `init` handler **before** applying `editable`.
+
+ @event init
+ @param {Object} event event object
+ @param {Object} editable editable instance (as here it cannot accessed via data('editable'))
+ @since 1.2.0
+ @example
+ $('#username').on('init', function(e, editable) {
+ alert('initialized ' + editable.options.name);
+ });
+ $('#username').editable();
+ **/
+ this.$element.triggerHandler('init', this);
+ }, this));
+ },
+
+ /*
+ Initializes parent element for live editables
+ */
+ initLive: function() {
+ //store selector
+ var selector = this.options.selector;
+ //modify options for child elements
+ this.options.selector = false;
+ this.options.autotext = 'never';
+ //listen toggle events
+ this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
+ var $target = $(e.target);
+ if(!$target.data('editable')) {
+ //if delegated element initially empty, we need to clear it's text (that was manually set to `empty` by user)
+ //see https://github.com/vitalets/x-editable/issues/137
+ if($target.hasClass(this.options.emptyclass)) {
+ $target.empty();
+ }
+ $target.editable(this.options).trigger(e);
+ }
+ }, this));
+ },
+
+ /*
+ Renders value into element's text.
+ Can call custom display method from options.
+ Can return deferred object.
+ @method render()
+ @param {mixed} response server response (if exist) to pass into display function
+ */
+ render: function(response) {
+ //do not display anything
+ if(this.options.display === false) {
+ return;
+ }
+
+ //if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded
+ if(this.input.value2htmlFinal) {
+ return this.input.value2html(this.value, this.$element[0], this.options.display, response);
+ //if display method defined --> use it
+ } else if(typeof this.options.display === 'function') {
+ return this.options.display.call(this.$element[0], this.value, response);
+ //else use input's original value2html() method
+ } else {
+ return this.input.value2html(this.value, this.$element[0]);
+ }
+ },
+
+ /**
+ Enables editable
+ @method enable()
+ **/
+ enable: function() {
+ this.options.disabled = false;
+ this.$element.removeClass('editable-disabled');
+ this.handleEmpty(this.isEmpty);
+ if(this.options.toggle !== 'manual') {
+ if(this.$element.attr('tabindex') === '-1') {
+ this.$element.removeAttr('tabindex');
+ }
+ }
+ },
+
+ /**
+ Disables editable
+ @method disable()
+ **/
+ disable: function() {
+ this.options.disabled = true;
+ this.hide();
+ this.$element.addClass('editable-disabled');
+ this.handleEmpty(this.isEmpty);
+ //do not stop focus on this element
+ this.$element.attr('tabindex', -1);
+ },
+
+ /**
+ Toggles enabled / disabled state of editable element
+ @method toggleDisabled()
+ **/
+ toggleDisabled: function() {
+ if(this.options.disabled) {
+ this.enable();
+ } else {
+ this.disable();
+ }
+ },
+
+ /**
+ Sets new option
+
+ @method option(key, value)
+ @param {string|object} key option name or object with several options
+ @param {mixed} value option new value
+ @example
+ $('.editable').editable('option', 'pk', 2);
+ **/
+ option: function(key, value) {
+ //set option(s) by object
+ if(key && typeof key === 'object') {
+ $.each(key, $.proxy(function(k, v){
+ this.option($.trim(k), v);
+ }, this));
+ return;
+ }
+
+ //set option by string
+ this.options[key] = value;
+
+ //disabled
+ if(key === 'disabled') {
+ return value ? this.disable() : this.enable();
+ }
+
+ //value
+ if(key === 'value') {
+ this.setValue(value);
+ }
+
+ //transfer new option to container!
+ if(this.container) {
+ this.container.option(key, value);
+ }
+
+ //pass option to input directly (as it points to the same in form)
+ if(this.input.option) {
+ this.input.option(key, value);
+ }
+
+ },
+
+ /*
+ * set emptytext if element is empty
+ */
+ handleEmpty: function (isEmpty) {
+ //do not handle empty if we do not display anything
+ if(this.options.display === false) {
+ return;
+ }
+
+ /*
+ isEmpty may be set directly as param of method.
+ It is required when we enable/disable field and can't rely on content
+ as node content is text: "Empty" that is not empty %)
+ */
+ if(isEmpty !== undefined) {
+ this.isEmpty = isEmpty;
+ } else {
+ //detect empty
+ //for some inputs we need more smart check
+ //e.g. wysihtml5 may have <br>, <p></p>, <img>
+ if(typeof(this.input.isEmpty) === 'function') {
+ this.isEmpty = this.input.isEmpty(this.$element);
+ } else {
+ this.isEmpty = $.trim(this.$element.html()) === '';
+ }
+ }
+
+ //emptytext shown only for enabled
+ if(!this.options.disabled) {
+ if (this.isEmpty) {
+ this.$element.html(this.options.emptytext);
+ if(this.options.emptyclass) {
+ this.$element.addClass(this.options.emptyclass);
+ }
+ } else if(this.options.emptyclass) {
+ this.$element.removeClass(this.options.emptyclass);
+ }
+ } else {
+ //below required if element disable property was changed
+ if(this.isEmpty) {
+ this.$element.empty();
+ if(this.options.emptyclass) {
+ this.$element.removeClass(this.options.emptyclass);
+ }
+ }
+ }
+ },
+
+ /**
+ Shows container with form
+ @method show()
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
+ **/
+ show: function (closeAll) {
+ if(this.options.disabled) {
+ return;
+ }
+
+ //init editableContainer: popover, tooltip, inline, etc..
+ if(!this.container) {
+ var containerOptions = $.extend({}, this.options, {
+ value: this.value,
+ input: this.input //pass input to form (as it is already created)
+ });
+ this.$element.editableContainer(containerOptions);
+ //listen `save` event
+ this.$element.on("save.internal", $.proxy(this.save, this));
+ this.container = this.$element.data('editableContainer');
+ } else if(this.container.tip().is(':visible')) {
+ return;
+ }
+
+ //show container
+ this.container.show(closeAll);
+ },
+
+ /**
+ Hides container with form
+ @method hide()
+ **/
+ hide: function () {
+ if(this.container) {
+ this.container.hide();
+ }
+ },
+
+ /**
+ Toggles container visibility (show / hide)
+ @method toggle()
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
+ **/
+ toggle: function(closeAll) {
+ if(this.container && this.container.tip().is(':visible')) {
+ this.hide();
+ } else {
+ this.show(closeAll);
+ }
+ },
+
+ /*
+ * called when form was submitted
+ */
+ save: function(e, params) {
+ //mark element with unsaved class if needed
+ if(this.options.unsavedclass) {
+ /*
+ Add unsaved css to element if:
+ - url is not user's function
+ - value was not sent to server
+ - params.response === undefined, that means data was not sent
+ - value changed
+ */
+ var sent = false;
+ sent = sent || typeof this.options.url === 'function';
+ sent = sent || this.options.display === false;
+ sent = sent || params.response !== undefined;
+ sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue));
+
+ if(sent) {
+ this.$element.removeClass(this.options.unsavedclass);
+ } else {
+ this.$element.addClass(this.options.unsavedclass);
+ }
+ }
+
+ //highlight when saving
+ if(this.options.highlight) {
+ var $e = this.$element,
+ bgColor = $e.css('background-color');
+
+ $e.css('background-color', this.options.highlight);
+ setTimeout(function(){
+ if(bgColor === 'transparent') {
+ bgColor = '';
+ }
+ $e.css('background-color', bgColor);
+ $e.addClass('editable-bg-transition');
+ setTimeout(function(){
+ $e.removeClass('editable-bg-transition');
+ }, 1700);
+ }, 10);
+ }
+
+ //set new value
+ this.setValue(params.newValue, false, params.response);
+
+ /**
+ Fired when new value was submitted. You can use <code>$(this).data('editable')</code> to access to editable instance
+
+ @event save
+ @param {Object} event event object
+ @param {Object} params additional params
+ @param {mixed} params.newValue submitted value
+ @param {Object} params.response ajax response
+ @example
+ $('#username').on('save', function(e, params) {
+ alert('Saved value: ' + params.newValue);
+ });
+ **/
+ //event itself is triggered by editableContainer. Description here is only for documentation
+ },
+
+ validate: function () {
+ if (typeof this.options.validate === 'function') {
+ return this.options.validate.call(this, this.value);
+ }
+ },
+
+ /**
+ Sets new value of editable
+ @method setValue(value, convertStr)
+ @param {mixed} value new value
+ @param {boolean} convertStr whether to convert value from string to internal format
+ **/
+ setValue: function(value, convertStr, response) {
+ if(convertStr) {
+ this.value = this.input.str2value(value);
+ } else {
+ this.value = value;
+ }
+ if(this.container) {
+ this.container.option('value', this.value);
+ }
+ $.when(this.render(response))
+ .then($.proxy(function() {
+ this.handleEmpty();
+ }, this));
+ },
+
+ /**
+ Activates input of visible container (e.g. set focus)
+ @method activate()
+ **/
+ activate: function() {
+ if(this.container) {
+ this.container.activate();
+ }
+ },
+
+ /**
+ Removes editable feature from element
+ @method destroy()
+ **/
+ destroy: function() {
+ this.disable();
+
+ if(this.container) {
+ this.container.destroy();
+ }
+
+ this.input.destroy();
+
+ if(this.options.toggle !== 'manual') {
+ this.$element.removeClass('editable-click');
+ this.$element.off(this.options.toggle + '.editable');
+ }
+
+ this.$element.off("save.internal");
+
+ this.$element.removeClass('editable editable-open editable-disabled');
+ this.$element.removeData('editable');
+ }
+ };
+
+ /* EDITABLE PLUGIN DEFINITION
+ * ======================= */
+
+ /**
+ jQuery method to initialize editable element.
+
+ @method $().editable(options)
+ @params {Object} options
+ @example
+ $('#username').editable({
+ type: 'text',
+ url: '/post',
+ pk: 1
+ });
+ **/
+ $.fn.editable = function (option) {
+ //special API methods returning non-jquery object
+ var result = {}, args = arguments, datakey = 'editable';
+ switch (option) {
+ /**
+ Runs client-side validation for all matched editables
+
+ @method validate()
+ @returns {Object} validation errors map
+ @example
+ $('#username, #fullname').editable('validate');
+ // possible result:
+ {
+ username: "username is required",
+ fullname: "fullname should be minimum 3 letters length"
+ }
+ **/
+ case 'validate':
+ this.each(function () {
+ var $this = $(this), data = $this.data(datakey), error;
+ if (data && (error = data.validate())) {
+ result[data.options.name] = error;
+ }
+ });
+ return result;
+
+ /**
+ Returns current values of editable elements.
+ Note that it returns an **object** with name-value pairs, not a value itself. It allows to get data from several elements.
+ If value of some editable is `null` or `undefined` it is excluded from result object.
+ When param `isSingle` is set to **true** - it is supposed you have single element and will return value of editable instead of object.
+
+ @method getValue()
+ @param {bool} isSingle whether to return just value of single element
+ @returns {Object} object of element names and values
+ @example
+ $('#username, #fullname').editable('getValue');
+ //result:
+ {
+ username: "superuser",
+ fullname: "John"
+ }
+ //isSingle = true
+ $('#username').editable('getValue', true);
+ //result "superuser"
+ **/
+ case 'getValue':
+ if(arguments.length === 2 && arguments[1] === true) { //isSingle = true
+ result = this.eq(0).data(datakey).value;
+ } else {
+ this.each(function () {
+ var $this = $(this), data = $this.data(datakey);
+ if (data && data.value !== undefined && data.value !== null) {
+ result[data.options.name] = data.input.value2submit(data.value);
+ }
+ });
+ }
+ return result;
+
+ /**
+ This method collects values from several editable elements and submit them all to server.
+ Internally it runs client-side validation for all fields and submits only in case of success.
+ See <a href="#newrecord">creating new records</a> for details.
+ Since 1.5.1 `submit` can be applied to single element to send data programmatically. In that case
+ `url`, `success` and `error` is taken from initial options and you can just call `$('#username').editable('submit')`.
+
+ @method submit(options)
+ @param {object} options
+ @param {object} options.url url to submit data
+ @param {object} options.data additional data to submit
+ @param {object} options.ajaxOptions additional ajax options
+ @param {function} options.error(obj) error handler
+ @param {function} options.success(obj,config) success handler
+ @returns {Object} jQuery object
+ **/
+ case 'submit': //collects value, validate and submit to server for creating new record
+ var config = arguments[1] || {},
+ $elems = this,
+ errors = this.editable('validate');
+
+ // validation ok
+ if($.isEmptyObject(errors)) {
+ var ajaxOptions = {};
+
+ // for single element use url, success etc from options
+ if($elems.length === 1) {
+ var editable = $elems.data('editable');
+ //standard params
+ var params = {
+ name: editable.options.name || '',
+ value: editable.input.value2submit(editable.value),
+ pk: (typeof editable.options.pk === 'function') ?
+ editable.options.pk.call(editable.options.scope) :
+ editable.options.pk
+ };
+
+ //additional params
+ if(typeof editable.options.params === 'function') {
+ params = editable.options.params.call(editable.options.scope, params);
+ } else {
+ //try parse json in single quotes (from data-params attribute)
+ editable.options.params = $.fn.editableutils.tryParseJson(editable.options.params, true);
+ $.extend(params, editable.options.params);
+ }
+
+ ajaxOptions = {
+ url: editable.options.url,
+ data: params,
+ type: 'POST'
+ };
+
+ // use success / error from options
+ config.success = config.success || editable.options.success;
+ config.error = config.error || editable.options.error;
+
+ // multiple elements
+ } else {
+ var values = this.editable('getValue');
+
+ ajaxOptions = {
+ url: config.url,
+ data: values,
+ type: 'POST'
+ };
+ }
+
+ // ajax success callabck (response 200 OK)
+ ajaxOptions.success = typeof config.success === 'function' ? function(response) {
+ config.success.call($elems, response, config);
+ } : $.noop;
+
+ // ajax error callabck
+ ajaxOptions.error = typeof config.error === 'function' ? function() {
+ config.error.apply($elems, arguments);
+ } : $.noop;
+
+ // extend ajaxOptions
+ if(config.ajaxOptions) {
+ $.extend(ajaxOptions, config.ajaxOptions);
+ }
+
+ // extra data
+ if(config.data) {
+ $.extend(ajaxOptions.data, config.data);
+ }
+
+ // perform ajax request
+ $.ajax(ajaxOptions);
+ } else { //client-side validation error
+ if(typeof config.error === 'function') {
+ config.error.call($elems, errors);
+ }
+ }
+ return this;
+ }
+
+ //return jquery object
+ return this.each(function () {
+ var $this = $(this),
+ data = $this.data(datakey),
+ options = typeof option === 'object' && option;
+
+ //for delegated targets do not store `editable` object for element
+ //it's allows several different selectors.
+ //see: https://github.com/vitalets/x-editable/issues/312
+ if(options && options.selector) {
+ data = new Editable(this, options);
+ return;
+ }
+
+ if (!data) {
+ $this.data(datakey, (data = new Editable(this, options)));
+ }
+
+ if (typeof option === 'string') { //call method
+ data[option].apply(data, Array.prototype.slice.call(args, 1));
+ }
+ });
+ };
+
+
+ $.fn.editable.defaults = {
+ /**
+ Type of input. Can be <code>text|textarea|select|date|checklist</code> and more
+
+ @property type
+ @type string
+ @default 'text'
+ **/
+ type: 'text',
+ /**
+ Sets disabled state of editable
+
+ @property disabled
+ @type boolean
+ @default false
+ **/
+ disabled: false,
+ /**
+ How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>.
+ When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.
+ **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element,
+ you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document.
+
+ @example
+ $('#edit-button').click(function(e) {
+ e.stopPropagation();
+ $('#username').editable('toggle');
+ });
+
+ @property toggle
+ @type string
+ @default 'click'
+ **/
+ toggle: 'click',
+ /**
+ Text shown when element is empty.
+
+ @property emptytext
+ @type string
+ @default 'Empty'
+ **/
+ emptytext: 'Empty',
+ /**
+ Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Useful for select and date.
+ For example, if dropdown list is <code>{1: 'a', 2: 'b'}</code> and element's value set to <code>1</code>, it's html will be automatically set to <code>'a'</code>.
+ <code>auto</code> - text will be automatically set only if element is empty.
+ <code>always|never</code> - always(never) try to set element's text.
+
+ @property autotext
+ @type string
+ @default 'auto'
+ **/
+ autotext: 'auto',
+ /**
+ Initial value of input. If not set, taken from element's text.
+ Note, that if element's text is empty - text is automatically generated from value and can be customized (see `autotext` option).
+ For example, to display currency sign:
+ @example
+ <a id="price" data-type="text" data-value="100"></a>
+ <script>
+ $('#price').editable({
+ ...
+ display: function(value) {
+ $(this).text(value + '$');
+ }
+ })
+ </script>
+
+ @property value
+ @type mixed
+ @default element's text
+ **/
+ value: null,
+ /**
+ Callback to perform custom displaying of value in element's text.
+ If `null`, default input's display used.
+ If `false`, no displaying methods will be called, element's text will never change.
+ Runs under element's scope.
+ _**Parameters:**_
+
+ * `value` current value to be displayed
+ * `response` server response (if display called after ajax submit), since 1.4.0
+
+ For _inputs with source_ (select, checklist) parameters are different:
+
+ * `value` current value to be displayed
+ * `sourceData` array of items for current input (e.g. dropdown items)
+ * `response` server response (if display called after ajax submit), since 1.4.0
+
+ To get currently selected items use `$.fn.editableutils.itemsByValue(value, sourceData)`.
+
+ @property display
+ @type function|boolean
+ @default null
+ @since 1.2.0
+ @example
+ display: function(value, sourceData) {
+ //display checklist as comma-separated values
+ var html = [],
+ checked = $.fn.editableutils.itemsByValue(value, sourceData);
+
+ if(checked.length) {
+ $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
+ $(this).html(html.join(', '));
+ } else {
+ $(this).empty();
+ }
+ }
+ **/
+ display: null,
+ /**
+ Css class applied when editable text is empty.
+
+ @property emptyclass
+ @type string
+ @since 1.4.1
+ @default editable-empty
+ **/
+ emptyclass: 'editable-empty',
+ /**
+ Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`).
+ You may set it to `null` if you work with editables locally and submit them together.
+
+ @property unsavedclass
+ @type string
+ @since 1.4.1
+ @default editable-unsaved
+ **/
+ unsavedclass: 'editable-unsaved',
+ /**
+ If selector is provided, editable will be delegated to the specified targets.
+ Usefull for dynamically generated DOM elements.
+ **Please note**, that delegated targets can't be initialized with `emptytext` and `autotext` options,
+ as they actually become editable only after first click.
+ You should manually set class `editable-click` to these elements.
+ Also, if element originally empty you should add class `editable-empty`, set `data-value=""` and write emptytext into element:
+
+ @property selector
+ @type string
+ @since 1.4.1
+ @default null
+ @example
+ <div id="user">
+ <!-- empty -->
+ <a href="#" data-name="username" data-type="text" class="editable-click editable-empty" data-value="" title="Username">Empty</a>
+ <!-- non-empty -->
+ <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" class="editable-click" title="Group">Operator</a>
+ </div>
+
+ <script>
+ $('#user').editable({
+ selector: 'a',
+ url: '/post',
+ pk: 1
+ });
+ </script>
+ **/
+ selector: null,
+ /**
+ Color used to highlight element after update. Implemented via CSS3 transition, works in modern browsers.
+
+ @property highlight
+ @type string|boolean
+ @since 1.4.5
+ @default #FFFF80
+ **/
+ highlight: '#FFFF80'
+ };
+
+}(window.jQuery));
+
+/**
+AbstractInput - base class for all editable inputs.
+It defines interface to be implemented by any input type.
+To create your own input you can inherit from this class.
+
+@class abstractinput
+**/
+(function ($) {
+ "use strict";
+
+ //types
+ $.fn.editabletypes = {};
+
+ var AbstractInput = function () { };
+
+ AbstractInput.prototype = {
+ /**
+ Initializes input
+
+ @method init()
+ **/
+ init: function(type, options, defaults) {
+ this.type = type;
+ this.options = $.extend({}, defaults, options);
+ },
+
+ /*
+ this method called before render to init $tpl that is inserted in DOM
+ */
+ prerender: function() {
+ this.$tpl = $(this.options.tpl); //whole tpl as jquery object
+ this.$input = this.$tpl; //control itself, can be changed in render method
+ this.$clear = null; //clear button
+ this.error = null; //error message, if input cannot be rendered
+ },
+
+ /**
+ Renders input from tpl. Can return jQuery deferred object.
+ Can be overwritten in child objects
+
+ @method render()
+ **/
+ render: function() {
+
+ },
+
+ /**
+ Sets element's html by value.
+
+ @method value2html(value, element)
+ @param {mixed} value
+ @param {DOMElement} element
+ **/
+ value2html: function(value, element) {
+ $(element)[this.options.escape ? 'text' : 'html']($.trim(value));
+ },
+
+ /**
+ Converts element's html to value
+
+ @method html2value(html)
+ @param {string} html
+ @returns {mixed}
+ **/
+ html2value: function(html) {
+ return $('<div>').html(html).text();
+ },
+
+ /**
+ Converts value to string (for internal compare). For submitting to server used value2submit().
+
+ @method value2str(value)
+ @param {mixed} value
+ @returns {string}
+ **/
+ value2str: function(value) {
+ return value;
+ },
+
+ /**
+ Converts string received from server into value. Usually from `data-value` attribute.
+
+ @method str2value(str)
+ @param {string} str
+ @returns {mixed}
+ **/
+ str2value: function(str) {
+ return str;
+ },
+
+ /**
+ Converts value for submitting to server. Result can be string or object.
+
+ @method value2submit(value)
+ @param {mixed} value
+ @returns {mixed}
+ **/
+ value2submit: function(value) {
+ return value;
+ },
+
+ /**
+ Sets value of input.
+
+ @method value2input(value)
+ @param {mixed} value
+ **/
+ value2input: function(value) {
+ this.$input.val(value);
+ },
+
+ /**
+ Returns value of input. Value can be object (e.g. datepicker)
+
+ @method input2value()
+ **/
+ input2value: function() {
+ return this.$input.val();
+ },
+
+ /**
+ Activates input. For text it sets focus.
+
+ @method activate()
+ **/
+ activate: function() {
+ if(this.$input.is(':visible')) {
+ this.$input.focus();
+ }
+ },
+
+ /**
+ Creates input.
+
+ @method clear()
+ **/
+ clear: function() {
+ this.$input.val(null);
+ },
+
+ /**
+ method to escape html.
+ **/
+ escape: function(str) {
+ return $('<div>').text(str).html();
+ },
+
+ /**
+ attach handler to automatically submit form when value changed (useful when buttons not shown)
+ **/
+ autosubmit: function() {
+
+ },
+
+ /**
+ Additional actions when destroying element
+ **/
+ destroy: function() {
+ },
+
+ // -------- helper functions --------
+ setClass: function() {
+ if(this.options.inputclass) {
+ this.$input.addClass(this.options.inputclass);
+ }
+ },
+
+ setAttr: function(attr) {
+ if (this.options[attr] !== undefined && this.options[attr] !== null) {
+ this.$input.attr(attr, this.options[attr]);
+ }
+ },
+
+ option: function(key, value) {
+ this.options[key] = value;
+ }
+
+ };
+
+ AbstractInput.defaults = {
+ /**
+ HTML template of input. Normally you should not change it.
+
+ @property tpl
+ @type string
+ @default ''
+ **/
+ tpl: '',
+ /**
+ CSS class automatically applied to input
+
+ @property inputclass
+ @type string
+ @default null
+ **/
+ inputclass: null,
+
+ /**
+ If `true` - html will be escaped in content of element via $.text() method.
+ If `false` - html will not be escaped, $.html() used.
+ When you use own `display` function, this option obviosly has no effect.
+
+ @property escape
+ @type boolean
+ @since 1.5.0
+ @default true
+ **/
+ escape: true,
+
+ //scope for external methods (e.g. source defined as function)
+ //for internal use only
+ scope: null,
+
+ //need to re-declare showbuttons here to get it's value from common config (passed only options existing in defaults)
+ showbuttons: true
+ };
+
+ $.extend($.fn.editabletypes, {abstractinput: AbstractInput});
+
+}(window.jQuery));
+
+/**
+List - abstract class for inputs that have source option loaded from js array or via ajax
+
+@class list
+@extends abstractinput
+**/
+(function ($) {
+ "use strict";
+
+ var List = function (options) {
+
+ };
+
+ $.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput);
+
+ $.extend(List.prototype, {
+ render: function () {
+ var deferred = $.Deferred();
+
+ this.error = null;
+ this.onSourceReady(function () {
+ this.renderList();
+ deferred.resolve();
+ }, function () {
+ this.error = this.options.sourceError;
+ deferred.resolve();
+ });
+
+ return deferred.promise();
+ },
+
+ html2value: function (html) {
+ return null; //can't set value by text
+ },
+
+ value2html: function (value, element, display, response) {
+ var deferred = $.Deferred(),
+ success = function () {
+ if(typeof display === 'function') {
+ //custom display method
+ display.call(element, value, this.sourceData, response);
+ } else {
+ this.value2htmlFinal(value, element);
+ }
+ deferred.resolve();
+ };
+
+ //for null value just call success without loading source
+ if(value === null) {
+ success.call(this);
+ } else {
+ this.onSourceReady(success, function () { deferred.resolve(); });
+ }
+
+ return deferred.promise();
+ },
+
+ // ------------- additional functions ------------
+
+ onSourceReady: function (success, error) {
+ //run source if it function
+ var source;
+ if ($.isFunction(this.options.source)) {
+ source = this.options.source.call(this.options.scope);
+ this.sourceData = null;
+ //note: if function returns the same source as URL - sourceData will be taken from cahce and no extra request performed
+ } else {
+ source = this.options.source;
+ }
+
+ //if allready loaded just call success
+ if(this.options.sourceCache && $.isArray(this.sourceData)) {
+ success.call(this);
+ return;
+ }
+
+ //try parse json in single quotes (for double quotes jquery does automatically)
+ try {
+ source = $.fn.editableutils.tryParseJson(source, false);
+ } catch (e) {
+ error.call(this);
+ return;
+ }
+
+ //loading from url
+ if (typeof source === 'string') {
+ //try to get sourceData from cache
+ if(this.options.sourceCache) {
+ var cacheID = source,
+ cache;
+
+ if (!$(document).data(cacheID)) {
+ $(document).data(cacheID, {});
+ }
+ cache = $(document).data(cacheID);
+
+ //check for cached data
+ if (cache.loading === false && cache.sourceData) { //take source from cache
+ this.sourceData = cache.sourceData;
+ this.doPrepend();
+ success.call(this);
+ return;
+ } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
+ cache.callbacks.push($.proxy(function () {
+ this.sourceData = cache.sourceData;
+ this.doPrepend();
+ success.call(this);
+ }, this));
+
+ //also collecting error callbacks
+ cache.err_callbacks.push($.proxy(error, this));
+ return;
+ } else { //no cache yet, activate it
+ cache.loading = true;
+ cache.callbacks = [];
+ cache.err_callbacks = [];
+ }
+ }
+
+ //ajaxOptions for source. Can be overwritten bt options.sourceOptions
+ var ajaxOptions = $.extend({
+ url: source,
+ type: 'get',
+ cache: false,
+ dataType: 'json',
+ success: $.proxy(function (data) {
+ if(cache) {
+ cache.loading = false;
+ }
+ this.sourceData = this.makeArray(data);
+ if($.isArray(this.sourceData)) {
+ if(cache) {
+ //store result in cache
+ cache.sourceData = this.sourceData;
+ //run success callbacks for other fields waiting for this source
+ $.each(cache.callbacks, function () { this.call(); });
+ }
+ this.doPrepend();
+ success.call(this);
+ } else {
+ error.call(this);
+ if(cache) {
+ //run error callbacks for other fields waiting for this source
+ $.each(cache.err_callbacks, function () { this.call(); });
+ }
+ }
+ }, this),
+ error: $.proxy(function () {
+ error.call(this);
+ if(cache) {
+ cache.loading = false;
+ //run error callbacks for other fields
+ $.each(cache.err_callbacks, function () { this.call(); });
+ }
+ }, this)
+ }, this.options.sourceOptions);
+
+ //loading sourceData from server
+ $.ajax(ajaxOptions);
+
+ } else { //options as json/array
+ this.sourceData = this.makeArray(source);
+
+ if($.isArray(this.sourceData)) {
+ this.doPrepend();
+ success.call(this);
+ } else {
+ error.call(this);
+ }
+ }
+ },
+
+ doPrepend: function () {
+ if(this.options.prepend === null || this.options.prepend === undefined) {
+ return;
+ }
+
+ if(!$.isArray(this.prependData)) {
+ //run prepend if it is function (once)
+ if ($.isFunction(this.options.prepend)) {
+ this.options.prepend = this.options.prepend.call(this.options.scope);
+ }
+
+ //try parse json in single quotes
+ this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
+
+ //convert prepend from string to object
+ if (typeof this.options.prepend === 'string') {
+ this.options.prepend = {'': this.options.prepend};
+ }
+
+ this.prependData = this.makeArray(this.options.prepend);
+ }
+
+ if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
+ this.sourceData = this.prependData.concat(this.sourceData);
+ }
+ },
+
+ /*
+ renders input list
+ */
+ renderList: function() {
+ // this method should be overwritten in child class
+ },
+
+ /*
+ set element's html by value
+ */
+ value2htmlFinal: function(value, element) {
+ // this method should be overwritten in child class
+ },
+
+ /**
+ * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
+ */
+ makeArray: function(data) {
+ var count, obj, result = [], item, iterateItem;
+ if(!data || typeof data === 'string') {
+ return null;
+ }
+
+ if($.isArray(data)) { //array
+ /*
+ function to iterate inside item of array if item is object.
+ Caclulates count of keys in item and store in obj.
+ */
+ iterateItem = function (k, v) {
+ obj = {value: k, text: v};
+ if(count++ >= 2) {
+ return false;// exit from `each` if item has more than one key.
+ }
+ };
+
+ for(var i = 0; i < data.length; i++) {
+ item = data[i];
+ if(typeof item === 'object') {
+ count = 0; //count of keys inside item
+ $.each(item, iterateItem);
+ //case: [{val1: 'text1'}, {val2: 'text2} ...]
+ if(count === 1) {
+ result.push(obj);
+ //case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
+ } else if(count > 1) {
+ //removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
+ if(item.children) {
+ item.children = this.makeArray(item.children);
+ }
+ result.push(item);
+ }
+ } else {
+ //case: ['text1', 'text2' ...]
+ result.push({value: item, text: item});
+ }
+ }
+ } else { //case: {val1: 'text1', val2: 'text2, ...}
+ $.each(data, function (k, v) {
+ result.push({value: k, text: v});
+ });
+ }
+ return result;
+ },
+
+ option: function(key, value) {
+ this.options[key] = value;
+ if(key === 'source') {
+ this.sourceData = null;
+ }
+ if(key === 'prepend') {
+ this.prependData = null;
+ }
+ }
+
+ });
+
+ List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ Source data for list.
+ If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`
+ For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
+
+ If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
+
+ If **function**, it should return data in format above (since 1.4.0).
+
+ Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
+ `[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
+
+
+ @property source
+ @type string | array | object | function
+ @default null
+ **/
+ source: null,
+ /**
+ Data automatically prepended to the beginning of dropdown list.
+
+ @property prepend
+ @type string | array | object | function
+ @default false
+ **/
+ prepend: false,
+ /**
+ Error message when list cannot be loaded (e.g. ajax error)
+
+ @property sourceError
+ @type string
+ @default Error when loading list
+ **/
+ sourceError: 'Error when loading list',
+ /**
+ if <code>true</code> and source is **string url** - results will be cached for fields with the same source.
+ Usefull for editable column in grid to prevent extra requests.
+
+ @property sourceCache
+ @type boolean
+ @default true
+ @since 1.2.0
+ **/
+ sourceCache: true,
+ /**
+ Additional ajax options to be used in $.ajax() when loading list from server.
+ Useful to send extra parameters (`data` key) or change request method (`type` key).
+
+ @property sourceOptions
+ @type object|function
+ @default null
+ @since 1.5.0
+ **/
+ sourceOptions: null
+ });
+
+ $.fn.editabletypes.list = List;
+
+}(window.jQuery));
+
+/**
+Text input
+
+@class text
+@extends abstractinput
+@final
+@example
+<a href="#" id="username" data-type="text" data-pk="1">awesome</a>
+<script>
+$(function(){
+ $('#username').editable({
+ url: '/post',
+ title: 'Enter username'
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var Text = function (options) {
+ this.init('text', options, Text.defaults);
+ };
+
+ $.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput);
+
+ $.extend(Text.prototype, {
+ render: function() {
+ this.renderClear();
+ this.setClass();
+ this.setAttr('placeholder');
+ },
+
+ activate: function() {
+ if(this.$input.is(':visible')) {
+ this.$input.focus();
+ $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
+ if(this.toggleClear) {
+ this.toggleClear();
+ }
+ }
+ },
+
+ //render clear button
+ renderClear: function() {
+ if (this.options.clear) {
+ this.$clear = $('<span class="editable-clear-x"></span>');
+ this.$input.after(this.$clear)
+ .css('padding-right', 24)
+ .keyup($.proxy(function(e) {
+ //arrows, enter, tab, etc
+ if(~$.inArray(e.keyCode, [40,38,9,13,27])) {
+ return;
+ }
+
+ clearTimeout(this.t);
+ var that = this;
+ this.t = setTimeout(function() {
+ that.toggleClear(e);
+ }, 100);
+
+ }, this))
+ .parent().css('position', 'relative');
+
+ this.$clear.click($.proxy(this.clear, this));
+ }
+ },
+
+ postrender: function() {
+ /*
+ //now `clear` is positioned via css
+ if(this.$clear) {
+ //can position clear button only here, when form is shown and height can be calculated
+// var h = this.$input.outerHeight(true) || 20,
+ var h = this.$clear.parent().height(),
+ delta = (h - this.$clear.height()) / 2;
+
+ //this.$clear.css({bottom: delta, right: delta});
+ }
+ */
+ },
+
+ //show / hide clear button
+ toggleClear: function(e) {
+ if(!this.$clear) {
+ return;
+ }
+
+ var len = this.$input.val().length,
+ visible = this.$clear.is(':visible');
+
+ if(len && !visible) {
+ this.$clear.show();
+ }
+
+ if(!len && visible) {
+ this.$clear.hide();
+ }
+ },
+
+ clear: function() {
+ this.$clear.hide();
+ this.$input.val('').focus();
+ }
+ });
+
+ Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <input type="text">
+ **/
+ tpl: '<input type="text">',
+ /**
+ Placeholder attribute of input. Shown when input is empty.
+
+ @property placeholder
+ @type string
+ @default null
+ **/
+ placeholder: null,
+
+ /**
+ Whether to show `clear` button
+
+ @property clear
+ @type boolean
+ @default true
+ **/
+ clear: true
+ });
+
+ $.fn.editabletypes.text = Text;
+
+}(window.jQuery));
+
+/**
+Textarea input
+
+@class textarea
+@extends abstractinput
+@final
+@example
+<a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a>
+<script>
+$(function(){
+ $('#comments').editable({
+ url: '/post',
+ title: 'Enter comments',
+ rows: 10
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var Textarea = function (options) {
+ this.init('textarea', options, Textarea.defaults);
+ };
+
+ $.fn.editableutils.inherit(Textarea, $.fn.editabletypes.abstractinput);
+
+ $.extend(Textarea.prototype, {
+ render: function () {
+ this.setClass();
+ this.setAttr('placeholder');
+ this.setAttr('rows');
+
+ //ctrl + enter
+ this.$input.keydown(function (e) {
+ if (e.ctrlKey && e.which === 13) {
+ $(this).closest('form').submit();
+ }
+ });
+ },
+
+ //using `white-space: pre-wrap` solves \n <--> BR conversion very elegant!
+ /*
+ value2html: function(value, element) {
+ var html = '', lines;
+ if(value) {
+ lines = value.split("\n");
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = $('<div>').text(lines[i]).html();
+ }
+ html = lines.join('<br>');
+ }
+ $(element).html(html);
+ },
+
+ html2value: function(html) {
+ if(!html) {
+ return '';
+ }
+
+ var regex = new RegExp(String.fromCharCode(10), 'g');
+ var lines = html.split(/<br\s*\/?>/i);
+ for (var i = 0; i < lines.length; i++) {
+ var text = $('<div>').html(lines[i]).text();
+
+ // Remove newline characters (\n) to avoid them being converted by value2html() method
+ // thus adding extra <br> tags
+ text = text.replace(regex, '');
+
+ lines[i] = text;
+ }
+ return lines.join("\n");
+ },
+ */
+ activate: function() {
+ $.fn.editabletypes.text.prototype.activate.call(this);
+ }
+ });
+
+ Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <textarea></textarea>
+ **/
+ tpl:'<textarea></textarea>',
+ /**
+ @property inputclass
+ @default input-large
+ **/
+ inputclass: 'input-large',
+ /**
+ Placeholder attribute of input. Shown when input is empty.
+
+ @property placeholder
+ @type string
+ @default null
+ **/
+ placeholder: null,
+ /**
+ Number of rows in textarea
+
+ @property rows
+ @type integer
+ @default 7
+ **/
+ rows: 7
+ });
+
+ $.fn.editabletypes.textarea = Textarea;
+
+}(window.jQuery));
+
+/**
+Select (dropdown)
+
+@class select
+@extends list
+@final
+@example
+<a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-title="Select status"></a>
+<script>
+$(function(){
+ $('#status').editable({
+ value: 2,
+ source: [
+ {value: 1, text: 'Active'},
+ {value: 2, text: 'Blocked'},
+ {value: 3, text: 'Deleted'}
+ ]
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var Select = function (options) {
+ this.init('select', options, Select.defaults);
+ };
+
+ $.fn.editableutils.inherit(Select, $.fn.editabletypes.list);
+
+ $.extend(Select.prototype, {
+ renderList: function() {
+ this.$input.empty();
+
+ var fillItems = function($el, data) {
+ var attr;
+ if($.isArray(data)) {
+ for(var i=0; i<data.length; i++) {
+ attr = {};
+ if(data[i].children) {
+ attr.label = data[i].text;
+ $el.append(fillItems($('<optgroup>', attr), data[i].children));
+ } else {
+ attr.value = data[i].value;
+ if(data[i].disabled) {
+ attr.disabled = true;
+ }
+ $el.append($('<option>', attr).text(data[i].text));
+ }
+ }
+ }
+ return $el;
+ };
+
+ fillItems(this.$input, this.sourceData);
+
+ this.setClass();
+
+ //enter submit
+ this.$input.on('keydown.editable', function (e) {
+ if (e.which === 13) {
+ $(this).closest('form').submit();
+ }
+ });
+ },
+
+ value2htmlFinal: function(value, element) {
+ var text = '',
+ items = $.fn.editableutils.itemsByValue(value, this.sourceData);
+
+ if(items.length) {
+ text = items[0].text;
+ }
+
+ //$(element).text(text);
+ $.fn.editabletypes.abstractinput.prototype.value2html.call(this, text, element);
+ },
+
+ autosubmit: function() {
+ this.$input.off('keydown.editable').on('change.editable', function(){
+ $(this).closest('form').submit();
+ });
+ }
+ });
+
+ Select.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
+ /**
+ @property tpl
+ @default <select></select>
+ **/
+ tpl:'<select></select>'
+ });
+
+ $.fn.editabletypes.select = Select;
+
+}(window.jQuery));
+
+/**
+List of checkboxes.
+Internally value stored as javascript array of values.
+
+@class checklist
+@extends list
+@final
+@example
+<a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-title="Select options"></a>
+<script>
+$(function(){
+ $('#options').editable({
+ value: [2, 3],
+ source: [
+ {value: 1, text: 'option1'},
+ {value: 2, text: 'option2'},
+ {value: 3, text: 'option3'}
+ ]
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var Checklist = function (options) {
+ this.init('checklist', options, Checklist.defaults);
+ };
+
+ $.fn.editableutils.inherit(Checklist, $.fn.editabletypes.list);
+
+ $.extend(Checklist.prototype, {
+ renderList: function() {
+ var $label, $div;
+
+ this.$tpl.empty();
+
+ if(!$.isArray(this.sourceData)) {
+ return;
+ }
+
+ for(var i=0; i<this.sourceData.length; i++) {
+ $label = $('<label>').append($('<input>', {
+ type: 'checkbox',
+ value: this.sourceData[i].value
+ }))
+ .append($('<span>').text(' '+this.sourceData[i].text));
+
+ $('<div>').append($label).appendTo(this.$tpl);
+ }
+
+ this.$input = this.$tpl.find('input[type="checkbox"]');
+ this.setClass();
+ },
+
+ value2str: function(value) {
+ return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : '';
+ },
+
+ //parse separated string
+ str2value: function(str) {
+ var reg, value = null;
+ if(typeof str === 'string' && str.length) {
+ reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*');
+ value = str.split(reg);
+ } else if($.isArray(str)) {
+ value = str;
+ } else {
+ value = [str];
+ }
+ return value;
+ },
+
+ //set checked on required checkboxes
+ value2input: function(value) {
+ this.$input.prop('checked', false);
+ if($.isArray(value) && value.length) {
+ this.$input.each(function(i, el) {
+ var $el = $(el);
+ // cannot use $.inArray as it performs strict comparison
+ $.each(value, function(j, val){
+ /*jslint eqeq: true*/
+ if($el.val() == val) {
+ /*jslint eqeq: false*/
+ $el.prop('checked', true);
+ }
+ });
+ });
+ }
+ },
+
+ input2value: function() {
+ var checked = [];
+ this.$input.filter(':checked').each(function(i, el) {
+ checked.push($(el).val());
+ });
+ return checked;
+ },
+
+ //collect text of checked boxes
+ value2htmlFinal: function(value, element) {
+ var html = [],
+ checked = $.fn.editableutils.itemsByValue(value, this.sourceData),
+ escape = this.options.escape;
+
+ if(checked.length) {
+ $.each(checked, function(i, v) {
+ var text = escape ? $.fn.editableutils.escape(v.text) : v.text;
+ html.push(text);
+ });
+ $(element).html(html.join('<br>'));
+ } else {
+ $(element).empty();
+ }
+ },
+
+ activate: function() {
+ this.$input.first().focus();
+ },
+
+ autosubmit: function() {
+ this.$input.on('keydown', function(e){
+ if (e.which === 13) {
+ $(this).closest('form').submit();
+ }
+ });
+ }
+ });
+
+ Checklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
+ /**
+ @property tpl
+ @default <div></div>
+ **/
+ tpl:'<div class="editable-checklist"></div>',
+
+ /**
+ @property inputclass
+ @type string
+ @default null
+ **/
+ inputclass: null,
+
+ /**
+ Separator of values when reading from `data-value` attribute
+
+ @property separator
+ @type string
+ @default ','
+ **/
+ separator: ','
+ });
+
+ $.fn.editabletypes.checklist = Checklist;
+
+}(window.jQuery));
+
+/**
+HTML5 input types.
+Following types are supported:
+
+* password
+* email
+* url
+* tel
+* number
+* range
+* time
+
+Learn more about html5 inputs:
+http://www.w3.org/wiki/HTML5_form_additions
+To check browser compatibility please see:
+https://developer.mozilla.org/en-US/docs/HTML/Element/Input
+
+@class html5types
+@extends text
+@final
+@since 1.3.0
+@example
+<a href="#" id="email" data-type="email" data-pk="1">admin@example.com</a>
+<script>
+$(function(){
+ $('#email').editable({
+ url: '/post',
+ title: 'Enter email'
+ });
+});
+</script>
+**/
+
+/**
+@property tpl
+@default depends on type
+**/
+
+/*
+Password
+*/
+(function ($) {
+ "use strict";
+
+ var Password = function (options) {
+ this.init('password', options, Password.defaults);
+ };
+ $.fn.editableutils.inherit(Password, $.fn.editabletypes.text);
+ $.extend(Password.prototype, {
+ //do not display password, show '[hidden]' instead
+ value2html: function(value, element) {
+ if(value) {
+ $(element).text('[hidden]');
+ } else {
+ $(element).empty();
+ }
+ },
+ //as password not displayed, should not set value by html
+ html2value: function(html) {
+ return null;
+ }
+ });
+ Password.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
+ tpl: '<input type="password">'
+ });
+ $.fn.editabletypes.password = Password;
+}(window.jQuery));
+
+
+/*
+Email
+*/
+(function ($) {
+ "use strict";
+
+ var Email = function (options) {
+ this.init('email', options, Email.defaults);
+ };
+ $.fn.editableutils.inherit(Email, $.fn.editabletypes.text);
+ Email.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
+ tpl: '<input type="email">'
+ });
+ $.fn.editabletypes.email = Email;
+}(window.jQuery));
+
+
+/*
+Url
+*/
+(function ($) {
+ "use strict";
+
+ var Url = function (options) {
+ this.init('url', options, Url.defaults);
+ };
+ $.fn.editableutils.inherit(Url, $.fn.editabletypes.text);
+ Url.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
+ tpl: '<input type="url">'
+ });
+ $.fn.editabletypes.url = Url;
+}(window.jQuery));
+
+
+/*
+Tel
+*/
+(function ($) {
+ "use strict";
+
+ var Tel = function (options) {
+ this.init('tel', options, Tel.defaults);
+ };
+ $.fn.editableutils.inherit(Tel, $.fn.editabletypes.text);
+ Tel.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
+ tpl: '<input type="tel">'
+ });
+ $.fn.editabletypes.tel = Tel;
+}(window.jQuery));
+
+
+/*
+Number
+*/
+(function ($) {
+ "use strict";
+
+ var NumberInput = function (options) {
+ this.init('number', options, NumberInput.defaults);
+ };
+ $.fn.editableutils.inherit(NumberInput, $.fn.editabletypes.text);
+ $.extend(NumberInput.prototype, {
+ render: function () {
+ NumberInput.superclass.render.call(this);
+ this.setAttr('min');
+ this.setAttr('max');
+ this.setAttr('step');
+ },
+ postrender: function() {
+ if(this.$clear) {
+ //increase right ffset for up/down arrows
+ this.$clear.css({right: 24});
+ /*
+ //can position clear button only here, when form is shown and height can be calculated
+ var h = this.$input.outerHeight(true) || 20,
+ delta = (h - this.$clear.height()) / 2;
+
+ //add 12px to offset right for up/down arrows
+ this.$clear.css({top: delta, right: delta + 16});
+ */
+ }
+ }
+ });
+ NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
+ tpl: '<input type="number">',
+ inputclass: 'input-mini',
+ min: null,
+ max: null,
+ step: null
+ });
+ $.fn.editabletypes.number = NumberInput;
+}(window.jQuery));
+
+
+/*
+Range (inherit from number)
+*/
+(function ($) {
+ "use strict";
+
+ var Range = function (options) {
+ this.init('range', options, Range.defaults);
+ };
+ $.fn.editableutils.inherit(Range, $.fn.editabletypes.number);
+ $.extend(Range.prototype, {
+ render: function () {
+ this.$input = this.$tpl.filter('input');
+
+ this.setClass();
+ this.setAttr('min');
+ this.setAttr('max');
+ this.setAttr('step');
+
+ this.$input.on('input', function(){
+ $(this).siblings('output').text($(this).val());
+ });
+ },
+ activate: function() {
+ this.$input.focus();
+ }
+ });
+ Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, {
+ tpl: '<input type="range"><output style="width: 30px; display: inline-block"></output>',
+ inputclass: 'input-medium'
+ });
+ $.fn.editabletypes.range = Range;
+}(window.jQuery));
+
+/*
+Time
+*/
+(function ($) {
+ "use strict";
+
+ var Time = function (options) {
+ this.init('time', options, Time.defaults);
+ };
+ //inherit from abstract, as inheritance from text gives selection error.
+ $.fn.editableutils.inherit(Time, $.fn.editabletypes.abstractinput);
+ $.extend(Time.prototype, {
+ render: function() {
+ this.setClass();
+ }
+ });
+ Time.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ tpl: '<input type="time">'
+ });
+ $.fn.editabletypes.time = Time;
+}(window.jQuery));
+
+/**
+Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
+Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
+
+You should manually download and include select2 distributive:
+
+ <link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
+ <script src="select2/select2.js"></script>
+
+To make it **bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css):
+
+ <link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>
+
+**Note:** currently `autotext` feature does not work for select2 with `ajax` remote source.
+You need initially put both `data-value` and element's text youself:
+
+ <a href="#" data-type="select2" data-value="1">Text1</a>
+
+
+@class select2
+@extends abstractinput
+@since 1.4.1
+@final
+@example
+<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a>
+<script>
+$(function(){
+ //local source
+ $('#country').editable({
+ source: [
+ {id: 'gb', text: 'Great Britain'},
+ {id: 'us', text: 'United States'},
+ {id: 'ru', text: 'Russia'}
+ ],
+ select2: {
+ multiple: true
+ }
+ });
+ //remote source (simple)
+ $('#country').editable({
+ source: '/getCountries',
+ select2: {
+ placeholder: 'Select Country',
+ minimumInputLength: 1
+ }
+ });
+ //remote source (advanced)
+ $('#country').editable({
+ select2: {
+ placeholder: 'Select Country',
+ allowClear: true,
+ minimumInputLength: 3,
+ id: function (item) {
+ return item.CountryId;
+ },
+ ajax: {
+ url: '/getCountries',
+ dataType: 'json',
+ data: function (term, page) {
+ return { query: term };
+ },
+ results: function (data, page) {
+ return { results: data };
+ }
+ },
+ formatResult: function (item) {
+ return item.CountryName;
+ },
+ formatSelection: function (item) {
+ return item.CountryName;
+ },
+ initSelection: function (element, callback) {
+ return $.get('/getCountryById', { query: element.val() }, function (data) {
+ callback(data);
+ });
+ }
+ }
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var Constructor = function (options) {
+ this.init('select2', options, Constructor.defaults);
+
+ options.select2 = options.select2 || {};
+
+ this.sourceData = null;
+
+ //placeholder
+ if(options.placeholder) {
+ options.select2.placeholder = options.placeholder;
+ }
+
+ //if not `tags` mode, use source
+ if(!options.select2.tags && options.source) {
+ var source = options.source;
+ //if source is function, call it (once!)
+ if ($.isFunction(options.source)) {
+ source = options.source.call(options.scope);
+ }
+
+ if (typeof source === 'string') {
+ options.select2.ajax = options.select2.ajax || {};
+ //some default ajax params
+ if(!options.select2.ajax.data) {
+ options.select2.ajax.data = function(term) {return { query:term };};
+ }
+ if(!options.select2.ajax.results) {
+ options.select2.ajax.results = function(data) { return {results:data };};
+ }
+ options.select2.ajax.url = source;
+ } else {
+ //check format and convert x-editable format to select2 format (if needed)
+ this.sourceData = this.convertSource(source);
+ options.select2.data = this.sourceData;
+ }
+ }
+
+ //overriding objects in config (as by default jQuery extend() is not recursive)
+ this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2);
+
+ //detect whether it is multi-valued
+ this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
+ this.isRemote = ('ajax' in this.options.select2);
+
+ //store function returning ID of item
+ //should be here as used inautotext for local source
+ this.idFunc = this.options.select2.id;
+ if (typeof(this.idFunc) !== "function") {
+ var idKey = this.idFunc || 'id';
+ this.idFunc = function (e) { return e[idKey]; };
+ }
+
+ //store function that renders text in select2
+ this.formatSelection = this.options.select2.formatSelection;
+ if (typeof(this.formatSelection) !== "function") {
+ this.formatSelection = function (e) { return e.text; };
+ }
+ };
+
+ $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
+
+ $.extend(Constructor.prototype, {
+ render: function() {
+ this.setClass();
+
+ //can not apply select2 here as it calls initSelection
+ //over input that does not have correct value yet.
+ //apply select2 only in value2input
+ //this.$input.select2(this.options.select2);
+
+ //when data is loaded via ajax, we need to know when it's done to populate listData
+ if(this.isRemote) {
+ //listen to loaded event to populate data
+ this.$input.on('select2-loaded', $.proxy(function(e) {
+ this.sourceData = e.items.results;
+ }, this));
+ }
+
+ //trigger resize of editableform to re-position container in multi-valued mode
+ if(this.isMultiple) {
+ this.$input.on('change', function() {
+ $(this).closest('form').parent().triggerHandler('resize');
+ });
+ }
+ },
+
+ value2html: function(value, element) {
+ var text = '', data,
+ that = this;
+
+ if(this.options.select2.tags) { //in tags mode just assign value
+ data = value;
+ //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
+ } else if(this.sourceData) {
+ data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
+ } else {
+ //can not get list of possible values
+ //(e.g. autotext for select2 with ajax source)
+ }
+
+ //data may be array (when multiple values allowed)
+ if($.isArray(data)) {
+ //collect selected data and show with separator
+ text = [];
+ $.each(data, function(k, v){
+ text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
+ });
+ } else if(data) {
+ text = that.formatSelection(data);
+ }
+
+ text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
+
+ //$(element).text(text);
+ Constructor.superclass.value2html.call(this, text, element);
+ },
+
+ html2value: function(html) {
+ return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
+ },
+
+ value2input: function(value) {
+ // if value array => join it anyway
+ if($.isArray(value)) {
+ value = value.join(this.getSeparator());
+ }
+
+ //for remote source just set value, text is updated by initSelection
+ if(!this.$input.data('select2')) {
+ this.$input.val(value);
+ this.$input.select2(this.options.select2);
+ } else {
+ //second argument needed to separate initial change from user's click (for autosubmit)
+ this.$input.val(value).trigger('change', true);
+
+ //Uncaught Error: cannot call val() if initSelection() is not defined
+ //this.$input.select2('val', value);
+ }
+
+ // if defined remote source AND no multiple mode AND no user's initSelection provided -->
+ // we should somehow get text for provided id.
+ // The solution is to use element's text as text for that id (exclude empty)
+ if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) {
+ // customId and customText are methods to extract `id` and `text` from data object
+ // we can use this workaround only if user did not define these methods
+ // otherwise we cant construct data object
+ var customId = this.options.select2.id,
+ customText = this.options.select2.formatSelection;
+
+ if(!customId && !customText) {
+ var $el = $(this.options.scope);
+ if (!$el.data('editable').isEmpty) {
+ var data = {id: value, text: $el.text()};
+ this.$input.select2('data', data);
+ }
+ }
+ }
+ },
+
+ input2value: function() {
+ return this.$input.select2('val');
+ },
+
+ str2value: function(str, separator) {
+ if(typeof str !== 'string' || !this.isMultiple) {
+ return str;
+ }
+
+ separator = separator || this.getSeparator();
+
+ var val, i, l;
+
+ if (str === null || str.length < 1) {
+ return null;
+ }
+ val = str.split(separator);
+ for (i = 0, l = val.length; i < l; i = i + 1) {
+ val[i] = $.trim(val[i]);
+ }
+
+ return val;
+ },
+
+ autosubmit: function() {
+ this.$input.on('change', function(e, isInitial){
+ if(!isInitial) {
+ $(this).closest('form').submit();
+ }
+ });
+ },
+
+ getSeparator: function() {
+ return this.options.select2.separator || $.fn.select2.defaults.separator;
+ },
+
+ /*
+ Converts source from x-editable format: {value: 1, text: "1"} to
+ select2 format: {id: 1, text: "1"}
+ */
+ convertSource: function(source) {
+ if($.isArray(source) && source.length && source[0].value !== undefined) {
+ for(var i = 0; i<source.length; i++) {
+ if(source[i].value !== undefined) {
+ source[i].id = source[i].value;
+ delete source[i].value;
+ }
+ }
+ }
+ return source;
+ },
+
+ destroy: function() {
+ if(this.$input.data('select2')) {
+ this.$input.select2('destroy');
+ }
+ }
+
+ });
+
+ Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <input type="hidden">
+ **/
+ tpl:'<input type="hidden">',
+ /**
+ Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
+
+ @property select2
+ @type object
+ @default null
+ **/
+ select2: null,
+ /**
+ Placeholder attribute of select
+
+ @property placeholder
+ @type string
+ @default null
+ **/
+ placeholder: null,
+ /**
+ Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
+ Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
+ E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
+
+ @property source
+ @type array|string|function
+ @default null
+ **/
+ source: null,
+ /**
+ Separator used to display tags.
+
+ @property viewseparator
+ @type string
+ @default ', '
+ **/
+ viewseparator: ', '
+ });
+
+ $.fn.editabletypes.select2 = Constructor;
+
+}(window.jQuery));
+
+/**
+* Combodate - 1.0.5
+* Dropdown date and time picker.
+* Converts text input into dropdowns to pick day, month, year, hour, minute and second.
+* Uses momentjs as datetime library http://momentjs.com.
+* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
+*
+* Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
+* In combodate:
+* 12:00 pm --> 12:00 (24-h format, midday)
+* 12:00 am --> 00:00 (24-h format, midnight, start of day)
+*
+* Differs from momentjs parse rules:
+* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change)
+* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change)
+*
+*
+* Author: Vitaliy Potapov
+* Project page: http://github.com/vitalets/combodate
+* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
+**/
+(function ($) {
+
+ var Combodate = function (element, options) {
+ this.$element = $(element);
+ if(!this.$element.is('input')) {
+ $.error('Combodate should be applied to INPUT element');
+ return;
+ }
+ this.options = $.extend({}, $.fn.combodate.defaults, options, this.$element.data());
+ this.init();
+ };
+
+ Combodate.prototype = {
+ constructor: Combodate,
+ init: function () {
+ this.map = {
+ //key regexp moment.method
+ day: ['D', 'date'],
+ month: ['M', 'month'],
+ year: ['Y', 'year'],
+ hour: ['[Hh]', 'hours'],
+ minute: ['m', 'minutes'],
+ second: ['s', 'seconds'],
+ ampm: ['[Aa]', '']
+ };
+
+ this.$widget = $('<span class="combodate"></span>').html(this.getTemplate());
+
+ this.initCombos();
+
+ //update original input on change
+ this.$widget.on('change', 'select', $.proxy(function(e) {
+ this.$element.val(this.getValue()).change();
+ // update days count if month or year changes
+ if (this.options.smartDays) {
+ if ($(e.target).is('.month') || $(e.target).is('.year')) {
+ this.fillCombo('day');
+ }
+ }
+ }, this));
+
+ this.$widget.find('select').css('width', 'auto');
+
+ // hide original input and insert widget
+ this.$element.hide().after(this.$widget);
+
+ // set initial value
+ this.setValue(this.$element.val() || this.options.value);
+ },
+
+ /*
+ Replace tokens in template with <select> elements
+ */
+ getTemplate: function() {
+ var tpl = this.options.template;
+
+ //first pass
+ $.each(this.map, function(k, v) {
+ v = v[0];
+ var r = new RegExp(v+'+'),
+ token = v.length > 1 ? v.substring(1, 2) : v;
+
+ tpl = tpl.replace(r, '{'+token+'}');
+ });
+
+ //replace spaces with &nbsp;
+ tpl = tpl.replace(/ /g, '&nbsp;');
+
+ //second pass
+ $.each(this.map, function(k, v) {
+ v = v[0];
+ var token = v.length > 1 ? v.substring(1, 2) : v;
+
+ tpl = tpl.replace('{'+token+'}', '<select class="'+k+'"></select>');
+ });
+
+ return tpl;
+ },
+
+ /*
+ Initialize combos that presents in template
+ */
+ initCombos: function() {
+ for (var k in this.map) {
+ var $c = this.$widget.find('.'+k);
+ // set properties like this.$day, this.$month etc.
+ this['$'+k] = $c.length ? $c : null;
+ // fill with items
+ this.fillCombo(k);
+ }
+ },
+
+ /*
+ Fill combo with items
+ */
+ fillCombo: function(k) {
+ var $combo = this['$'+k];
+ if (!$combo) {
+ return;
+ }
+
+ // define method name to fill items, e.g `fillDays`
+ var f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1);
+ var items = this[f]();
+ var value = $combo.val();
+
+ $combo.empty();
+ for(var i=0; i<items.length; i++) {
+ $combo.append('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
+ }
+
+ $combo.val(value);
+ },
+
+ /*
+ Initialize items of combos. Handles `firstItem` option
+ */
+ fillCommon: function(key) {
+ var values = [],
+ relTime;
+
+ if(this.options.firstItem === 'name') {
+ //need both to support moment ver < 2 and >= 2
+ relTime = moment.relativeTime || moment.langData()._relativeTime;
+ var header = typeof relTime[key] === 'function' ? relTime[key](1, true, key, false) : relTime[key];
+ //take last entry (see momentjs lang files structure)
+ header = header.split(' ').reverse()[0];
+ values.push(['', header]);
+ } else if(this.options.firstItem === 'empty') {
+ values.push(['', '']);
+ }
+ return values;
+ },
+
+
+ /*
+ fill day
+ */
+ fillDay: function() {
+ var items = this.fillCommon('d'), name, i,
+ twoDigit = this.options.template.indexOf('DD') !== -1,
+ daysCount = 31;
+
+ // detect days count (depends on month and year)
+ // originally https://github.com/vitalets/combodate/pull/7
+ if (this.options.smartDays && this.$month && this.$year) {
+ var month = parseInt(this.$month.val(), 10);
+ var year = parseInt(this.$year.val(), 10);
+
+ if (!isNaN(month) && !isNaN(year)) {
+ daysCount = moment([year, month]).daysInMonth();
+ }
+ }
+
+ for (i = 1; i <= daysCount; i++) {
+ name = twoDigit ? this.leadZero(i) : i;
+ items.push([i, name]);
+ }
+ return items;
+ },
+
+ /*
+ fill month
+ */
+ fillMonth: function() {
+ var items = this.fillCommon('M'), name, i,
+ longNames = this.options.template.indexOf('MMMM') !== -1,
+ shortNames = this.options.template.indexOf('MMM') !== -1,
+ twoDigit = this.options.template.indexOf('MM') !== -1;
+
+ for(i=0; i<=11; i++) {
+ if(longNames) {
+ //see https://github.com/timrwood/momentjs.com/pull/36
+ name = moment().date(1).month(i).format('MMMM');
+ } else if(shortNames) {
+ name = moment().date(1).month(i).format('MMM');
+ } else if(twoDigit) {
+ name = this.leadZero(i+1);
+ } else {
+ name = i+1;
+ }
+ items.push([i, name]);
+ }
+ return items;
+ },
+
+ /*
+ fill year
+ */
+ fillYear: function() {
+ var items = [], name, i,
+ longNames = this.options.template.indexOf('YYYY') !== -1;
+
+ for(i=this.options.maxYear; i>=this.options.minYear; i--) {
+ name = longNames ? i : (i+'').substring(2);
+ items[this.options.yearDescending ? 'push' : 'unshift']([i, name]);
+ }
+
+ items = this.fillCommon('y').concat(items);
+
+ return items;
+ },
+
+ /*
+ fill hour
+ */
+ fillHour: function() {
+ var items = this.fillCommon('h'), name, i,
+ h12 = this.options.template.indexOf('h') !== -1,
+ h24 = this.options.template.indexOf('H') !== -1,
+ twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
+ min = h12 ? 1 : 0,
+ max = h12 ? 12 : 23;
+
+ for(i=min; i<=max; i++) {
+ name = twoDigit ? this.leadZero(i) : i;
+ items.push([i, name]);
+ }
+ return items;
+ },
+
+ /*
+ fill minute
+ */
+ fillMinute: function() {
+ var items = this.fillCommon('m'), name, i,
+ twoDigit = this.options.template.indexOf('mm') !== -1;
+
+ for(i=0; i<=59; i+= this.options.minuteStep) {
+ name = twoDigit ? this.leadZero(i) : i;
+ items.push([i, name]);
+ }
+ return items;
+ },
+
+ /*
+ fill second
+ */
+ fillSecond: function() {
+ var items = this.fillCommon('s'), name, i,
+ twoDigit = this.options.template.indexOf('ss') !== -1;
+
+ for(i=0; i<=59; i+= this.options.secondStep) {
+ name = twoDigit ? this.leadZero(i) : i;
+ items.push([i, name]);
+ }
+ return items;
+ },
+
+ /*
+ fill ampm
+ */
+ fillAmpm: function() {
+ var ampmL = this.options.template.indexOf('a') !== -1,
+ ampmU = this.options.template.indexOf('A') !== -1,
+ items = [
+ ['am', ampmL ? 'am' : 'AM'],
+ ['pm', ampmL ? 'pm' : 'PM']
+ ];
+ return items;
+ },
+
+ /*
+ Returns current date value from combos.
+ If format not specified - `options.format` used.
+ If format = `null` - Moment object returned.
+ */
+ getValue: function(format) {
+ var dt, values = {},
+ that = this,
+ notSelected = false;
+
+ //getting selected values
+ $.each(this.map, function(k, v) {
+ if(k === 'ampm') {
+ return;
+ }
+ var def = k === 'day' ? 1 : 0;
+
+ values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def;
+
+ if(isNaN(values[k])) {
+ notSelected = true;
+ return false;
+ }
+ });
+
+ //if at least one visible combo not selected - return empty string
+ if(notSelected) {
+ return '';
+ }
+
+ //convert hours 12h --> 24h
+ if(this.$ampm) {
+ //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
+ if(values.hour === 12) {
+ values.hour = this.$ampm.val() === 'am' ? 0 : 12;
+ } else {
+ values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
+ }
+ }
+
+ dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
+
+ //highlight invalid date
+ this.highlight(dt);
+
+ format = format === undefined ? this.options.format : format;
+ if(format === null) {
+ return dt.isValid() ? dt : null;
+ } else {
+ return dt.isValid() ? dt.format(format) : '';
+ }
+ },
+
+ setValue: function(value) {
+ if(!value) {
+ return;
+ }
+
+ var dt = typeof value === 'string' ? moment(value, this.options.format) : moment(value),
+ that = this,
+ values = {};
+
+ //function to find nearest value in select options
+ function getNearest($select, value) {
+ var delta = {};
+ $select.children('option').each(function(i, opt){
+ var optValue = $(opt).attr('value'),
+ distance;
+
+ if(optValue === '') return;
+ distance = Math.abs(optValue - value);
+ if(typeof delta.distance === 'undefined' || distance < delta.distance) {
+ delta = {value: optValue, distance: distance};
+ }
+ });
+ return delta.value;
+ }
+
+ if(dt.isValid()) {
+ //read values from date object
+ $.each(this.map, function(k, v) {
+ if(k === 'ampm') {
+ return;
+ }
+ values[k] = dt[v[1]]();
+ });
+
+ if(this.$ampm) {
+ //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
+ if(values.hour >= 12) {
+ values.ampm = 'pm';
+ if(values.hour > 12) {
+ values.hour -= 12;
+ }
+ } else {
+ values.ampm = 'am';
+ if(values.hour === 0) {
+ values.hour = 12;
+ }
+ }
+ }
+
+ $.each(values, function(k, v) {
+ //call val() for each existing combo, e.g. this.$hour.val()
+ if(that['$'+k]) {
+
+ if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) {
+ v = getNearest(that['$'+k], v);
+ }
+
+ if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) {
+ v = getNearest(that['$'+k], v);
+ }
+
+ that['$'+k].val(v);
+ }
+ });
+
+ // update days count
+ if (this.options.smartDays) {
+ this.fillCombo('day');
+ }
+
+ this.$element.val(dt.format(this.options.format)).change();
+ }
+ },
+
+ /*
+ highlight combos if date is invalid
+ */
+ highlight: function(dt) {
+ if(!dt.isValid()) {
+ if(this.options.errorClass) {
+ this.$widget.addClass(this.options.errorClass);
+ } else {
+ //store original border color
+ if(!this.borderColor) {
+ this.borderColor = this.$widget.find('select').css('border-color');
+ }
+ this.$widget.find('select').css('border-color', 'red');
+ }
+ } else {
+ if(this.options.errorClass) {
+ this.$widget.removeClass(this.options.errorClass);
+ } else {
+ this.$widget.find('select').css('border-color', this.borderColor);
+ }
+ }
+ },
+
+ leadZero: function(v) {
+ return v <= 9 ? '0' + v : v;
+ },
+
+ destroy: function() {
+ this.$widget.remove();
+ this.$element.removeData('combodate').show();
+ }
+
+ //todo: clear method
+ };
+
+ $.fn.combodate = function ( option ) {
+ var d, args = Array.apply(null, arguments);
+ args.shift();
+
+ //getValue returns date as string / object (not jQuery object)
+ if(option === 'getValue' && this.length && (d = this.eq(0).data('combodate'))) {
+ return d.getValue.apply(d, args);
+ }
+
+ return this.each(function () {
+ var $this = $(this),
+ data = $this.data('combodate'),
+ options = typeof option == 'object' && option;
+ if (!data) {
+ $this.data('combodate', (data = new Combodate(this, options)));
+ }
+ if (typeof option == 'string' && typeof data[option] == 'function') {
+ data[option].apply(data, args);
+ }
+ });
+ };
+
+ $.fn.combodate.defaults = {
+ //in this format value stored in original input
+ format: 'DD-MM-YYYY HH:mm',
+ //in this format items in dropdowns are displayed
+ template: 'D / MMM / YYYY H : mm',
+ //initial value, can be `new Date()`
+ value: null,
+ minYear: 1970,
+ maxYear: 2015,
+ yearDescending: true,
+ minuteStep: 5,
+ secondStep: 1,
+ firstItem: 'empty', //'name', 'empty', 'none'
+ errorClass: null,
+ roundTime: true, // whether to round minutes and seconds if step > 1
+ smartDays: false // whether days in combo depend on selected month: 31, 30, 28
+ };
+
+}(window.jQuery));
+/**
+Combodate input - dropdown date and time picker.
+Based on [combodate](http://vitalets.github.com/combodate) plugin (included). To use it you should manually include [momentjs](http://momentjs.com).
+
+ <script src="js/moment.min.js"></script>
+
+Allows to input:
+
+* only date
+* only time
+* both date and time
+
+Please note, that format is taken from momentjs and **not compatible** with bootstrap-datepicker / jquery UI datepicker.
+Internally value stored as `momentjs` object.
+
+@class combodate
+@extends abstractinput
+@final
+@since 1.4.0
+@example
+<a href="#" id="dob" data-type="combodate" data-pk="1" data-url="/post" data-value="1984-05-15" data-title="Select date"></a>
+<script>
+$(function(){
+ $('#dob').editable({
+ format: 'YYYY-MM-DD',
+ viewformat: 'DD.MM.YYYY',
+ template: 'D / MMMM / YYYY',
+ combodate: {
+ minYear: 2000,
+ maxYear: 2015,
+ minuteStep: 1
+ }
+ }
+ });
+});
+</script>
+**/
+
+/*global moment*/
+
+(function ($) {
+ "use strict";
+
+ var Constructor = function (options) {
+ this.init('combodate', options, Constructor.defaults);
+
+ //by default viewformat equals to format
+ if(!this.options.viewformat) {
+ this.options.viewformat = this.options.format;
+ }
+
+ //try parse combodate config defined as json string in data-combodate
+ options.combodate = $.fn.editableutils.tryParseJson(options.combodate, true);
+
+ //overriding combodate config (as by default jQuery extend() is not recursive)
+ this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, {
+ format: this.options.format,
+ template: this.options.template
+ });
+ };
+
+ $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
+
+ $.extend(Constructor.prototype, {
+ render: function () {
+ this.$input.combodate(this.options.combodate);
+
+ if($.fn.editableform.engine === 'bs3') {
+ this.$input.siblings().find('select').addClass('form-control');
+ }
+
+ if(this.options.inputclass) {
+ this.$input.siblings().find('select').addClass(this.options.inputclass);
+ }
+ //"clear" link
+ /*
+ if(this.options.clear) {
+ this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ this.clear();
+ }, this));
+
+ this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
+ }
+ */
+ },
+
+ value2html: function(value, element) {
+ var text = value ? value.format(this.options.viewformat) : '';
+ //$(element).text(text);
+ Constructor.superclass.value2html.call(this, text, element);
+ },
+
+ html2value: function(html) {
+ return html ? moment(html, this.options.viewformat) : null;
+ },
+
+ value2str: function(value) {
+ return value ? value.format(this.options.format) : '';
+ },
+
+ str2value: function(str) {
+ return str ? moment(str, this.options.format) : null;
+ },
+
+ value2submit: function(value) {
+ return this.value2str(value);
+ },
+
+ value2input: function(value) {
+ this.$input.combodate('setValue', value);
+ },
+
+ input2value: function() {
+ return this.$input.combodate('getValue', null);
+ },
+
+ activate: function() {
+ this.$input.siblings('.combodate').find('select').eq(0).focus();
+ },
+
+ /*
+ clear: function() {
+ this.$input.data('datepicker').date = null;
+ this.$input.find('.active').removeClass('active');
+ },
+ */
+
+ autosubmit: function() {
+
+ }
+
+ });
+
+ Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <input type="text">
+ **/
+ tpl:'<input type="text">',
+ /**
+ @property inputclass
+ @default null
+ **/
+ inputclass: null,
+ /**
+ Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
+ See list of tokens in [momentjs docs](http://momentjs.com/docs/#/parsing/string-format)
+
+ @property format
+ @type string
+ @default YYYY-MM-DD
+ **/
+ format:'YYYY-MM-DD',
+ /**
+ Format used for displaying date. Also applied when converting date from element's text on init.
+ If not specified equals to `format`.
+
+ @property viewformat
+ @type string
+ @default null
+ **/
+ viewformat: null,
+ /**
+ Template used for displaying dropdowns.
+
+ @property template
+ @type string
+ @default D / MMM / YYYY
+ **/
+ template: 'D / MMM / YYYY',
+ /**
+ Configuration of combodate.
+ Full list of options: http://vitalets.github.com/combodate/#docs
+
+ @property combodate
+ @type object
+ @default null
+ **/
+ combodate: null
+
+ /*
+ (not implemented yet)
+ Text shown as clear date button.
+ If <code>false</code> clear button will not be rendered.
+
+ @property clear
+ @type boolean|string
+ @default 'x clear'
+ */
+ //clear: '&times; clear'
+ });
+
+ $.fn.editabletypes.combodate = Constructor;
+
+}(window.jQuery));
+
+/*
+Editableform based on Twitter Bootstrap 3
+*/
+(function ($) {
+ "use strict";
+
+ //store parent methods
+ var pInitInput = $.fn.editableform.Constructor.prototype.initInput;
+
+ $.extend($.fn.editableform.Constructor.prototype, {
+ initTemplate: function() {
+ this.$form = $($.fn.editableform.template);
+ this.$form.find('.control-group').addClass('form-group');
+ this.$form.find('.editable-error-block').addClass('help-block');
+ },
+ initInput: function() {
+ pInitInput.apply(this);
+
+ //for bs3 set default class `input-sm` to standard inputs
+ var emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false;
+ var defaultClass = 'input-sm';
+
+ //bs3 add `form-control` class to standard inputs
+ var stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs'.split(',');
+ if(~$.inArray(this.input.type, stdtypes)) {
+ this.input.$input.addClass('form-control');
+ if(emptyInputClass) {
+ this.input.options.inputclass = defaultClass;
+ this.input.$input.addClass(defaultClass);
+ }
+ }
+
+ //apply bs3 size class also to buttons (to fit size of control)
+ var $btn = this.$form.find('.editable-buttons');
+ var classes = emptyInputClass ? [defaultClass] : this.input.options.inputclass.split(' ');
+ for(var i=0; i<classes.length; i++) {
+ // `btn-sm` is default now
+ /*
+ if(classes[i].toLowerCase() === 'input-sm') {
+ $btn.find('button').addClass('btn-sm');
+ }
+ */
+ if(classes[i].toLowerCase() === 'input-lg') {
+ $btn.find('button').removeClass('btn-sm').addClass('btn-lg');
+ }
+ }
+ }
+ });
+
+ //buttons
+ $.fn.editableform.buttons =
+ '<button type="submit" class="btn btn-primary btn-sm editable-submit">'+
+ '<i class="glyphicon glyphicon-ok"></i>'+
+ '</button>'+
+ '<button type="button" class="btn btn-default btn-sm editable-cancel">'+
+ '<i class="glyphicon glyphicon-remove"></i>'+
+ '</button>';
+
+ //error classes
+ $.fn.editableform.errorGroupClass = 'has-error';
+ $.fn.editableform.errorBlockClass = null;
+ //engine
+ $.fn.editableform.engine = 'bs3';
+}(window.jQuery));
+/**
+* Editable Popover3 (for Bootstrap 3)
+* ---------------------
+* requires bootstrap-popover.js
+*/
+(function ($) {
+ "use strict";
+
+ //extend methods
+ $.extend($.fn.editableContainer.Popup.prototype, {
+ containerName: 'popover',
+ containerDataName: 'bs.popover',
+ innerCss: '.popover-content',
+ defaults: $.fn.popover.Constructor.DEFAULTS,
+
+ initContainer: function(){
+ $.extend(this.containerOptions, {
+ trigger: 'manual',
+ selector: false,
+ content: ' ',
+ template: this.defaults.template
+ });
+
+ //as template property is used in inputs, hide it from popover
+ var t;
+ if(this.$element.data('template')) {
+ t = this.$element.data('template');
+ this.$element.removeData('template');
+ }
+
+ this.call(this.containerOptions);
+
+ if(t) {
+ //restore data('template')
+ this.$element.data('template', t);
+ }
+ },
+
+ /* show */
+ innerShow: function () {
+ this.call('show');
+ },
+
+ /* hide */
+ innerHide: function () {
+ this.call('hide');
+ },
+
+ /* destroy */
+ innerDestroy: function() {
+ this.call('destroy');
+ },
+
+ setContainerOption: function(key, value) {
+ this.container().options[key] = value;
+ },
+
+ /**
+ * move popover to new position. This function mainly copied from bootstrap-popover.
+ */
+ /*jshint laxcomma: true, eqeqeq: false*/
+ setPosition: function () {
+
+ (function() {
+ /*
+ var $tip = this.tip()
+ , inside
+ , pos
+ , actualWidth
+ , actualHeight
+ , placement
+ , tp
+ , tpt
+ , tpb
+ , tpl
+ , tpr;
+
+ placement = typeof this.options.placement === 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement;
+
+ inside = /in/.test(placement);
+
+ $tip
+ // .detach()
+ //vitalets: remove any placement class because otherwise they dont influence on re-positioning of visible popover
+ .removeClass('top right bottom left')
+ .css({ top: 0, left: 0, display: 'block' });
+ // .insertAfter(this.$element);
+
+ pos = this.getPosition(inside);
+
+ actualWidth = $tip[0].offsetWidth;
+ actualHeight = $tip[0].offsetHeight;
+
+ placement = inside ? placement.split(' ')[1] : placement;
+
+ tpb = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2};
+ tpt = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2};
+ tpl = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth};
+ tpr = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width};
+
+ switch (placement) {
+ case 'bottom':
+ if ((tpb.top + actualHeight) > ($(window).scrollTop() + $(window).height())) {
+ if (tpt.top > $(window).scrollTop()) {
+ placement = 'top';
+ } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
+ placement = 'right';
+ } else if (tpl.left > $(window).scrollLeft()) {
+ placement = 'left';
+ } else {
+ placement = 'right';
+ }
+ }
+ break;
+ case 'top':
+ if (tpt.top < $(window).scrollTop()) {
+ if ((tpb.top + actualHeight) < ($(window).scrollTop() + $(window).height())) {
+ placement = 'bottom';
+ } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
+ placement = 'right';
+ } else if (tpl.left > $(window).scrollLeft()) {
+ placement = 'left';
+ } else {
+ placement = 'right';
+ }
+ }
+ break;
+ case 'left':
+ if (tpl.left < $(window).scrollLeft()) {
+ if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
+ placement = 'right';
+ } else if (tpt.top > $(window).scrollTop()) {
+ placement = 'top';
+ } else if (tpt.top > $(window).scrollTop()) {
+ placement = 'bottom';
+ } else {
+ placement = 'right';
+ }
+ }
+ break;
+ case 'right':
+ if ((tpr.left + actualWidth) > ($(window).scrollLeft() + $(window).width())) {
+ if (tpl.left > $(window).scrollLeft()) {
+ placement = 'left';
+ } else if (tpt.top > $(window).scrollTop()) {
+ placement = 'top';
+ } else if (tpt.top > $(window).scrollTop()) {
+ placement = 'bottom';
+ }
+ }
+ break;
+ }
+
+ switch (placement) {
+ case 'bottom':
+ tp = tpb;
+ break;
+ case 'top':
+ tp = tpt;
+ break;
+ case 'left':
+ tp = tpl;
+ break;
+ case 'right':
+ tp = tpr;
+ break;
+ }
+
+ $tip
+ .offset(tp)
+ .addClass(placement)
+ .addClass('in');
+ */
+
+
+ var $tip = this.tip();
+
+ var placement = typeof this.options.placement == 'function' ?
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
+ this.options.placement;
+
+ var autoToken = /\s?auto?\s?/i;
+ var autoPlace = autoToken.test(placement);
+ if (autoPlace) {
+ placement = placement.replace(autoToken, '') || 'top';
+ }
+
+
+ var pos = this.getPosition();
+ var actualWidth = $tip[0].offsetWidth;
+ var actualHeight = $tip[0].offsetHeight;
+
+ if (autoPlace) {
+ var $parent = this.$element.parent();
+
+ var orgPlacement = placement;
+ var docScroll = document.documentElement.scrollTop || document.body.scrollTop;
+ var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth();
+ var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight();
+ var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left;
+
+ placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
+ placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
+ placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
+ placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
+ placement;
+
+ $tip
+ .removeClass(orgPlacement)
+ .addClass(placement);
+ }
+
+
+ var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight);
+
+ this.applyPlacement(calculatedOffset, placement);
+
+
+ }).call(this.container());
+ /*jshint laxcomma: false, eqeqeq: true*/
+ }
+ });
+
+}(window.jQuery));
+
+/* =========================================================
+ * bootstrap-datepicker.js
+ * http://www.eyecon.ro/bootstrap-datepicker
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ * Improvements by Andrew Rowls
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= */
+
+(function( $ ) {
+
+ function UTCDate(){
+ return new Date(Date.UTC.apply(Date, arguments));
+ }
+ function UTCToday(){
+ var today = new Date();
+ return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+ }
+
+ // Picker object
+
+ var Datepicker = function(element, options) {
+ var that = this;
+
+ this._process_options(options);
+
+ this.element = $(element);
+ this.isInline = false;
+ this.isInput = this.element.is('input');
+ this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
+ this.hasInput = this.component && this.element.find('input').length;
+ if(this.component && this.component.length === 0)
+ this.component = false;
+
+ this.picker = $(DPGlobal.template);
+ this._buildEvents();
+ this._attachEvents();
+
+ if(this.isInline) {
+ this.picker.addClass('datepicker-inline').appendTo(this.element);
+ } else {
+ this.picker.addClass('datepicker-dropdown dropdown-menu');
+ }
+
+ if (this.o.rtl){
+ this.picker.addClass('datepicker-rtl');
+ this.picker.find('.prev i, .next i')
+ .toggleClass('icon-arrow-left icon-arrow-right');
+ }
+
+
+ this.viewMode = this.o.startView;
+
+ if (this.o.calendarWeeks)
+ this.picker.find('tfoot th.today')
+ .attr('colspan', function(i, val){
+ return parseInt(val) + 1;
+ });
+
+ this._allow_update = false;
+
+ this.setStartDate(this.o.startDate);
+ this.setEndDate(this.o.endDate);
+ this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
+
+ this.fillDow();
+ this.fillMonths();
+
+ this._allow_update = true;
+
+ this.update();
+ this.showMode();
+
+ if(this.isInline) {
+ this.show();
+ }
+ };
+
+ Datepicker.prototype = {
+ constructor: Datepicker,
+
+ _process_options: function(opts){
+ // Store raw options for reference
+ this._o = $.extend({}, this._o, opts);
+ // Processed options
+ var o = this.o = $.extend({}, this._o);
+
+ // Check if "de-DE" style date is available, if not language should
+ // fallback to 2 letter code eg "de"
+ var lang = o.language;
+ if (!dates[lang]) {
+ lang = lang.split('-')[0];
+ if (!dates[lang])
+ lang = defaults.language;
+ }
+ o.language = lang;
+
+ switch(o.startView){
+ case 2:
+ case 'decade':
+ o.startView = 2;
+ break;
+ case 1:
+ case 'year':
+ o.startView = 1;
+ break;
+ default:
+ o.startView = 0;
+ }
+
+ switch (o.minViewMode) {
+ case 1:
+ case 'months':
+ o.minViewMode = 1;
+ break;
+ case 2:
+ case 'years':
+ o.minViewMode = 2;
+ break;
+ default:
+ o.minViewMode = 0;
+ }
+
+ o.startView = Math.max(o.startView, o.minViewMode);
+
+ o.weekStart %= 7;
+ o.weekEnd = ((o.weekStart + 6) % 7);
+
+ var format = DPGlobal.parseFormat(o.format)
+ if (o.startDate !== -Infinity) {
+ o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
+ }
+ if (o.endDate !== Infinity) {
+ o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
+ }
+
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
+ if (!$.isArray(o.daysOfWeekDisabled))
+ o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
+ o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
+ return parseInt(d, 10);
+ });
+ },
+ _events: [],
+ _secondaryEvents: [],
+ _applyEvents: function(evs){
+ for (var i=0, el, ev; i<evs.length; i++){
+ el = evs[i][0];
+ ev = evs[i][1];
+ el.on(ev);
+ }
+ },
+ _unapplyEvents: function(evs){
+ for (var i=0, el, ev; i<evs.length; i++){
+ el = evs[i][0];
+ ev = evs[i][1];
+ el.off(ev);
+ }
+ },
+ _buildEvents: function(){
+ if (this.isInput) { // single input
+ this._events = [
+ [this.element, {
+ focus: $.proxy(this.show, this),
+ keyup: $.proxy(this.update, this),
+ keydown: $.proxy(this.keydown, this)
+ }]
+ ];
+ }
+ else if (this.component && this.hasInput){ // component: input + button
+ this._events = [
+ // For components that are not readonly, allow keyboard nav
+ [this.element.find('input'), {
+ focus: $.proxy(this.show, this),
+ keyup: $.proxy(this.update, this),
+ keydown: $.proxy(this.keydown, this)
+ }],
+ [this.component, {
+ click: $.proxy(this.show, this)
+ }]
+ ];
+ }
+ else if (this.element.is('div')) { // inline datepicker
+ this.isInline = true;
+ }
+ else {
+ this._events = [
+ [this.element, {
+ click: $.proxy(this.show, this)
+ }]
+ ];
+ }
+
+ this._secondaryEvents = [
+ [this.picker, {
+ click: $.proxy(this.click, this)
+ }],
+ [$(window), {
+ resize: $.proxy(this.place, this)
+ }],
+ [$(document), {
+ mousedown: $.proxy(function (e) {
+ // Clicked outside the datepicker, hide it
+ if (!(
+ this.element.is(e.target) ||
+ this.element.find(e.target).size() ||
+ this.picker.is(e.target) ||
+ this.picker.find(e.target).size()
+ )) {
+ this.hide();
+ }
+ }, this)
+ }]
+ ];
+ },
+ _attachEvents: function(){
+ this._detachEvents();
+ this._applyEvents(this._events);
+ },
+ _detachEvents: function(){
+ this._unapplyEvents(this._events);
+ },
+ _attachSecondaryEvents: function(){
+ this._detachSecondaryEvents();
+ this._applyEvents(this._secondaryEvents);
+ },
+ _detachSecondaryEvents: function(){
+ this._unapplyEvents(this._secondaryEvents);
+ },
+ _trigger: function(event, altdate){
+ var date = altdate || this.date,
+ local_date = new Date(date.getTime() + (date.getTimezoneOffset()*60000));
+
+ this.element.trigger({
+ type: event,
+ date: local_date,
+ format: $.proxy(function(altformat){
+ var format = altformat || this.o.format;
+ return DPGlobal.formatDate(date, format, this.o.language);
+ }, this)
+ });
+ },
+
+ show: function(e) {
+ if (!this.isInline)
+ this.picker.appendTo('body');
+ this.picker.show();
+ this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
+ this.place();
+ this._attachSecondaryEvents();
+ if (e) {
+ e.preventDefault();
+ }
+ this._trigger('show');
+ },
+
+ hide: function(e){
+ if(this.isInline) return;
+ if (!this.picker.is(':visible')) return;
+ this.picker.hide().detach();
+ this._detachSecondaryEvents();
+ this.viewMode = this.o.startView;
+ this.showMode();
+
+ if (
+ this.o.forceParse &&
+ (
+ this.isInput && this.element.val() ||
+ this.hasInput && this.element.find('input').val()
+ )
+ )
+ this.setValue();
+ this._trigger('hide');
+ },
+
+ remove: function() {
+ this.hide();
+ this._detachEvents();
+ this._detachSecondaryEvents();
+ this.picker.remove();
+ delete this.element.data().datepicker;
+ if (!this.isInput) {
+ delete this.element.data().date;
+ }
+ },
+
+ getDate: function() {
+ var d = this.getUTCDate();
+ return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
+ },
+
+ getUTCDate: function() {
+ return this.date;
+ },
+
+ setDate: function(d) {
+ this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+ },
+
+ setUTCDate: function(d) {
+ this.date = d;
+ this.setValue();
+ },
+
+ setValue: function() {
+ var formatted = this.getFormattedDate();
+ if (!this.isInput) {
+ if (this.component){
+ this.element.find('input').val(formatted);
+ }
+ } else {
+ this.element.val(formatted);
+ }
+ },
+
+ getFormattedDate: function(format) {
+ if (format === undefined)
+ format = this.o.format;
+ return DPGlobal.formatDate(this.date, format, this.o.language);
+ },
+
+ setStartDate: function(startDate){
+ this._process_options({startDate: startDate});
+ this.update();
+ this.updateNavArrows();
+ },
+
+ setEndDate: function(endDate){
+ this._process_options({endDate: endDate});
+ this.update();
+ this.updateNavArrows();
+ },
+
+ setDaysOfWeekDisabled: function(daysOfWeekDisabled){
+ this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
+ this.update();
+ this.updateNavArrows();
+ },
+
+ place: function(){
+ if(this.isInline) return;
+ var zIndex = parseInt(this.element.parents().filter(function() {
+ return $(this).css('z-index') != 'auto';
+ }).first().css('z-index'))+10;
+ var offset = this.component ? this.component.parent().offset() : this.element.offset();
+ var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
+ this.picker.css({
+ top: offset.top + height,
+ left: offset.left,
+ zIndex: zIndex
+ });
+ },
+
+ _allow_update: true,
+ update: function(){
+ if (!this._allow_update) return;
+
+ var date, fromArgs = false;
+ if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
+ date = arguments[0];
+ fromArgs = true;
+ } else {
+ date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
+ delete this.element.data().date;
+ }
+
+ this.date = DPGlobal.parseDate(date, this.o.format, this.o.language);
+
+ if(fromArgs) this.setValue();
+
+ if (this.date < this.o.startDate) {
+ this.viewDate = new Date(this.o.startDate);
+ } else if (this.date > this.o.endDate) {
+ this.viewDate = new Date(this.o.endDate);
+ } else {
+ this.viewDate = new Date(this.date);
+ }
+ this.fill();
+ },
+
+ fillDow: function(){
+ var dowCnt = this.o.weekStart,
+ html = '<tr>';
+ if(this.o.calendarWeeks){
+ var cell = '<th class="cw">&nbsp;</th>';
+ html += cell;
+ this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
+ }
+ while (dowCnt < this.o.weekStart + 7) {
+ html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
+ }
+ html += '</tr>';
+ this.picker.find('.datepicker-days thead').append(html);
+ },
+
+ fillMonths: function(){
+ var html = '',
+ i = 0;
+ while (i < 12) {
+ html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
+ }
+ this.picker.find('.datepicker-months td').html(html);
+ },
+
+ setRange: function(range){
+ if (!range || !range.length)
+ delete this.range;
+ else
+ this.range = $.map(range, function(d){ return d.valueOf(); });
+ this.fill();
+ },
+
+ getClassNames: function(date){
+ var cls = [],
+ year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth(),
+ currentDate = this.date.valueOf(),
+ today = new Date();
+ if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
+ cls.push('old');
+ } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
+ cls.push('new');
+ }
+ // Compare internal UTC date with local today, not UTC today
+ if (this.o.todayHighlight &&
+ date.getUTCFullYear() == today.getFullYear() &&
+ date.getUTCMonth() == today.getMonth() &&
+ date.getUTCDate() == today.getDate()) {
+ cls.push('today');
+ }
+ if (currentDate && date.valueOf() == currentDate) {
+ cls.push('active');
+ }
+ if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
+ $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
+ cls.push('disabled');
+ }
+ if (this.range){
+ if (date > this.range[0] && date < this.range[this.range.length-1]){
+ cls.push('range');
+ }
+ if ($.inArray(date.valueOf(), this.range) != -1){
+ cls.push('selected');
+ }
+ }
+ return cls;
+ },
+
+ fill: function() {
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth(),
+ startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
+ startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
+ endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
+ endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
+ currentDate = this.date && this.date.valueOf(),
+ tooltip;
+ this.picker.find('.datepicker-days thead th.datepicker-switch')
+ .text(dates[this.o.language].months[month]+' '+year);
+ this.picker.find('tfoot th.today')
+ .text(dates[this.o.language].today)
+ .toggle(this.o.todayBtn !== false);
+ this.picker.find('tfoot th.clear')
+ .text(dates[this.o.language].clear)
+ .toggle(this.o.clearBtn !== false);
+ this.updateNavArrows();
+ this.fillMonths();
+ var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
+ day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+ prevMonth.setUTCDate(day);
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
+ var nextMonth = new Date(prevMonth);
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ nextMonth = nextMonth.valueOf();
+ var html = [];
+ var clsName;
+ while(prevMonth.valueOf() < nextMonth) {
+ if (prevMonth.getUTCDay() == this.o.weekStart) {
+ html.push('<tr>');
+ if(this.o.calendarWeeks){
+ // ISO 8601: First week contains first thursday.
+ // ISO also states week starts on Monday, but we can be more abstract here.
+ var
+ // Start of current week: based on weekstart/current date
+ ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
+ // Thursday of this week
+ th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
+ // First Thursday of year, year from thursday
+ yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
+ // Calendar week: ms between thursdays, div ms per day, div 7 days
+ calWeek = (th - yth) / 864e5 / 7 + 1;
+ html.push('<td class="cw">'+ calWeek +'</td>');
+
+ }
+ }
+ clsName = this.getClassNames(prevMonth);
+ clsName.push('day');
+
+ var before = this.o.beforeShowDay(prevMonth);
+ if (before === undefined)
+ before = {};
+ else if (typeof(before) === 'boolean')
+ before = {enabled: before};
+ else if (typeof(before) === 'string')
+ before = {classes: before};
+ if (before.enabled === false)
+ clsName.push('disabled');
+ if (before.classes)
+ clsName = clsName.concat(before.classes.split(/\s+/));
+ if (before.tooltip)
+ tooltip = before.tooltip;
+
+ clsName = $.unique(clsName);
+ html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
+ if (prevMonth.getUTCDay() == this.o.weekEnd) {
+ html.push('</tr>');
+ }
+ prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
+ }
+ this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
+ var currentYear = this.date && this.date.getUTCFullYear();
+
+ var months = this.picker.find('.datepicker-months')
+ .find('th:eq(1)')
+ .text(year)
+ .end()
+ .find('span').removeClass('active');
+ if (currentYear && currentYear == year) {
+ months.eq(this.date.getUTCMonth()).addClass('active');
+ }
+ if (year < startYear || year > endYear) {
+ months.addClass('disabled');
+ }
+ if (year == startYear) {
+ months.slice(0, startMonth).addClass('disabled');
+ }
+ if (year == endYear) {
+ months.slice(endMonth+1).addClass('disabled');
+ }
+
+ html = '';
+ year = parseInt(year/10, 10) * 10;
+ var yearCont = this.picker.find('.datepicker-years')
+ .find('th:eq(1)')
+ .text(year + '-' + (year + 9))
+ .end()
+ .find('td');
+ year -= 1;
+ for (var i = -1; i < 11; i++) {
+ html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
+ year += 1;
+ }
+ yearCont.html(html);
+ },
+
+ updateNavArrows: function() {
+ if (!this._allow_update) return;
+
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth();
+ switch (this.viewMode) {
+ case 0:
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
+ this.picker.find('.prev').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.prev').css({visibility: 'visible'});
+ }
+ if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
+ this.picker.find('.next').css({visibility: 'hidden'});
+ } else {
+ this.picker.find('.next').css({visibility: 'visible'});
+ }
+ break;
+ }
+ },
+
+ click: function(e) {
+ e.preventDefault();
+ var target = $(e.target).closest('span, td, th');
+ if (target.length == 1) {
+ switch(target[0].nodeName.toLowerCase()) {
+ case 'th':
+ switch(target[0].className) {
+ case 'datepicker-switch':
+ this.showMode(1);
+ break;
+ case 'prev':
+ case 'next':
+ var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
+ switch(this.viewMode){
+ case 0:
+ this.viewDate = this.moveMonth(this.viewDate, dir);
+ break;
+ case 1:
+ case 2:
+ this.viewDate = this.moveYear(this.viewDate, dir);
+ break;
+ }
+ this.fill();
+ break;
+ case 'today':
+ var date = new Date();
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+
+ this.showMode(-2);
+ var which = this.o.todayBtn == 'linked' ? null : 'view';
+ this._setDate(date, which);
+ break;
+ case 'clear':
+ var element;
+ if (this.isInput)
+ element = this.element;
+ else if (this.component)
+ element = this.element.find('input');
+ if (element)
+ element.val("").change();
+ this._trigger('changeDate');
+ this.update();
+ if (this.o.autoclose)
+ this.hide();
+ break;
+ }
+ break;
+ case 'span':
+ if (!target.is('.disabled')) {
+ this.viewDate.setUTCDate(1);
+ if (target.is('.month')) {
+ var day = 1;
+ var month = target.parent().find('span').index(target);
+ var year = this.viewDate.getUTCFullYear();
+ this.viewDate.setUTCMonth(month);
+ this._trigger('changeMonth', this.viewDate);
+ if (this.o.minViewMode === 1) {
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ } else {
+ var year = parseInt(target.text(), 10)||0;
+ var day = 1;
+ var month = 0;
+ this.viewDate.setUTCFullYear(year);
+ this._trigger('changeYear', this.viewDate);
+ if (this.o.minViewMode === 2) {
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ }
+ this.showMode(-1);
+ this.fill();
+ }
+ break;
+ case 'td':
+ if (target.is('.day') && !target.is('.disabled')){
+ var day = parseInt(target.text(), 10)||1;
+ var year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth();
+ if (target.is('.old')) {
+ if (month === 0) {
+ month = 11;
+ year -= 1;
+ } else {
+ month -= 1;
+ }
+ } else if (target.is('.new')) {
+ if (month == 11) {
+ month = 0;
+ year += 1;
+ } else {
+ month += 1;
+ }
+ }
+ this._setDate(UTCDate(year, month, day,0,0,0,0));
+ }
+ break;
+ }
+ }
+ },
+
+ _setDate: function(date, which){
+ if (!which || which == 'date')
+ this.date = new Date(date);
+ if (!which || which == 'view')
+ this.viewDate = new Date(date);
+ this.fill();
+ this.setValue();
+ this._trigger('changeDate');
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ if (this.o.autoclose && (!which || which == 'date')) {
+ this.hide();
+ }
+ }
+ },
+
+ moveMonth: function(date, dir){
+ if (!dir) return date;
+ var new_date = new Date(date.valueOf()),
+ day = new_date.getUTCDate(),
+ month = new_date.getUTCMonth(),
+ mag = Math.abs(dir),
+ new_month, test;
+ dir = dir > 0 ? 1 : -1;
+ if (mag == 1){
+ test = dir == -1
+ // If going back one month, make sure month is not current month
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+ ? function(){ return new_date.getUTCMonth() == month; }
+ // If going forward one month, make sure month is as expected
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+ : function(){ return new_date.getUTCMonth() != new_month; };
+ new_month = month + dir;
+ new_date.setUTCMonth(new_month);
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+ if (new_month < 0 || new_month > 11)
+ new_month = (new_month + 12) % 12;
+ } else {
+ // For magnitudes >1, move one month at a time...
+ for (var i=0; i<mag; i++)
+ // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
+ new_date = this.moveMonth(new_date, dir);
+ // ...then reset the day, keeping it in the new month
+ new_month = new_date.getUTCMonth();
+ new_date.setUTCDate(day);
+ test = function(){ return new_month != new_date.getUTCMonth(); };
+ }
+ // Common date-resetting loop -- if date is beyond end of month, make it
+ // end of month
+ while (test()){
+ new_date.setUTCDate(--day);
+ new_date.setUTCMonth(new_month);
+ }
+ return new_date;
+ },
+
+ moveYear: function(date, dir){
+ return this.moveMonth(date, dir*12);
+ },
+
+ dateWithinRange: function(date){
+ return date >= this.o.startDate && date <= this.o.endDate;
+ },
+
+ keydown: function(e){
+ if (this.picker.is(':not(:visible)')){
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
+ this.show();
+ return;
+ }
+ var dateChanged = false,
+ dir, day, month,
+ newDate, newViewDate;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ if (!this.o.keyboardNavigation) break;
+ dir = e.keyCode == 37 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 38: // up
+ case 40: // down
+ if (!this.o.keyboardNavigation) break;
+ dir = e.keyCode == 38 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue();
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 13: // enter
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ this.hide();
+ break;
+ }
+ if (dateChanged){
+ this._trigger('changeDate');
+ var element;
+ if (this.isInput) {
+ element = this.element;
+ } else if (this.component){
+ element = this.element.find('input');
+ }
+ if (element) {
+ element.change();
+ }
+ }
+ },
+
+ showMode: function(dir) {
+ if (dir) {
+ this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
+ }
+ /*
+ vitalets: fixing bug of very special conditions:
+ jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
+ Method show() does not set display css correctly and datepicker is not shown.
+ Changed to .css('display', 'block') solve the problem.
+ See https://github.com/vitalets/x-editable/issues/37
+
+ In jquery 1.7.2+ everything works fine.
+ */
+ //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
+ this.updateNavArrows();
+ }
+ };
+
+ var DateRangePicker = function(element, options){
+ this.element = $(element);
+ this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
+ delete options.inputs;
+
+ $(this.inputs)
+ .datepicker(options)
+ .bind('changeDate', $.proxy(this.dateUpdated, this));
+
+ this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
+ this.updateDates();
+ };
+ DateRangePicker.prototype = {
+ updateDates: function(){
+ this.dates = $.map(this.pickers, function(i){ return i.date; });
+ this.updateRanges();
+ },
+ updateRanges: function(){
+ var range = $.map(this.dates, function(d){ return d.valueOf(); });
+ $.each(this.pickers, function(i, p){
+ p.setRange(range);
+ });
+ },
+ dateUpdated: function(e){
+ var dp = $(e.target).data('datepicker'),
+ new_date = dp.getUTCDate(),
+ i = $.inArray(e.target, this.inputs),
+ l = this.inputs.length;
+ if (i == -1) return;
+
+ if (new_date < this.dates[i]){
+ // Date being moved earlier/left
+ while (i>=0 && new_date < this.dates[i]){
+ this.pickers[i--].setUTCDate(new_date);
+ }
+ }
+ else if (new_date > this.dates[i]){
+ // Date being moved later/right
+ while (i<l && new_date > this.dates[i]){
+ this.pickers[i++].setUTCDate(new_date);
+ }
+ }
+ this.updateDates();
+ },
+ remove: function(){
+ $.map(this.pickers, function(p){ p.remove(); });
+ delete this.element.data().datepicker;
+ }
+ };
+
+ function opts_from_el(el, prefix){
+ // Derive options from element data-attrs
+ var data = $(el).data(),
+ out = {}, inkey,
+ replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
+ prefix = new RegExp('^' + prefix.toLowerCase());
+ for (var key in data)
+ if (prefix.test(key)){
+ inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
+ out[inkey] = data[key];
+ }
+ return out;
+ }
+
+ function opts_from_locale(lang){
+ // Derive options from locale plugins
+ var out = {};
+ // Check if "de-DE" style date is available, if not language should
+ // fallback to 2 letter code eg "de"
+ if (!dates[lang]) {
+ lang = lang.split('-')[0]
+ if (!dates[lang])
+ return;
+ }
+ var d = dates[lang];
+ $.each(locale_opts, function(i,k){
+ if (k in d)
+ out[k] = d[k];
+ });
+ return out;
+ }
+
+ var old = $.fn.datepicker;
+ var datepicker = $.fn.datepicker = function ( option ) {
+ var args = Array.apply(null, arguments);
+ args.shift();
+ var internal_return,
+ this_return;
+ this.each(function () {
+ var $this = $(this),
+ data = $this.data('datepicker'),
+ options = typeof option == 'object' && option;
+ if (!data) {
+ var elopts = opts_from_el(this, 'date'),
+ // Preliminary otions
+ xopts = $.extend({}, defaults, elopts, options),
+ locopts = opts_from_locale(xopts.language),
+ // Options priority: js args, data-attrs, locales, defaults
+ opts = $.extend({}, defaults, locopts, elopts, options);
+ if ($this.is('.input-daterange') || opts.inputs){
+ var ropts = {
+ inputs: opts.inputs || $this.find('input').toArray()
+ };
+ $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
+ }
+ else{
+ $this.data('datepicker', (data = new Datepicker(this, opts)));
+ }
+ }
+ if (typeof option == 'string' && typeof data[option] == 'function') {
+ internal_return = data[option].apply(data, args);
+ if (internal_return !== undefined)
+ return false;
+ }
+ });
+ if (internal_return !== undefined)
+ return internal_return;
+ else
+ return this;
+ };
+
+ var defaults = $.fn.datepicker.defaults = {
+ autoclose: false,
+ beforeShowDay: $.noop,
+ calendarWeeks: false,
+ clearBtn: false,
+ daysOfWeekDisabled: [],
+ endDate: Infinity,
+ forceParse: true,
+ format: 'mm/dd/yyyy',
+ keyboardNavigation: true,
+ language: 'en',
+ minViewMode: 0,
+ rtl: false,
+ startDate: -Infinity,
+ startView: 0,
+ todayBtn: false,
+ todayHighlight: false,
+ weekStart: 0
+ };
+ var locale_opts = $.fn.datepicker.locale_opts = [
+ 'format',
+ 'rtl',
+ 'weekStart'
+ ];
+ $.fn.datepicker.Constructor = Datepicker;
+ var dates = $.fn.datepicker.dates = {
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today",
+ clear: "Clear"
+ }
+ };
+
+ var DPGlobal = {
+ modes: [
+ {
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }],
+ isLeapYear: function (year) {
+ return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
+ },
+ getDaysInMonth: function (year, month) {
+ return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
+ },
+ validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
+ nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
+ parseFormat: function(format){
+ // IE treats \0 as a string end in inputs (truncating the value),
+ // so it's a bad format delimiter, anyway
+ var separators = format.replace(this.validParts, '\0').split('\0'),
+ parts = format.match(this.validParts);
+ if (!separators || !separators.length || !parts || parts.length === 0){
+ throw new Error("Invalid date format.");
+ }
+ return {separators: separators, parts: parts};
+ },
+ parseDate: function(date, format, language) {
+ if (date instanceof Date) return date;
+ if (typeof format === 'string')
+ format = DPGlobal.parseFormat(format);
+ if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
+ var part_re = /([\-+]\d+)([dmwy])/,
+ parts = date.match(/([\-+]\d+)([dmwy])/g),
+ part, dir;
+ date = new Date();
+ for (var i=0; i<parts.length; i++) {
+ part = part_re.exec(parts[i]);
+ dir = parseInt(part[1]);
+ switch(part[2]){
+ case 'd':
+ date.setUTCDate(date.getUTCDate() + dir);
+ break;
+ case 'm':
+ date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
+ break;
+ case 'w':
+ date.setUTCDate(date.getUTCDate() + dir * 7);
+ break;
+ case 'y':
+ date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
+ break;
+ }
+ }
+ return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
+ }
+ var parts = date && date.match(this.nonpunctuation) || [],
+ date = new Date(),
+ parsed = {},
+ setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
+ setters_map = {
+ yyyy: function(d,v){ return d.setUTCFullYear(v); },
+ yy: function(d,v){ return d.setUTCFullYear(2000+v); },
+ m: function(d,v){
+ v -= 1;
+ while (v<0) v += 12;
+ v %= 12;
+ d.setUTCMonth(v);
+ while (d.getUTCMonth() != v)
+ d.setUTCDate(d.getUTCDate()-1);
+ return d;
+ },
+ d: function(d,v){ return d.setUTCDate(v); }
+ },
+ val, filtered, part;
+ setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
+ setters_map['dd'] = setters_map['d'];
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+ var fparts = format.parts.slice();
+ // Remove noop parts
+ if (parts.length != fparts.length) {
+ fparts = $(fparts).filter(function(i,p){
+ return $.inArray(p, setters_order) !== -1;
+ }).toArray();
+ }
+ // Process remainder
+ if (parts.length == fparts.length) {
+ for (var i=0, cnt = fparts.length; i < cnt; i++) {
+ val = parseInt(parts[i], 10);
+ part = fparts[i];
+ if (isNaN(val)) {
+ switch(part) {
+ case 'MM':
+ filtered = $(dates[language].months).filter(function(){
+ var m = this.slice(0, parts[i].length),
+ p = parts[i].slice(0, m.length);
+ return m == p;
+ });
+ val = $.inArray(filtered[0], dates[language].months) + 1;
+ break;
+ case 'M':
+ filtered = $(dates[language].monthsShort).filter(function(){
+ var m = this.slice(0, parts[i].length),
+ p = parts[i].slice(0, m.length);
+ return m == p;
+ });
+ val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
+ break;
+ }
+ }
+ parsed[part] = val;
+ }
+ for (var i=0, s; i<setters_order.length; i++){
+ s = setters_order[i];
+ if (s in parsed && !isNaN(parsed[s]))
+ setters_map[s](date, parsed[s]);
+ }
+ }
+ return date;
+ },
+ formatDate: function(date, format, language){
+ if (typeof format === 'string')
+ format = DPGlobal.parseFormat(format);
+ var val = {
+ d: date.getUTCDate(),
+ D: dates[language].daysShort[date.getUTCDay()],
+ DD: dates[language].days[date.getUTCDay()],
+ m: date.getUTCMonth() + 1,
+ M: dates[language].monthsShort[date.getUTCMonth()],
+ MM: dates[language].months[date.getUTCMonth()],
+ yy: date.getUTCFullYear().toString().substring(2),
+ yyyy: date.getUTCFullYear()
+ };
+ val.dd = (val.d < 10 ? '0' : '') + val.d;
+ val.mm = (val.m < 10 ? '0' : '') + val.m;
+ var date = [],
+ seps = $.extend([], format.separators);
+ for (var i=0, cnt = format.parts.length; i <= cnt; i++) {
+ if (seps.length)
+ date.push(seps.shift());
+ date.push(val[format.parts[i]]);
+ }
+ return date.join('');
+ },
+ headTemplate: '<thead>'+
+ '<tr>'+
+ '<th class="prev"><i class="icon-arrow-left"/></th>'+
+ '<th colspan="5" class="datepicker-switch"></th>'+
+ '<th class="next"><i class="icon-arrow-right"/></th>'+
+ '</tr>'+
+ '</thead>',
+ contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
+ footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'
+ };
+ DPGlobal.template = '<div class="datepicker">'+
+ '<div class="datepicker-days">'+
+ '<table class=" table-condensed">'+
+ DPGlobal.headTemplate+
+ '<tbody></tbody>'+
+ DPGlobal.footTemplate+
+ '</table>'+
+ '</div>'+
+ '<div class="datepicker-months">'+
+ '<table class="table-condensed">'+
+ DPGlobal.headTemplate+
+ DPGlobal.contTemplate+
+ DPGlobal.footTemplate+
+ '</table>'+
+ '</div>'+
+ '<div class="datepicker-years">'+
+ '<table class="table-condensed">'+
+ DPGlobal.headTemplate+
+ DPGlobal.contTemplate+
+ DPGlobal.footTemplate+
+ '</table>'+
+ '</div>'+
+ '</div>';
+
+ $.fn.datepicker.DPGlobal = DPGlobal;
+
+
+ /* DATEPICKER NO CONFLICT
+ * =================== */
+
+ $.fn.datepicker.noConflict = function(){
+ $.fn.datepicker = old;
+ return this;
+ };
+
+
+ /* DATEPICKER DATA-API
+ * ================== */
+
+ $(document).on(
+ 'focus.datepicker.data-api click.datepicker.data-api',
+ '[data-provide="datepicker"]',
+ function(e){
+ var $this = $(this);
+ if ($this.data('datepicker')) return;
+ e.preventDefault();
+ // component click requires us to explicitly show it
+ datepicker.call($this, 'show');
+ }
+ );
+ $(function(){
+ //$('[data-provide="datepicker-inline"]').datepicker();
+ //vit: changed to support noConflict()
+ datepicker.call($('[data-provide="datepicker-inline"]'));
+ });
+
+}( window.jQuery ));
+
+/**
+Bootstrap-datepicker.
+Description and examples: https://github.com/eternicode/bootstrap-datepicker.
+For **i18n** you should include js file from here: https://github.com/eternicode/bootstrap-datepicker/tree/master/js/locales
+and set `language` option.
+Since 1.4.0 date has different appearance in **popup** and **inline** modes.
+
+@class date
+@extends abstractinput
+@final
+@example
+<a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-title="Select date">15/05/1984</a>
+<script>
+$(function(){
+ $('#dob').editable({
+ format: 'yyyy-mm-dd',
+ viewformat: 'dd/mm/yyyy',
+ datepicker: {
+ weekStart: 1
+ }
+ }
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ //store bootstrap-datepicker as bdateicker to exclude conflict with jQuery UI one
+ $.fn.bdatepicker = $.fn.datepicker.noConflict();
+ if(!$.fn.datepicker) { //if there were no other datepickers, keep also original name
+ $.fn.datepicker = $.fn.bdatepicker;
+ }
+
+ var Date = function (options) {
+ this.init('date', options, Date.defaults);
+ this.initPicker(options, Date.defaults);
+ };
+
+ $.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
+
+ $.extend(Date.prototype, {
+ initPicker: function(options, defaults) {
+ //'format' is set directly from settings or data-* attributes
+
+ //by default viewformat equals to format
+ if(!this.options.viewformat) {
+ this.options.viewformat = this.options.format;
+ }
+
+ //try parse datepicker config defined as json string in data-datepicker
+ options.datepicker = $.fn.editableutils.tryParseJson(options.datepicker, true);
+
+ //overriding datepicker config (as by default jQuery extend() is not recursive)
+ //since 1.4 datepicker internally uses viewformat instead of format. Format is for submit only
+ this.options.datepicker = $.extend({}, defaults.datepicker, options.datepicker, {
+ format: this.options.viewformat
+ });
+
+ //language
+ this.options.datepicker.language = this.options.datepicker.language || 'en';
+
+ //store DPglobal
+ this.dpg = $.fn.bdatepicker.DPGlobal;
+
+ //store parsed formats
+ this.parsedFormat = this.dpg.parseFormat(this.options.format);
+ this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);
+ },
+
+ render: function () {
+ this.$input.bdatepicker(this.options.datepicker);
+
+ //"clear" link
+ if(this.options.clear) {
+ this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ this.clear();
+ }, this));
+
+ this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
+ }
+ },
+
+ value2html: function(value, element) {
+ var text = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
+ Date.superclass.value2html.call(this, text, element);
+ },
+
+ html2value: function(html) {
+ return this.parseDate(html, this.parsedViewFormat);
+ },
+
+ value2str: function(value) {
+ return value ? this.dpg.formatDate(value, this.parsedFormat, this.options.datepicker.language) : '';
+ },
+
+ str2value: function(str) {
+ return this.parseDate(str, this.parsedFormat);
+ },
+
+ value2submit: function(value) {
+ return this.value2str(value);
+ },
+
+ value2input: function(value) {
+ this.$input.bdatepicker('update', value);
+ },
+
+ input2value: function() {
+ return this.$input.data('datepicker').date;
+ },
+
+ activate: function() {
+ },
+
+ clear: function() {
+ this.$input.data('datepicker').date = null;
+ this.$input.find('.active').removeClass('active');
+ if(!this.options.showbuttons) {
+ this.$input.closest('form').submit();
+ }
+ },
+
+ autosubmit: function() {
+ this.$input.on('mouseup', '.day', function(e){
+ if($(e.currentTarget).is('.old') || $(e.currentTarget).is('.new')) {
+ return;
+ }
+ var $form = $(this).closest('form');
+ setTimeout(function() {
+ $form.submit();
+ }, 200);
+ });
+ //changedate is not suitable as it triggered when showing datepicker. see #149
+ /*
+ this.$input.on('changeDate', function(e){
+ var $form = $(this).closest('form');
+ setTimeout(function() {
+ $form.submit();
+ }, 200);
+ });
+ */
+ },
+
+ /*
+ For incorrect date bootstrap-datepicker returns current date that is not suitable
+ for datefield.
+ This function returns null for incorrect date.
+ */
+ parseDate: function(str, format) {
+ var date = null, formattedBack;
+ if(str) {
+ date = this.dpg.parseDate(str, format, this.options.datepicker.language);
+ if(typeof str === 'string') {
+ formattedBack = this.dpg.formatDate(date, format, this.options.datepicker.language);
+ if(str !== formattedBack) {
+ date = null;
+ }
+ }
+ }
+ return date;
+ }
+
+ });
+
+ Date.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <div></div>
+ **/
+ tpl:'<div class="editable-date well"></div>',
+ /**
+ @property inputclass
+ @default null
+ **/
+ inputclass: null,
+ /**
+ Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
+ Possible tokens are: <code>d, dd, m, mm, yy, yyyy</code>
+
+ @property format
+ @type string
+ @default yyyy-mm-dd
+ **/
+ format:'yyyy-mm-dd',
+ /**
+ Format used for displaying date. Also applied when converting date from element's text on init.
+ If not specified equals to <code>format</code>
+
+ @property viewformat
+ @type string
+ @default null
+ **/
+ viewformat: null,
+ /**
+ Configuration of datepicker.
+ Full list of options: http://bootstrap-datepicker.readthedocs.org/en/latest/options.html
+
+ @property datepicker
+ @type object
+ @default {
+ weekStart: 0,
+ startView: 0,
+ minViewMode: 0,
+ autoclose: false
+ }
+ **/
+ datepicker:{
+ weekStart: 0,
+ startView: 0,
+ minViewMode: 0,
+ autoclose: false
+ },
+ /**
+ Text shown as clear date button.
+ If <code>false</code> clear button will not be rendered.
+
+ @property clear
+ @type boolean|string
+ @default 'x clear'
+ **/
+ clear: '&times; clear'
+ });
+
+ $.fn.editabletypes.date = Date;
+
+}(window.jQuery));
+
+/**
+Bootstrap datefield input - modification for inline mode.
+Shows normal <input type="text"> and binds popup datepicker.
+Automatically shown in inline mode.
+
+@class datefield
+@extends date
+
+@since 1.4.0
+**/
+(function ($) {
+ "use strict";
+
+ var DateField = function (options) {
+ this.init('datefield', options, DateField.defaults);
+ this.initPicker(options, DateField.defaults);
+ };
+
+ $.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
+
+ $.extend(DateField.prototype, {
+ render: function () {
+ this.$input = this.$tpl.find('input');
+ this.setClass();
+ this.setAttr('placeholder');
+
+ //bootstrap-datepicker is set `bdateicker` to exclude conflict with jQuery UI one. (in date.js)
+ this.$tpl.bdatepicker(this.options.datepicker);
+
+ //need to disable original event handlers
+ this.$input.off('focus keydown');
+
+ //update value of datepicker
+ this.$input.keyup($.proxy(function(){
+ this.$tpl.removeData('date');
+ this.$tpl.bdatepicker('update');
+ }, this));
+
+ },
+
+ value2input: function(value) {
+ this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
+ this.$tpl.bdatepicker('update');
+ },
+
+ input2value: function() {
+ return this.html2value(this.$input.val());
+ },
+
+ activate: function() {
+ $.fn.editabletypes.text.prototype.activate.call(this);
+ },
+
+ autosubmit: function() {
+ //reset autosubmit to empty
+ }
+ });
+
+ DateField.defaults = $.extend({}, $.fn.editabletypes.date.defaults, {
+ /**
+ @property tpl
+ **/
+ tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
+ /**
+ @property inputclass
+ @default 'input-small'
+ **/
+ inputclass: 'input-small',
+
+ /* datepicker config */
+ datepicker: {
+ weekStart: 0,
+ startView: 0,
+ minViewMode: 0,
+ autoclose: true
+ }
+ });
+
+ $.fn.editabletypes.datefield = DateField;
+
+}(window.jQuery));
+/**
+Bootstrap-datetimepicker.
+Based on [smalot bootstrap-datetimepicker plugin](https://github.com/smalot/bootstrap-datetimepicker).
+Before usage you should manually include dependent js and css:
+
+ <link href="css/datetimepicker.css" rel="stylesheet" type="text/css"></link>
+ <script src="js/bootstrap-datetimepicker.js"></script>
+
+For **i18n** you should include js file from here: https://github.com/smalot/bootstrap-datetimepicker/tree/master/js/locales
+and set `language` option.
+
+@class datetime
+@extends abstractinput
+@final
+@since 1.4.4
+@example
+<a href="#" id="last_seen" data-type="datetime" data-pk="1" data-url="/post" title="Select date & time">15/03/2013 12:45</a>
+<script>
+$(function(){
+ $('#last_seen').editable({
+ format: 'yyyy-mm-dd hh:ii',
+ viewformat: 'dd/mm/yyyy hh:ii',
+ datetimepicker: {
+ weekStart: 1
+ }
+ }
+ });
+});
+</script>
+**/
+(function ($) {
+ "use strict";
+
+ var DateTime = function (options) {
+ this.init('datetime', options, DateTime.defaults);
+ this.initPicker(options, DateTime.defaults);
+ };
+
+ $.fn.editableutils.inherit(DateTime, $.fn.editabletypes.abstractinput);
+
+ $.extend(DateTime.prototype, {
+ initPicker: function(options, defaults) {
+ //'format' is set directly from settings or data-* attributes
+
+ //by default viewformat equals to format
+ if(!this.options.viewformat) {
+ this.options.viewformat = this.options.format;
+ }
+
+ //try parse datetimepicker config defined as json string in data-datetimepicker
+ options.datetimepicker = $.fn.editableutils.tryParseJson(options.datetimepicker, true);
+
+ //overriding datetimepicker config (as by default jQuery extend() is not recursive)
+ //since 1.4 datetimepicker internally uses viewformat instead of format. Format is for submit only
+ this.options.datetimepicker = $.extend({}, defaults.datetimepicker, options.datetimepicker, {
+ format: this.options.viewformat
+ });
+
+ //language
+ this.options.datetimepicker.language = this.options.datetimepicker.language || 'en';
+
+ //store DPglobal
+ this.dpg = $.fn.datetimepicker.DPGlobal;
+
+ //store parsed formats
+ this.parsedFormat = this.dpg.parseFormat(this.options.format, this.options.formatType);
+ this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat, this.options.formatType);
+ },
+
+ render: function () {
+ this.$input.datetimepicker(this.options.datetimepicker);
+
+ //adjust container position when viewMode changes
+ //see https://github.com/smalot/bootstrap-datetimepicker/pull/80
+ this.$input.on('changeMode', function(e) {
+ var f = $(this).closest('form').parent();
+ //timeout here, otherwise container changes position before form has new size
+ setTimeout(function(){
+ f.triggerHandler('resize');
+ }, 0);
+ });
+
+ //"clear" link
+ if(this.options.clear) {
+ this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ this.clear();
+ }, this));
+
+ this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
+ }
+ },
+
+ value2html: function(value, element) {
+ //formatDate works with UTCDate!
+ var text = value ? this.dpg.formatDate(this.toUTC(value), this.parsedViewFormat, this.options.datetimepicker.language, this.options.formatType) : '';
+ if(element) {
+ DateTime.superclass.value2html.call(this, text, element);
+ } else {
+ return text;
+ }
+ },
+
+ html2value: function(html) {
+ //parseDate return utc date!
+ var value = this.parseDate(html, this.parsedViewFormat);
+ return value ? this.fromUTC(value) : null;
+ },
+
+ value2str: function(value) {
+ //formatDate works with UTCDate!
+ return value ? this.dpg.formatDate(this.toUTC(value), this.parsedFormat, this.options.datetimepicker.language, this.options.formatType) : '';
+ },
+
+ str2value: function(str) {
+ //parseDate return utc date!
+ var value = this.parseDate(str, this.parsedFormat);
+ return value ? this.fromUTC(value) : null;
+ },
+
+ value2submit: function(value) {
+ return this.value2str(value);
+ },
+
+ value2input: function(value) {
+ if(value) {
+ this.$input.data('datetimepicker').setDate(value);
+ }
+ },
+
+ input2value: function() {
+ //date may be cleared, in that case getDate() triggers error
+ var dt = this.$input.data('datetimepicker');
+ return dt.date ? dt.getDate() : null;
+ },
+
+ activate: function() {
+ },
+
+ clear: function() {
+ this.$input.data('datetimepicker').date = null;
+ this.$input.find('.active').removeClass('active');
+ if(!this.options.showbuttons) {
+ this.$input.closest('form').submit();
+ }
+ },
+
+ autosubmit: function() {
+ this.$input.on('mouseup', '.minute', function(e){
+ var $form = $(this).closest('form');
+ setTimeout(function() {
+ $form.submit();
+ }, 200);
+ });
+ },
+
+ //convert date from local to utc
+ toUTC: function(value) {
+ return value ? new Date(value.valueOf() - value.getTimezoneOffset() * 60000) : value;
+ },
+
+ //convert date from utc to local
+ fromUTC: function(value) {
+ return value ? new Date(value.valueOf() + value.getTimezoneOffset() * 60000) : value;
+ },
+
+ /*
+ For incorrect date bootstrap-datetimepicker returns current date that is not suitable
+ for datetimefield.
+ This function returns null for incorrect date.
+ */
+ parseDate: function(str, format) {
+ var date = null, formattedBack;
+ if(str) {
+ date = this.dpg.parseDate(str, format, this.options.datetimepicker.language, this.options.formatType);
+ if(typeof str === 'string') {
+ formattedBack = this.dpg.formatDate(date, format, this.options.datetimepicker.language, this.options.formatType);
+ if(str !== formattedBack) {
+ date = null;
+ }
+ }
+ }
+ return date;
+ }
+
+ });
+
+ DateTime.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
+ /**
+ @property tpl
+ @default <div></div>
+ **/
+ tpl:'<div class="editable-date well"></div>',
+ /**
+ @property inputclass
+ @default null
+ **/
+ inputclass: null,
+ /**
+ Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
+ Possible tokens are: <code>d, dd, m, mm, yy, yyyy, h, i</code>
+
+ @property format
+ @type string
+ @default yyyy-mm-dd hh:ii
+ **/
+ format:'yyyy-mm-dd hh:ii',
+ formatType:'standard',
+ /**
+ Format used for displaying date. Also applied when converting date from element's text on init.
+ If not specified equals to <code>format</code>
+
+ @property viewformat
+ @type string
+ @default null
+ **/
+ viewformat: null,
+ /**
+ Configuration of datetimepicker.
+ Full list of options: https://github.com/smalot/bootstrap-datetimepicker
+
+ @property datetimepicker
+ @type object
+ @default { }
+ **/
+ datetimepicker:{
+ todayHighlight: false,
+ autoclose: false
+ },
+ /**
+ Text shown as clear date button.
+ If <code>false</code> clear button will not be rendered.
+
+ @property clear
+ @type boolean|string
+ @default 'x clear'
+ **/
+ clear: '&times; clear'
+ });
+
+ $.fn.editabletypes.datetime = DateTime;
+
+}(window.jQuery));
+/**
+Bootstrap datetimefield input - datetime input for inline mode.
+Shows normal <input type="text"> and binds popup datetimepicker.
+Automatically shown in inline mode.
+
+@class datetimefield
+@extends datetime
+
+**/
+(function ($) {
+ "use strict";
+
+ var DateTimeField = function (options) {
+ this.init('datetimefield', options, DateTimeField.defaults);
+ this.initPicker(options, DateTimeField.defaults);
+ };
+
+ $.fn.editableutils.inherit(DateTimeField, $.fn.editabletypes.datetime);
+
+ $.extend(DateTimeField.prototype, {
+ render: function () {
+ this.$input = this.$tpl.find('input');
+ this.setClass();
+ this.setAttr('placeholder');
+
+ this.$tpl.datetimepicker(this.options.datetimepicker);
+
+ //need to disable original event handlers
+ this.$input.off('focus keydown');
+
+ //update value of datepicker
+ this.$input.keyup($.proxy(function(){
+ this.$tpl.removeData('date');
+ this.$tpl.datetimepicker('update');
+ }, this));
+
+ },
+
+ value2input: function(value) {
+ this.$input.val(this.value2html(value));
+ this.$tpl.datetimepicker('update');
+ },
+
+ input2value: function() {
+ return this.html2value(this.$input.val());
+ },
+
+ activate: function() {
+ $.fn.editabletypes.text.prototype.activate.call(this);
+ },
+
+ autosubmit: function() {
+ //reset autosubmit to empty
+ }
+ });
+
+ DateTimeField.defaults = $.extend({}, $.fn.editabletypes.datetime.defaults, {
+ /**
+ @property tpl
+ **/
+ tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
+ /**
+ @property inputclass
+ @default 'input-medium'
+ **/
+ inputclass: 'input-medium',
+
+ /* datetimepicker config */
+ datetimepicker:{
+ todayHighlight: false,
+ autoclose: true
+ }
+ });
+
+ $.fn.editabletypes.datetimefield = DateTimeField;
+
+}(window.jQuery)); \ No newline at end of file
diff --git a/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js
new file mode 100644
index 0000000..e2703ae
--- /dev/null
+++ b/views/js/bootstrap3-editable-1.5.1/bootstrap3-editable/js/bootstrap-editable.min.js
@@ -0,0 +1,7 @@
+/*! X-editable - v1.5.1
+* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
+* http://github.com/vitalets/x-editable
+* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
+!function(a){"use strict";var b=function(b,c){this.options=a.extend({},a.fn.editableform.defaults,c),this.$div=a(b),this.options.scope||(this.options.scope=this)};b.prototype={constructor:b,initInput:function(){this.input=this.options.input,this.value=this.input.str2value(this.options.value),this.input.prerender()},initTemplate:function(){this.$form=a(a.fn.editableform.template)},initButtons:function(){var b=this.$form.find(".editable-buttons");b.append(a.fn.editableform.buttons),"bottom"===this.options.showbuttons&&b.addClass("editable-buttons-bottom")},render:function(){this.$loading=a(a.fn.editableform.loading),this.$div.empty().append(this.$loading),this.initTemplate(),this.options.showbuttons?this.initButtons():this.$form.find(".editable-buttons").remove(),this.showLoading(),this.isSaving=!1,this.$div.triggerHandler("rendering"),this.initInput(),this.$form.find("div.editable-input").append(this.input.$tpl),this.$div.append(this.$form),a.when(this.input.render()).then(a.proxy(function(){if(this.options.showbuttons||this.input.autosubmit(),this.$form.find(".editable-cancel").click(a.proxy(this.cancel,this)),this.input.error)this.error(this.input.error),this.$form.find(".editable-submit").attr("disabled",!0),this.input.$input.attr("disabled",!0),this.$form.submit(function(a){a.preventDefault()});else{this.error(!1),this.input.$input.removeAttr("disabled"),this.$form.find(".editable-submit").removeAttr("disabled");var b=null===this.value||void 0===this.value||""===this.value?this.options.defaultValue:this.value;this.input.value2input(b),this.$form.submit(a.proxy(this.submit,this))}this.$div.triggerHandler("rendered"),this.showForm(),this.input.postrender&&this.input.postrender()},this))},cancel:function(){this.$div.triggerHandler("cancel")},showLoading:function(){var a,b;this.$form?(a=this.$form.outerWidth(),b=this.$form.outerHeight(),a&&this.$loading.width(a),b&&this.$loading.height(b),this.$form.hide()):(a=this.$loading.parent().width(),a&&this.$loading.width(a)),this.$loading.show()},showForm:function(a){this.$loading.hide(),this.$form.show(),a!==!1&&this.input.activate(),this.$div.triggerHandler("show")},error:function(b){var c,d=this.$form.find(".control-group"),e=this.$form.find(".editable-error-block");if(b===!1)d.removeClass(a.fn.editableform.errorGroupClass),e.removeClass(a.fn.editableform.errorBlockClass).empty().hide();else{if(b){c=(""+b).split("\n");for(var f=0;f<c.length;f++)c[f]=a("<div>").text(c[f]).html();b=c.join("<br>")}d.addClass(a.fn.editableform.errorGroupClass),e.addClass(a.fn.editableform.errorBlockClass).html(b).show()}},submit:function(b){b.stopPropagation(),b.preventDefault();var c=this.input.input2value(),d=this.validate(c);if("object"===a.type(d)&&void 0!==d.newValue){if(c=d.newValue,this.input.value2input(c),"string"==typeof d.msg)return this.error(d.msg),this.showForm(),void 0}else if(d)return this.error(d),this.showForm(),void 0;if(!this.options.savenochange&&this.input.value2str(c)==this.input.value2str(this.value))return this.$div.triggerHandler("nochange"),void 0;var e=this.input.value2submit(c);this.isSaving=!0,a.when(this.save(e)).done(a.proxy(function(a){this.isSaving=!1;var b="function"==typeof this.options.success?this.options.success.call(this.options.scope,a,c):null;return b===!1?(this.error(!1),this.showForm(!1),void 0):"string"==typeof b?(this.error(b),this.showForm(),void 0):(b&&"object"==typeof b&&b.hasOwnProperty("newValue")&&(c=b.newValue),this.error(!1),this.value=c,this.$div.triggerHandler("save",{newValue:c,submitValue:e,response:a}),void 0)},this)).fail(a.proxy(function(a){this.isSaving=!1;var b;b="function"==typeof this.options.error?this.options.error.call(this.options.scope,a,c):"string"==typeof a?a:a.responseText||a.statusText||"Unknown error!",this.error(b),this.showForm()},this))},save:function(b){this.options.pk=a.fn.editableutils.tryParseJson(this.options.pk,!0);var c,d="function"==typeof this.options.pk?this.options.pk.call(this.options.scope):this.options.pk,e=!!("function"==typeof this.options.url||this.options.url&&("always"===this.options.send||"auto"===this.options.send&&null!==d&&void 0!==d));return e?(this.showLoading(),c={name:this.options.name||"",value:b,pk:d},"function"==typeof this.options.params?c=this.options.params.call(this.options.scope,c):(this.options.params=a.fn.editableutils.tryParseJson(this.options.params,!0),a.extend(c,this.options.params)),"function"==typeof this.options.url?this.options.url.call(this.options.scope,c):a.ajax(a.extend({url:this.options.url,data:c,type:"POST"},this.options.ajaxOptions))):void 0},validate:function(a){return void 0===a&&(a=this.value),"function"==typeof this.options.validate?this.options.validate.call(this.options.scope,a):void 0},option:function(a,b){a in this.options&&(this.options[a]=b),"value"===a&&this.setValue(b)},setValue:function(a,b){this.value=b?this.input.str2value(a):a,this.$form&&this.$form.is(":visible")&&this.input.value2input(this.value)}},a.fn.editableform=function(c){var d=arguments;return this.each(function(){var e=a(this),f=e.data("editableform"),g="object"==typeof c&&c;f||e.data("editableform",f=new b(this,g)),"string"==typeof c&&f[c].apply(f,Array.prototype.slice.call(d,1))})},a.fn.editableform.Constructor=b,a.fn.editableform.defaults={type:"text",url:null,params:null,name:null,pk:null,value:null,defaultValue:null,send:"auto",validate:null,success:null,error:null,ajaxOptions:null,showbuttons:!0,scope:null,savenochange:!1},a.fn.editableform.template='<form class="form-inline editableform"><div class="control-group"><div><div class="editable-input"></div><div class="editable-buttons"></div></div><div class="editable-error-block"></div></div></form>',a.fn.editableform.loading='<div class="editableform-loading"></div>',a.fn.editableform.buttons='<button type="submit" class="editable-submit">ok</button><button type="button" class="editable-cancel">cancel</button>',a.fn.editableform.errorGroupClass=null,a.fn.editableform.errorBlockClass="editable-error",a.fn.editableform.engine="jquery"}(window.jQuery),function(a){"use strict";a.fn.editableutils={inherit:function(a,b){var c=function(){};c.prototype=b.prototype,a.prototype=new c,a.prototype.constructor=a,a.superclass=b.prototype},setCursorPosition:function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}},tryParseJson:function(a,b){if("string"==typeof a&&a.length&&a.match(/^[\{\[].*[\}\]]$/))if(b)try{a=new Function("return "+a)()}catch(c){}finally{return a}else a=new Function("return "+a)();return a},sliceObj:function(b,c,d){var e,f,g={};if(!a.isArray(c)||!c.length)return g;for(var h=0;h<c.length;h++)e=c[h],b.hasOwnProperty(e)&&(g[e]=b[e]),d!==!0&&(f=e.toLowerCase(),b.hasOwnProperty(f)&&(g[e]=b[f]));return g},getConfigData:function(b){var c={};return a.each(b.data(),function(a,b){("object"!=typeof b||b&&"object"==typeof b&&(b.constructor===Object||b.constructor===Array))&&(c[a]=b)}),c},objectKeys:function(a){if(Object.keys)return Object.keys(a);if(a!==Object(a))throw new TypeError("Object.keys called on a non-object");var b,c=[];for(b in a)Object.prototype.hasOwnProperty.call(a,b)&&c.push(b);return c},escape:function(b){return a("<div>").text(b).html()},itemsByValue:function(b,c,d){if(!c||null===b)return[];if("function"!=typeof d){var e=d||"value";d=function(a){return a[e]}}var f=a.isArray(b),g=[],h=this;return a.each(c,function(c,e){if(e.children)g=g.concat(h.itemsByValue(b,e.children,d));else if(f)a.grep(b,function(a){return a==(e&&"object"==typeof e?d(e):e)}).length&&g.push(e);else{var i=e&&"object"==typeof e?d(e):e;b==i&&g.push(e)}}),g},createInput:function(b){var c,d,e,f=b.type;return"date"===f&&("inline"===b.mode?a.fn.editabletypes.datefield?f="datefield":a.fn.editabletypes.dateuifield&&(f="dateuifield"):a.fn.editabletypes.date?f="date":a.fn.editabletypes.dateui&&(f="dateui"),"date"!==f||a.fn.editabletypes.date||(f="combodate")),"datetime"===f&&"inline"===b.mode&&(f="datetimefield"),"wysihtml5"!==f||a.fn.editabletypes[f]||(f="textarea"),"function"==typeof a.fn.editabletypes[f]?(c=a.fn.editabletypes[f],d=this.sliceObj(b,this.objectKeys(c.defaults)),e=new c(d)):(a.error("Unknown type: "+f),!1)},supportsTransitions:function(){var a=document.body||document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e<d.length;e++)if("string"==typeof b[d[e]+c])return!0;return!1}}}(window.jQuery),function(a){"use strict";var b=function(a,b){this.init(a,b)},c=function(a,b){this.init(a,b)};b.prototype={containerName:null,containerDataName:null,innerCss:null,containerClass:"editable-container editable-popup",defaults:{},init:function(c,d){this.$element=a(c),this.options=a.extend({},a.fn.editableContainer.defaults,d),this.splitOptions(),this.formOptions.scope=this.$element[0],this.initContainer(),this.delayedHide=!1,this.$element.on("destroyed",a.proxy(function(){this.destroy()},this)),a(document).data("editable-handlers-attached")||(a(document).on("keyup.editable",function(b){27===b.which&&a(".editable-open").editableContainer("hide")}),a(document).on("click.editable",function(c){var d,e=a(c.target),f=[".editable-container",".ui-datepicker-header",".datepicker",".modal-backdrop",".bootstrap-wysihtml5-insert-image-modal",".bootstrap-wysihtml5-insert-link-modal"];if(a.contains(document.documentElement,c.target)&&!e.is(document)){for(d=0;d<f.length;d++)if(e.is(f[d])||e.parents(f[d]).length)return;b.prototype.closeOthers(c.target)}}),a(document).data("editable-handlers-attached",!0))},splitOptions:function(){if(this.containerOptions={},this.formOptions={},!a.fn[this.containerName])throw new Error(this.containerName+" not found. Have you included corresponding js file?");for(var b in this.options)b in this.defaults?this.containerOptions[b]=this.options[b]:this.formOptions[b]=this.options[b]},tip:function(){return this.container()?this.container().$tip:null},container:function(){var a;return this.containerDataName&&(a=this.$element.data(this.containerDataName))?a:a=this.$element.data(this.containerName)},call:function(){this.$element[this.containerName].apply(this.$element,arguments)},initContainer:function(){this.call(this.containerOptions)},renderForm:function(){this.$form.editableform(this.formOptions).on({save:a.proxy(this.save,this),nochange:a.proxy(function(){this.hide("nochange")},this),cancel:a.proxy(function(){this.hide("cancel")},this),show:a.proxy(function(){this.delayedHide?(this.hide(this.delayedHide.reason),this.delayedHide=!1):this.setPosition()},this),rendering:a.proxy(this.setPosition,this),resize:a.proxy(this.setPosition,this),rendered:a.proxy(function(){this.$element.triggerHandler("shown",a(this.options.scope).data("editable"))},this)}).editableform("render")},show:function(b){this.$element.addClass("editable-open"),b!==!1&&this.closeOthers(this.$element[0]),this.innerShow(),this.tip().addClass(this.containerClass),this.$form,this.$form=a("<div>"),this.tip().is(this.innerCss)?this.tip().append(this.$form):this.tip().find(this.innerCss).append(this.$form),this.renderForm()},hide:function(a){if(this.tip()&&this.tip().is(":visible")&&this.$element.hasClass("editable-open")){if(this.$form.data("editableform").isSaving)return this.delayedHide={reason:a},void 0;this.delayedHide=!1,this.$element.removeClass("editable-open"),this.innerHide(),this.$element.triggerHandler("hidden",a||"manual")}},innerShow:function(){},innerHide:function(){},toggle:function(a){this.container()&&this.tip()&&this.tip().is(":visible")?this.hide():this.show(a)},setPosition:function(){},save:function(a,b){this.$element.triggerHandler("save",b),this.hide("save")},option:function(a,b){this.options[a]=b,a in this.containerOptions?(this.containerOptions[a]=b,this.setContainerOption(a,b)):(this.formOptions[a]=b,this.$form&&this.$form.editableform("option",a,b))},setContainerOption:function(a,b){this.call("option",a,b)},destroy:function(){this.hide(),this.innerDestroy(),this.$element.off("destroyed"),this.$element.removeData("editableContainer")},innerDestroy:function(){},closeOthers:function(b){a(".editable-open").each(function(c,d){if(d!==b&&!a(d).find(b).length){var e=a(d),f=e.data("editableContainer");f&&("cancel"===f.options.onblur?e.data("editableContainer").hide("onblur"):"submit"===f.options.onblur&&e.data("editableContainer").tip().find("form").submit())}})},activate:function(){this.tip&&this.tip().is(":visible")&&this.$form&&this.$form.data("editableform").input.activate()}},a.fn.editableContainer=function(d){var e=arguments;return this.each(function(){var f=a(this),g="editableContainer",h=f.data(g),i="object"==typeof d&&d,j="inline"===i.mode?c:b;h||f.data(g,h=new j(this,i)),"string"==typeof d&&h[d].apply(h,Array.prototype.slice.call(e,1))})},a.fn.editableContainer.Popup=b,a.fn.editableContainer.Inline=c,a.fn.editableContainer.defaults={value:null,placement:"top",autohide:!0,onblur:"cancel",anim:!1,mode:"popup"},jQuery.event.special.destroyed={remove:function(a){a.handler&&a.handler()}}}(window.jQuery),function(a){"use strict";a.extend(a.fn.editableContainer.Inline.prototype,a.fn.editableContainer.Popup.prototype,{containerName:"editableform",innerCss:".editable-inline",containerClass:"editable-container editable-inline",initContainer:function(){this.$tip=a("<span></span>"),this.options.anim||(this.options.anim=0)},splitOptions:function(){this.containerOptions={},this.formOptions=this.options},tip:function(){return this.$tip},innerShow:function(){this.$element.hide(),this.tip().insertAfter(this.$element).show()},innerHide:function(){this.$tip.hide(this.options.anim,a.proxy(function(){this.$element.show(),this.innerDestroy()},this))},innerDestroy:function(){this.tip()&&this.tip().empty().remove()}})}(window.jQuery),function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.editable.defaults,c,a.fn.editableutils.getConfigData(this.$element)),this.options.selector?this.initLive():this.init(),this.options.highlight&&!a.fn.editableutils.supportsTransitions()&&(this.options.highlight=!1)};b.prototype={constructor:b,init:function(){var b,c=!1;if(this.options.name=this.options.name||this.$element.attr("id"),this.options.scope=this.$element[0],this.input=a.fn.editableutils.createInput(this.options),this.input){switch(void 0===this.options.value||null===this.options.value?(this.value=this.input.html2value(a.trim(this.$element.html())),c=!0):(this.options.value=a.fn.editableutils.tryParseJson(this.options.value,!0),this.value="string"==typeof this.options.value?this.input.str2value(this.options.value):this.options.value),this.$element.addClass("editable"),"textarea"===this.input.type&&this.$element.addClass("editable-pre-wrapped"),"manual"!==this.options.toggle?(this.$element.addClass("editable-click"),this.$element.on(this.options.toggle+".editable",a.proxy(function(a){if(this.options.disabled||a.preventDefault(),"mouseenter"===this.options.toggle)this.show();else{var b="click"!==this.options.toggle;this.toggle(b)}},this))):this.$element.attr("tabindex",-1),"function"==typeof this.options.display&&(this.options.autotext="always"),this.options.autotext){case"always":b=!0;break;case"auto":b=!a.trim(this.$element.text()).length&&null!==this.value&&void 0!==this.value&&!c;break;default:b=!1}a.when(b?this.render():!0).then(a.proxy(function(){this.options.disabled?this.disable():this.enable(),this.$element.triggerHandler("init",this)},this))}},initLive:function(){var b=this.options.selector;this.options.selector=!1,this.options.autotext="never",this.$element.on(this.options.toggle+".editable",b,a.proxy(function(b){var c=a(b.target);c.data("editable")||(c.hasClass(this.options.emptyclass)&&c.empty(),c.editable(this.options).trigger(b))},this))},render:function(a){return this.options.display!==!1?this.input.value2htmlFinal?this.input.value2html(this.value,this.$element[0],this.options.display,a):"function"==typeof this.options.display?this.options.display.call(this.$element[0],this.value,a):this.input.value2html(this.value,this.$element[0]):void 0},enable:function(){this.options.disabled=!1,this.$element.removeClass("editable-disabled"),this.handleEmpty(this.isEmpty),"manual"!==this.options.toggle&&"-1"===this.$element.attr("tabindex")&&this.$element.removeAttr("tabindex")},disable:function(){this.options.disabled=!0,this.hide(),this.$element.addClass("editable-disabled"),this.handleEmpty(this.isEmpty),this.$element.attr("tabindex",-1)},toggleDisabled:function(){this.options.disabled?this.enable():this.disable()},option:function(b,c){return b&&"object"==typeof b?(a.each(b,a.proxy(function(b,c){this.option(a.trim(b),c)},this)),void 0):(this.options[b]=c,"disabled"===b?c?this.disable():this.enable():("value"===b&&this.setValue(c),this.container&&this.container.option(b,c),this.input.option&&this.input.option(b,c),void 0))},handleEmpty:function(b){this.options.display!==!1&&(this.isEmpty=void 0!==b?b:"function"==typeof this.input.isEmpty?this.input.isEmpty(this.$element):""===a.trim(this.$element.html()),this.options.disabled?this.isEmpty&&(this.$element.empty(),this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass)):this.isEmpty?(this.$element.html(this.options.emptytext),this.options.emptyclass&&this.$element.addClass(this.options.emptyclass)):this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass))},show:function(b){if(!this.options.disabled){if(this.container){if(this.container.tip().is(":visible"))return}else{var c=a.extend({},this.options,{value:this.value,input:this.input});this.$element.editableContainer(c),this.$element.on("save.internal",a.proxy(this.save,this)),this.container=this.$element.data("editableContainer")}this.container.show(b)}},hide:function(){this.container&&this.container.hide()},toggle:function(a){this.container&&this.container.tip().is(":visible")?this.hide():this.show(a)},save:function(a,b){if(this.options.unsavedclass){var c=!1;c=c||"function"==typeof this.options.url,c=c||this.options.display===!1,c=c||void 0!==b.response,c=c||this.options.savenochange&&this.input.value2str(this.value)!==this.input.value2str(b.newValue),c?this.$element.removeClass(this.options.unsavedclass):this.$element.addClass(this.options.unsavedclass)}if(this.options.highlight){var d=this.$element,e=d.css("background-color");d.css("background-color",this.options.highlight),setTimeout(function(){"transparent"===e&&(e=""),d.css("background-color",e),d.addClass("editable-bg-transition"),setTimeout(function(){d.removeClass("editable-bg-transition")},1700)},10)}this.setValue(b.newValue,!1,b.response)},validate:function(){return"function"==typeof this.options.validate?this.options.validate.call(this,this.value):void 0},setValue:function(b,c,d){this.value=c?this.input.str2value(b):b,this.container&&this.container.option("value",this.value),a.when(this.render(d)).then(a.proxy(function(){this.handleEmpty()},this))},activate:function(){this.container&&this.container.activate()},destroy:function(){this.disable(),this.container&&this.container.destroy(),this.input.destroy(),"manual"!==this.options.toggle&&(this.$element.removeClass("editable-click"),this.$element.off(this.options.toggle+".editable")),this.$element.off("save.internal"),this.$element.removeClass("editable editable-open editable-disabled"),this.$element.removeData("editable")}},a.fn.editable=function(c){var d={},e=arguments,f="editable";switch(c){case"validate":return this.each(function(){var b,c=a(this),e=c.data(f);e&&(b=e.validate())&&(d[e.options.name]=b)}),d;case"getValue":return 2===arguments.length&&arguments[1]===!0?d=this.eq(0).data(f).value:this.each(function(){var b=a(this),c=b.data(f);c&&void 0!==c.value&&null!==c.value&&(d[c.options.name]=c.input.value2submit(c.value))}),d;case"submit":var g=arguments[1]||{},h=this,i=this.editable("validate");if(a.isEmptyObject(i)){var j={};if(1===h.length){var k=h.data("editable"),l={name:k.options.name||"",value:k.input.value2submit(k.value),pk:"function"==typeof k.options.pk?k.options.pk.call(k.options.scope):k.options.pk};"function"==typeof k.options.params?l=k.options.params.call(k.options.scope,l):(k.options.params=a.fn.editableutils.tryParseJson(k.options.params,!0),a.extend(l,k.options.params)),j={url:k.options.url,data:l,type:"POST"},g.success=g.success||k.options.success,g.error=g.error||k.options.error}else{var m=this.editable("getValue");j={url:g.url,data:m,type:"POST"}}j.success="function"==typeof g.success?function(a){g.success.call(h,a,g)}:a.noop,j.error="function"==typeof g.error?function(){g.error.apply(h,arguments)}:a.noop,g.ajaxOptions&&a.extend(j,g.ajaxOptions),g.data&&a.extend(j.data,g.data),a.ajax(j)}else"function"==typeof g.error&&g.error.call(h,i);return this}return this.each(function(){var d=a(this),g=d.data(f),h="object"==typeof c&&c;return h&&h.selector?(g=new b(this,h),void 0):(g||d.data(f,g=new b(this,h)),"string"==typeof c&&g[c].apply(g,Array.prototype.slice.call(e,1)),void 0)})},a.fn.editable.defaults={type:"text",disabled:!1,toggle:"click",emptytext:"Empty",autotext:"auto",value:null,display:null,emptyclass:"editable-empty",unsavedclass:"editable-unsaved",selector:null,highlight:"#FFFF80"}}(window.jQuery),function(a){"use strict";a.fn.editabletypes={};var b=function(){};b.prototype={init:function(b,c,d){this.type=b,this.options=a.extend({},d,c)},prerender:function(){this.$tpl=a(this.options.tpl),this.$input=this.$tpl,this.$clear=null,this.error=null},render:function(){},value2html:function(b,c){a(c)[this.options.escape?"text":"html"](a.trim(b))},html2value:function(b){return a("<div>").html(b).text()},value2str:function(a){return a},str2value:function(a){return a},value2submit:function(a){return a},value2input:function(a){this.$input.val(a)},input2value:function(){return this.$input.val()},activate:function(){this.$input.is(":visible")&&this.$input.focus()},clear:function(){this.$input.val(null)},escape:function(b){return a("<div>").text(b).html()},autosubmit:function(){},destroy:function(){},setClass:function(){this.options.inputclass&&this.$input.addClass(this.options.inputclass)},setAttr:function(a){void 0!==this.options[a]&&null!==this.options[a]&&this.$input.attr(a,this.options[a])},option:function(a,b){this.options[a]=b}},b.defaults={tpl:"",inputclass:null,escape:!0,scope:null,showbuttons:!0},a.extend(a.fn.editabletypes,{abstractinput:b})}(window.jQuery),function(a){"use strict";var b=function(){};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){var b=a.Deferred();return this.error=null,this.onSourceReady(function(){this.renderList(),b.resolve()},function(){this.error=this.options.sourceError,b.resolve()}),b.promise()},html2value:function(){return null},value2html:function(b,c,d,e){var f=a.Deferred(),g=function(){"function"==typeof d?d.call(c,b,this.sourceData,e):this.value2htmlFinal(b,c),f.resolve()};return null===b?g.call(this):this.onSourceReady(g,function(){f.resolve()}),f.promise()},onSourceReady:function(b,c){var d;if(a.isFunction(this.options.source)?(d=this.options.source.call(this.options.scope),this.sourceData=null):d=this.options.source,this.options.sourceCache&&a.isArray(this.sourceData))return b.call(this),void 0;try{d=a.fn.editableutils.tryParseJson(d,!1)}catch(e){return c.call(this),void 0}if("string"==typeof d){if(this.options.sourceCache){var f,g=d;if(a(document).data(g)||a(document).data(g,{}),f=a(document).data(g),f.loading===!1&&f.sourceData)return this.sourceData=f.sourceData,this.doPrepend(),b.call(this),void 0;if(f.loading===!0)return f.callbacks.push(a.proxy(function(){this.sourceData=f.sourceData,this.doPrepend(),b.call(this)},this)),f.err_callbacks.push(a.proxy(c,this)),void 0;f.loading=!0,f.callbacks=[],f.err_callbacks=[]}var h=a.extend({url:d,type:"get",cache:!1,dataType:"json",success:a.proxy(function(d){f&&(f.loading=!1),this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(f&&(f.sourceData=this.sourceData,a.each(f.callbacks,function(){this.call()})),this.doPrepend(),b.call(this)):(c.call(this),f&&a.each(f.err_callbacks,function(){this.call()}))},this),error:a.proxy(function(){c.call(this),f&&(f.loading=!1,a.each(f.err_callbacks,function(){this.call()}))},this)},this.options.sourceOptions);a.ajax(h)}else this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(this.doPrepend(),b.call(this)):c.call(this)},doPrepend:function(){null!==this.options.prepend&&void 0!==this.options.prepend&&(a.isArray(this.prependData)||(a.isFunction(this.options.prepend)&&(this.options.prepend=this.options.prepend.call(this.options.scope)),this.options.prepend=a.fn.editableutils.tryParseJson(this.options.prepend,!0),"string"==typeof this.options.prepend&&(this.options.prepend={"":this.options.prepend}),this.prependData=this.makeArray(this.options.prepend)),a.isArray(this.prependData)&&a.isArray(this.sourceData)&&(this.sourceData=this.prependData.concat(this.sourceData)))},renderList:function(){},value2htmlFinal:function(){},makeArray:function(b){var c,d,e,f,g=[];if(!b||"string"==typeof b)return null;if(a.isArray(b)){f=function(a,b){return d={value:a,text:b},c++>=2?!1:void 0};for(var h=0;h<b.length;h++)e=b[h],"object"==typeof e?(c=0,a.each(e,f),1===c?g.push(d):c>1&&(e.children&&(e.children=this.makeArray(e.children)),g.push(e))):g.push({value:e,text:e})}else a.each(b,function(a,b){g.push({value:a,text:b})});return g},option:function(a,b){this.options[a]=b,"source"===a&&(this.sourceData=null),"prepend"===a&&(this.prependData=null)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{source:null,prepend:!1,sourceError:"Error when loading list",sourceCache:!0,sourceOptions:null}),a.fn.editabletypes.list=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("text",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.renderClear(),this.setClass(),this.setAttr("placeholder")},activate:function(){this.$input.is(":visible")&&(this.$input.focus(),a.fn.editableutils.setCursorPosition(this.$input.get(0),this.$input.val().length),this.toggleClear&&this.toggleClear())},renderClear:function(){this.options.clear&&(this.$clear=a('<span class="editable-clear-x"></span>'),this.$input.after(this.$clear).css("padding-right",24).keyup(a.proxy(function(b){if(!~a.inArray(b.keyCode,[40,38,9,13,27])){clearTimeout(this.t);var c=this;this.t=setTimeout(function(){c.toggleClear(b)},100)}},this)).parent().css("position","relative"),this.$clear.click(a.proxy(this.clear,this)))},postrender:function(){},toggleClear:function(){if(this.$clear){var a=this.$input.val().length,b=this.$clear.is(":visible");a&&!b&&this.$clear.show(),!a&&b&&this.$clear.hide()}},clear:function(){this.$clear.hide(),this.$input.val("").focus()}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="text">',placeholder:null,clear:!0}),a.fn.editabletypes.text=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("textarea",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass(),this.setAttr("placeholder"),this.setAttr("rows"),this.$input.keydown(function(b){b.ctrlKey&&13===b.which&&a(this).closest("form").submit()})},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:"<textarea></textarea>",inputclass:"input-large",placeholder:null,rows:7}),a.fn.editabletypes.textarea=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("select",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.list),a.extend(b.prototype,{renderList:function(){this.$input.empty();var b=function(c,d){var e;if(a.isArray(d))for(var f=0;f<d.length;f++)e={},d[f].children?(e.label=d[f].text,c.append(b(a("<optgroup>",e),d[f].children))):(e.value=d[f].value,d[f].disabled&&(e.disabled=!0),c.append(a("<option>",e).text(d[f].text)));return c};b(this.$input,this.sourceData),this.setClass(),this.$input.on("keydown.editable",function(b){13===b.which&&a(this).closest("form").submit()})},value2htmlFinal:function(b,c){var d="",e=a.fn.editableutils.itemsByValue(b,this.sourceData);e.length&&(d=e[0].text),a.fn.editabletypes.abstractinput.prototype.value2html.call(this,d,c)},autosubmit:function(){this.$input.off("keydown.editable").on("change.editable",function(){a(this).closest("form").submit()})}}),b.defaults=a.extend({},a.fn.editabletypes.list.defaults,{tpl:"<select></select>"}),a.fn.editabletypes.select=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("checklist",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.list),a.extend(b.prototype,{renderList:function(){var b;if(this.$tpl.empty(),a.isArray(this.sourceData)){for(var c=0;c<this.sourceData.length;c++)b=a("<label>").append(a("<input>",{type:"checkbox",value:this.sourceData[c].value})).append(a("<span>").text(" "+this.sourceData[c].text)),a("<div>").append(b).appendTo(this.$tpl);this.$input=this.$tpl.find('input[type="checkbox"]'),this.setClass()}},value2str:function(b){return a.isArray(b)?b.sort().join(a.trim(this.options.separator)):""},str2value:function(b){var c,d=null;return"string"==typeof b&&b.length?(c=new RegExp("\\s*"+a.trim(this.options.separator)+"\\s*"),d=b.split(c)):d=a.isArray(b)?b:[b],d},value2input:function(b){this.$input.prop("checked",!1),a.isArray(b)&&b.length&&this.$input.each(function(c,d){var e=a(d);a.each(b,function(a,b){e.val()==b&&e.prop("checked",!0)})})},input2value:function(){var b=[];return this.$input.filter(":checked").each(function(c,d){b.push(a(d).val())}),b},value2htmlFinal:function(b,c){var d=[],e=a.fn.editableutils.itemsByValue(b,this.sourceData),f=this.options.escape;e.length?(a.each(e,function(b,c){var e=f?a.fn.editableutils.escape(c.text):c.text;d.push(e)}),a(c).html(d.join("<br>"))):a(c).empty()},activate:function(){this.$input.first().focus()},autosubmit:function(){this.$input.on("keydown",function(b){13===b.which&&a(this).closest("form").submit()})}}),b.defaults=a.extend({},a.fn.editabletypes.list.defaults,{tpl:'<div class="editable-checklist"></div>',inputclass:null,separator:","}),a.fn.editabletypes.checklist=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("password",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),a.extend(b.prototype,{value2html:function(b,c){b?a(c).text("[hidden]"):a(c).empty()},html2value:function(){return null}}),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="password">'}),a.fn.editabletypes.password=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("email",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="email">'}),a.fn.editabletypes.email=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("url",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="url">'}),a.fn.editabletypes.url=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("tel",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="tel">'}),a.fn.editabletypes.tel=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("number",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.text),a.extend(b.prototype,{render:function(){b.superclass.render.call(this),this.setAttr("min"),this.setAttr("max"),this.setAttr("step")},postrender:function(){this.$clear&&this.$clear.css({right:24})}}),b.defaults=a.extend({},a.fn.editabletypes.text.defaults,{tpl:'<input type="number">',inputclass:"input-mini",min:null,max:null,step:null}),a.fn.editabletypes.number=b}(window.jQuery),function(a){"use strict";
+var b=function(a){this.init("range",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.number),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.filter("input"),this.setClass(),this.setAttr("min"),this.setAttr("max"),this.setAttr("step"),this.$input.on("input",function(){a(this).siblings("output").text(a(this).val())})},activate:function(){this.$input.focus()}}),b.defaults=a.extend({},a.fn.editabletypes.number.defaults,{tpl:'<input type="range"><output style="width: 30px; display: inline-block"></output>',inputclass:"input-medium"}),a.fn.editabletypes.range=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("time",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass()}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="time">'}),a.fn.editabletypes.time=b}(window.jQuery),function(a){"use strict";var b=function(c){if(this.init("select2",c,b.defaults),c.select2=c.select2||{},this.sourceData=null,c.placeholder&&(c.select2.placeholder=c.placeholder),!c.select2.tags&&c.source){var d=c.source;a.isFunction(c.source)&&(d=c.source.call(c.scope)),"string"==typeof d?(c.select2.ajax=c.select2.ajax||{},c.select2.ajax.data||(c.select2.ajax.data=function(a){return{query:a}}),c.select2.ajax.results||(c.select2.ajax.results=function(a){return{results:a}}),c.select2.ajax.url=d):(this.sourceData=this.convertSource(d),c.select2.data=this.sourceData)}if(this.options.select2=a.extend({},b.defaults.select2,c.select2),this.isMultiple=this.options.select2.tags||this.options.select2.multiple,this.isRemote="ajax"in this.options.select2,this.idFunc=this.options.select2.id,"function"!=typeof this.idFunc){var e=this.idFunc||"id";this.idFunc=function(a){return a[e]}}this.formatSelection=this.options.select2.formatSelection,"function"!=typeof this.formatSelection&&(this.formatSelection=function(a){return a.text})};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass(),this.isRemote&&this.$input.on("select2-loaded",a.proxy(function(a){this.sourceData=a.items.results},this)),this.isMultiple&&this.$input.on("change",function(){a(this).closest("form").parent().triggerHandler("resize")})},value2html:function(c,d){var e,f="",g=this;this.options.select2.tags?e=c:this.sourceData&&(e=a.fn.editableutils.itemsByValue(c,this.sourceData,this.idFunc)),a.isArray(e)?(f=[],a.each(e,function(a,b){f.push(b&&"object"==typeof b?g.formatSelection(b):b)})):e&&(f=g.formatSelection(e)),f=a.isArray(f)?f.join(this.options.viewseparator):f,b.superclass.value2html.call(this,f,d)},html2value:function(a){return this.options.select2.tags?this.str2value(a,this.options.viewseparator):null},value2input:function(b){if(a.isArray(b)&&(b=b.join(this.getSeparator())),this.$input.data("select2")?this.$input.val(b).trigger("change",!0):(this.$input.val(b),this.$input.select2(this.options.select2)),this.isRemote&&!this.isMultiple&&!this.options.select2.initSelection){var c=this.options.select2.id,d=this.options.select2.formatSelection;if(!c&&!d){var e=a(this.options.scope);if(!e.data("editable").isEmpty){var f={id:b,text:e.text()};this.$input.select2("data",f)}}}},input2value:function(){return this.$input.select2("val")},str2value:function(b,c){if("string"!=typeof b||!this.isMultiple)return b;c=c||this.getSeparator();var d,e,f;if(null===b||b.length<1)return null;for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d},autosubmit:function(){this.$input.on("change",function(b,c){c||a(this).closest("form").submit()})},getSeparator:function(){return this.options.select2.separator||a.fn.select2.defaults.separator},convertSource:function(b){if(a.isArray(b)&&b.length&&void 0!==b[0].value)for(var c=0;c<b.length;c++)void 0!==b[c].value&&(b[c].id=b[c].value,delete b[c].value);return b},destroy:function(){this.$input.data("select2")&&this.$input.select2("destroy")}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="hidden">',select2:null,placeholder:null,source:null,viewseparator:", "}),a.fn.editabletypes.select2=b}(window.jQuery),function(a){var b=function(b,c){return this.$element=a(b),this.$element.is("input")?(this.options=a.extend({},a.fn.combodate.defaults,c,this.$element.data()),this.init(),void 0):(a.error("Combodate should be applied to INPUT element"),void 0)};b.prototype={constructor:b,init:function(){this.map={day:["D","date"],month:["M","month"],year:["Y","year"],hour:["[Hh]","hours"],minute:["m","minutes"],second:["s","seconds"],ampm:["[Aa]",""]},this.$widget=a('<span class="combodate"></span>').html(this.getTemplate()),this.initCombos(),this.$widget.on("change","select",a.proxy(function(b){this.$element.val(this.getValue()).change(),this.options.smartDays&&(a(b.target).is(".month")||a(b.target).is(".year"))&&this.fillCombo("day")},this)),this.$widget.find("select").css("width","auto"),this.$element.hide().after(this.$widget),this.setValue(this.$element.val()||this.options.value)},getTemplate:function(){var b=this.options.template;return a.each(this.map,function(a,c){c=c[0];var d=new RegExp(c+"+"),e=c.length>1?c.substring(1,2):c;b=b.replace(d,"{"+e+"}")}),b=b.replace(/ /g,"&nbsp;"),a.each(this.map,function(a,c){c=c[0];var d=c.length>1?c.substring(1,2):c;b=b.replace("{"+d+"}",'<select class="'+a+'"></select>')}),b},initCombos:function(){for(var a in this.map){var b=this.$widget.find("."+a);this["$"+a]=b.length?b:null,this.fillCombo(a)}},fillCombo:function(a){var b=this["$"+a];if(b){var c="fill"+a.charAt(0).toUpperCase()+a.slice(1),d=this[c](),e=b.val();b.empty();for(var f=0;f<d.length;f++)b.append('<option value="'+d[f][0]+'">'+d[f][1]+"</option>");b.val(e)}},fillCommon:function(a){var b,c=[];if("name"===this.options.firstItem){b=moment.relativeTime||moment.langData()._relativeTime;var d="function"==typeof b[a]?b[a](1,!0,a,!1):b[a];d=d.split(" ").reverse()[0],c.push(["",d])}else"empty"===this.options.firstItem&&c.push(["",""]);return c},fillDay:function(){var a,b,c=this.fillCommon("d"),d=-1!==this.options.template.indexOf("DD"),e=31;if(this.options.smartDays&&this.$month&&this.$year){var f=parseInt(this.$month.val(),10),g=parseInt(this.$year.val(),10);isNaN(f)||isNaN(g)||(e=moment([g,f]).daysInMonth())}for(b=1;e>=b;b++)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillMonth:function(){var a,b,c=this.fillCommon("M"),d=-1!==this.options.template.indexOf("MMMM"),e=-1!==this.options.template.indexOf("MMM"),f=-1!==this.options.template.indexOf("MM");for(b=0;11>=b;b++)a=d?moment().date(1).month(b).format("MMMM"):e?moment().date(1).month(b).format("MMM"):f?this.leadZero(b+1):b+1,c.push([b,a]);return c},fillYear:function(){var a,b,c=[],d=-1!==this.options.template.indexOf("YYYY");for(b=this.options.maxYear;b>=this.options.minYear;b--)a=d?b:(b+"").substring(2),c[this.options.yearDescending?"push":"unshift"]([b,a]);return c=this.fillCommon("y").concat(c)},fillHour:function(){var a,b,c=this.fillCommon("h"),d=-1!==this.options.template.indexOf("h"),e=(-1!==this.options.template.indexOf("H"),-1!==this.options.template.toLowerCase().indexOf("hh")),f=d?1:0,g=d?12:23;for(b=f;g>=b;b++)a=e?this.leadZero(b):b,c.push([b,a]);return c},fillMinute:function(){var a,b,c=this.fillCommon("m"),d=-1!==this.options.template.indexOf("mm");for(b=0;59>=b;b+=this.options.minuteStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillSecond:function(){var a,b,c=this.fillCommon("s"),d=-1!==this.options.template.indexOf("ss");for(b=0;59>=b;b+=this.options.secondStep)a=d?this.leadZero(b):b,c.push([b,a]);return c},fillAmpm:function(){var a=-1!==this.options.template.indexOf("a"),b=(-1!==this.options.template.indexOf("A"),[["am",a?"am":"AM"],["pm",a?"pm":"PM"]]);return b},getValue:function(b){var c,d={},e=this,f=!1;return a.each(this.map,function(a){if("ampm"!==a){var b="day"===a?1:0;return d[a]=e["$"+a]?parseInt(e["$"+a].val(),10):b,isNaN(d[a])?(f=!0,!1):void 0}}),f?"":(this.$ampm&&(d.hour=12===d.hour?"am"===this.$ampm.val()?0:12:"am"===this.$ampm.val()?d.hour:d.hour+12),c=moment([d.year,d.month,d.day,d.hour,d.minute,d.second]),this.highlight(c),b=void 0===b?this.options.format:b,null===b?c.isValid()?c:null:c.isValid()?c.format(b):"")},setValue:function(b){function c(b,c){var d={};return b.children("option").each(function(b,e){var f,g=a(e).attr("value");""!==g&&(f=Math.abs(g-c),("undefined"==typeof d.distance||f<d.distance)&&(d={value:g,distance:f}))}),d.value}if(b){var d="string"==typeof b?moment(b,this.options.format):moment(b),e=this,f={};d.isValid()&&(a.each(this.map,function(a,b){"ampm"!==a&&(f[a]=d[b[1]]())}),this.$ampm&&(f.hour>=12?(f.ampm="pm",f.hour>12&&(f.hour-=12)):(f.ampm="am",0===f.hour&&(f.hour=12))),a.each(f,function(a,b){e["$"+a]&&("minute"===a&&e.options.minuteStep>1&&e.options.roundTime&&(b=c(e["$"+a],b)),"second"===a&&e.options.secondStep>1&&e.options.roundTime&&(b=c(e["$"+a],b)),e["$"+a].val(b))}),this.options.smartDays&&this.fillCombo("day"),this.$element.val(d.format(this.options.format)).change())}},highlight:function(a){a.isValid()?this.options.errorClass?this.$widget.removeClass(this.options.errorClass):this.$widget.find("select").css("border-color",this.borderColor):this.options.errorClass?this.$widget.addClass(this.options.errorClass):(this.borderColor||(this.borderColor=this.$widget.find("select").css("border-color")),this.$widget.find("select").css("border-color","red"))},leadZero:function(a){return 9>=a?"0"+a:a},destroy:function(){this.$widget.remove(),this.$element.removeData("combodate").show()}},a.fn.combodate=function(c){var d,e=Array.apply(null,arguments);return e.shift(),"getValue"===c&&this.length&&(d=this.eq(0).data("combodate"))?d.getValue.apply(d,e):this.each(function(){var d=a(this),f=d.data("combodate"),g="object"==typeof c&&c;f||d.data("combodate",f=new b(this,g)),"string"==typeof c&&"function"==typeof f[c]&&f[c].apply(f,e)})},a.fn.combodate.defaults={format:"DD-MM-YYYY HH:mm",template:"D / MMM / YYYY H : mm",value:null,minYear:1970,maxYear:2015,yearDescending:!0,minuteStep:5,secondStep:1,firstItem:"empty",errorClass:null,roundTime:!0,smartDays:!1}}(window.jQuery),function(a){"use strict";var b=function(c){this.init("combodate",c,b.defaults),this.options.viewformat||(this.options.viewformat=this.options.format),c.combodate=a.fn.editableutils.tryParseJson(c.combodate,!0),this.options.combodate=a.extend({},b.defaults.combodate,c.combodate,{format:this.options.format,template:this.options.template})};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.$input.combodate(this.options.combodate),"bs3"===a.fn.editableform.engine&&this.$input.siblings().find("select").addClass("form-control"),this.options.inputclass&&this.$input.siblings().find("select").addClass(this.options.inputclass)},value2html:function(a,c){var d=a?a.format(this.options.viewformat):"";b.superclass.value2html.call(this,d,c)},html2value:function(a){return a?moment(a,this.options.viewformat):null},value2str:function(a){return a?a.format(this.options.format):""},str2value:function(a){return a?moment(a,this.options.format):null},value2submit:function(a){return this.value2str(a)},value2input:function(a){this.$input.combodate("setValue",a)},input2value:function(){return this.$input.combodate("getValue",null)},activate:function(){this.$input.siblings(".combodate").find("select").eq(0).focus()},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<input type="text">',inputclass:null,format:"YYYY-MM-DD",viewformat:null,template:"D / MMM / YYYY",combodate:null}),a.fn.editabletypes.combodate=b}(window.jQuery),function(a){"use strict";var b=a.fn.editableform.Constructor.prototype.initInput;a.extend(a.fn.editableform.Constructor.prototype,{initTemplate:function(){this.$form=a(a.fn.editableform.template),this.$form.find(".control-group").addClass("form-group"),this.$form.find(".editable-error-block").addClass("help-block")},initInput:function(){b.apply(this);var c=null===this.input.options.inputclass||this.input.options.inputclass===!1,d="input-sm",e="text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs".split(",");~a.inArray(this.input.type,e)&&(this.input.$input.addClass("form-control"),c&&(this.input.options.inputclass=d,this.input.$input.addClass(d)));for(var f=this.$form.find(".editable-buttons"),g=c?[d]:this.input.options.inputclass.split(" "),h=0;h<g.length;h++)"input-lg"===g[h].toLowerCase()&&f.find("button").removeClass("btn-sm").addClass("btn-lg")}}),a.fn.editableform.buttons='<button type="submit" class="btn btn-primary btn-sm editable-submit"><i class="glyphicon glyphicon-ok"></i></button><button type="button" class="btn btn-default btn-sm editable-cancel"><i class="glyphicon glyphicon-remove"></i></button>',a.fn.editableform.errorGroupClass="has-error",a.fn.editableform.errorBlockClass=null,a.fn.editableform.engine="bs3"}(window.jQuery),function(a){"use strict";a.extend(a.fn.editableContainer.Popup.prototype,{containerName:"popover",containerDataName:"bs.popover",innerCss:".popover-content",defaults:a.fn.popover.Constructor.DEFAULTS,initContainer:function(){a.extend(this.containerOptions,{trigger:"manual",selector:!1,content:" ",template:this.defaults.template});var b;this.$element.data("template")&&(b=this.$element.data("template"),this.$element.removeData("template")),this.call(this.containerOptions),b&&this.$element.data("template",b)},innerShow:function(){this.call("show")},innerHide:function(){this.call("hide")},innerDestroy:function(){this.call("destroy")},setContainerOption:function(a,b){this.container().options[a]=b},setPosition:function(){!function(){var a=this.tip(),b="function"==typeof this.options.placement?this.options.placement.call(this,a[0],this.$element[0]):this.options.placement,c=/\s?auto?\s?/i,d=c.test(b);d&&(b=b.replace(c,"")||"top");var e=this.getPosition(),f=a[0].offsetWidth,g=a[0].offsetHeight;if(d){var h=this.$element.parent(),i=b,j=document.documentElement.scrollTop||document.body.scrollTop,k="body"==this.options.container?window.innerWidth:h.outerWidth(),l="body"==this.options.container?window.innerHeight:h.outerHeight(),m="body"==this.options.container?0:h.offset().left;b="bottom"==b&&e.top+e.height+g-j>l?"top":"top"==b&&e.top-j-g<0?"bottom":"right"==b&&e.right+f>k?"left":"left"==b&&e.left-f<m?"right":b,a.removeClass(i).addClass(b)}var n=this.getCalculatedOffset(b,e,f,g);this.applyPlacement(n,b)}.call(this.container())}})}(window.jQuery),function(a){function b(){return new Date(Date.UTC.apply(Date,arguments))}function c(b,c){var d,e=a(b).data(),f={},g=new RegExp("^"+c.toLowerCase()+"([A-Z])"),c=new RegExp("^"+c.toLowerCase());for(var h in e)c.test(h)&&(d=h.replace(g,function(a,b){return b.toLowerCase()}),f[d]=e[h]);return f}function d(b){var c={};if(k[b]||(b=b.split("-")[0],k[b])){var d=k[b];return a.each(j,function(a,b){b in d&&(c[b]=d[b])}),c}}var e=function(b,c){this._process_options(c),this.element=a(b),this.isInline=!1,this.isInput=this.element.is("input"),this.component=this.element.is(".date")?this.element.find(".add-on, .btn"):!1,this.hasInput=this.component&&this.element.find("input").length,this.component&&0===this.component.length&&(this.component=!1),this.picker=a(l.template),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&(this.picker.addClass("datepicker-rtl"),this.picker.find(".prev i, .next i").toggleClass("icon-arrow-left icon-arrow-right")),this.viewMode=this.o.startView,this.o.calendarWeeks&&this.picker.find("tfoot th.today").attr("colspan",function(a,b){return parseInt(b)+1}),this._allow_update=!1,this.setStartDate(this.o.startDate),this.setEndDate(this.o.endDate),this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled),this.fillDow(),this.fillMonths(),this._allow_update=!0,this.update(),this.showMode(),this.isInline&&this.show()};e.prototype={constructor:e,_process_options:function(b){this._o=a.extend({},this._o,b);var c=this.o=a.extend({},this._o),d=c.language;switch(k[d]||(d=d.split("-")[0],k[d]||(d=i.language)),c.language=d,c.startView){case 2:case"decade":c.startView=2;break;case 1:case"year":c.startView=1;break;default:c.startView=0}switch(c.minViewMode){case 1:case"months":c.minViewMode=1;break;case 2:case"years":c.minViewMode=2;break;default:c.minViewMode=0}c.startView=Math.max(c.startView,c.minViewMode),c.weekStart%=7,c.weekEnd=(c.weekStart+6)%7;var e=l.parseFormat(c.format);c.startDate!==-1/0&&(c.startDate=l.parseDate(c.startDate,e,c.language)),1/0!==c.endDate&&(c.endDate=l.parseDate(c.endDate,e,c.language)),c.daysOfWeekDisabled=c.daysOfWeekDisabled||[],a.isArray(c.daysOfWeekDisabled)||(c.daysOfWeekDisabled=c.daysOfWeekDisabled.split(/[,\s]*/)),c.daysOfWeekDisabled=a.map(c.daysOfWeekDisabled,function(a){return parseInt(a,10)})},_events:[],_secondaryEvents:[],_applyEvents:function(a){for(var b,c,d=0;d<a.length;d++)b=a[d][0],c=a[d][1],b.on(c)},_unapplyEvents:function(a){for(var b,c,d=0;d<a.length;d++)b=a[d][0],c=a[d][1],b.off(c)},_buildEvents:function(){this.isInput?this._events=[[this.element,{focus:a.proxy(this.show,this),keyup:a.proxy(this.update,this),keydown:a.proxy(this.keydown,this)}]]:this.component&&this.hasInput?this._events=[[this.element.find("input"),{focus:a.proxy(this.show,this),keyup:a.proxy(this.update,this),keydown:a.proxy(this.keydown,this)}],[this.component,{click:a.proxy(this.show,this)}]]:this.element.is("div")?this.isInline=!0:this._events=[[this.element,{click:a.proxy(this.show,this)}]],this._secondaryEvents=[[this.picker,{click:a.proxy(this.click,this)}],[a(window),{resize:a.proxy(this.place,this)}],[a(document),{mousedown:a.proxy(function(a){this.element.is(a.target)||this.element.find(a.target).size()||this.picker.is(a.target)||this.picker.find(a.target).size()||this.hide()},this)}]]},_attachEvents:function(){this._detachEvents(),this._applyEvents(this._events)},_detachEvents:function(){this._unapplyEvents(this._events)},_attachSecondaryEvents:function(){this._detachSecondaryEvents(),this._applyEvents(this._secondaryEvents)},_detachSecondaryEvents:function(){this._unapplyEvents(this._secondaryEvents)},_trigger:function(b,c){var d=c||this.date,e=new Date(d.getTime()+6e4*d.getTimezoneOffset());this.element.trigger({type:b,date:e,format:a.proxy(function(a){var b=a||this.o.format;return l.formatDate(d,b,this.o.language)},this)})},show:function(a){this.isInline||this.picker.appendTo("body"),this.picker.show(),this.height=this.component?this.component.outerHeight():this.element.outerHeight(),this.place(),this._attachSecondaryEvents(),a&&a.preventDefault(),this._trigger("show")},hide:function(){this.isInline||this.picker.is(":visible")&&(this.picker.hide().detach(),this._detachSecondaryEvents(),this.viewMode=this.o.startView,this.showMode(),this.o.forceParse&&(this.isInput&&this.element.val()||this.hasInput&&this.element.find("input").val())&&this.setValue(),this._trigger("hide"))},remove:function(){this.hide(),this._detachEvents(),this._detachSecondaryEvents(),this.picker.remove(),delete this.element.data().datepicker,this.isInput||delete this.element.data().date},getDate:function(){var a=this.getUTCDate();return new Date(a.getTime()+6e4*a.getTimezoneOffset())},getUTCDate:function(){return this.date},setDate:function(a){this.setUTCDate(new Date(a.getTime()-6e4*a.getTimezoneOffset()))},setUTCDate:function(a){this.date=a,this.setValue()},setValue:function(){var a=this.getFormattedDate();this.isInput?this.element.val(a):this.component&&this.element.find("input").val(a)},getFormattedDate:function(a){return void 0===a&&(a=this.o.format),l.formatDate(this.date,a,this.o.language)},setStartDate:function(a){this._process_options({startDate:a}),this.update(),this.updateNavArrows()},setEndDate:function(a){this._process_options({endDate:a}),this.update(),this.updateNavArrows()},setDaysOfWeekDisabled:function(a){this._process_options({daysOfWeekDisabled:a}),this.update(),this.updateNavArrows()},place:function(){if(!this.isInline){var b=parseInt(this.element.parents().filter(function(){return"auto"!=a(this).css("z-index")}).first().css("z-index"))+10,c=this.component?this.component.parent().offset():this.element.offset(),d=this.component?this.component.outerHeight(!0):this.element.outerHeight(!0);this.picker.css({top:c.top+d,left:c.left,zIndex:b})}},_allow_update:!0,update:function(){if(this._allow_update){var a,b=!1;arguments&&arguments.length&&("string"==typeof arguments[0]||arguments[0]instanceof Date)?(a=arguments[0],b=!0):(a=this.isInput?this.element.val():this.element.data("date")||this.element.find("input").val(),delete this.element.data().date),this.date=l.parseDate(a,this.o.format,this.o.language),b&&this.setValue(),this.viewDate=this.date<this.o.startDate?new Date(this.o.startDate):this.date>this.o.endDate?new Date(this.o.endDate):new Date(this.date),this.fill()}},fillDow:function(){var a=this.o.weekStart,b="<tr>";if(this.o.calendarWeeks){var c='<th class="cw">&nbsp;</th>';b+=c,this.picker.find(".datepicker-days thead tr:first-child").prepend(c)}for(;a<this.o.weekStart+7;)b+='<th class="dow">'+k[this.o.language].daysMin[a++%7]+"</th>";b+="</tr>",this.picker.find(".datepicker-days thead").append(b)},fillMonths:function(){for(var a="",b=0;12>b;)a+='<span class="month">'+k[this.o.language].monthsShort[b++]+"</span>";this.picker.find(".datepicker-months td").html(a)},setRange:function(b){b&&b.length?this.range=a.map(b,function(a){return a.valueOf()}):delete this.range,this.fill()},getClassNames:function(b){var c=[],d=this.viewDate.getUTCFullYear(),e=this.viewDate.getUTCMonth(),f=this.date.valueOf(),g=new Date;return b.getUTCFullYear()<d||b.getUTCFullYear()==d&&b.getUTCMonth()<e?c.push("old"):(b.getUTCFullYear()>d||b.getUTCFullYear()==d&&b.getUTCMonth()>e)&&c.push("new"),this.o.todayHighlight&&b.getUTCFullYear()==g.getFullYear()&&b.getUTCMonth()==g.getMonth()&&b.getUTCDate()==g.getDate()&&c.push("today"),f&&b.valueOf()==f&&c.push("active"),(b.valueOf()<this.o.startDate||b.valueOf()>this.o.endDate||-1!==a.inArray(b.getUTCDay(),this.o.daysOfWeekDisabled))&&c.push("disabled"),this.range&&(b>this.range[0]&&b<this.range[this.range.length-1]&&c.push("range"),-1!=a.inArray(b.valueOf(),this.range)&&c.push("selected")),c},fill:function(){var c,d=new Date(this.viewDate),e=d.getUTCFullYear(),f=d.getUTCMonth(),g=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,h=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,i=1/0!==this.o.endDate?this.o.endDate.getUTCFullYear():1/0,j=1/0!==this.o.endDate?this.o.endDate.getUTCMonth():1/0;this.date&&this.date.valueOf(),this.picker.find(".datepicker-days thead th.datepicker-switch").text(k[this.o.language].months[f]+" "+e),this.picker.find("tfoot th.today").text(k[this.o.language].today).toggle(this.o.todayBtn!==!1),this.picker.find("tfoot th.clear").text(k[this.o.language].clear).toggle(this.o.clearBtn!==!1),this.updateNavArrows(),this.fillMonths();var m=b(e,f-1,28,0,0,0,0),n=l.getDaysInMonth(m.getUTCFullYear(),m.getUTCMonth());m.setUTCDate(n),m.setUTCDate(n-(m.getUTCDay()-this.o.weekStart+7)%7);var o=new Date(m);o.setUTCDate(o.getUTCDate()+42),o=o.valueOf();for(var p,q=[];m.valueOf()<o;){if(m.getUTCDay()==this.o.weekStart&&(q.push("<tr>"),this.o.calendarWeeks)){var r=new Date(+m+864e5*((this.o.weekStart-m.getUTCDay()-7)%7)),s=new Date(+r+864e5*((11-r.getUTCDay())%7)),t=new Date(+(t=b(s.getUTCFullYear(),0,1))+864e5*((11-t.getUTCDay())%7)),u=(s-t)/864e5/7+1;q.push('<td class="cw">'+u+"</td>")}p=this.getClassNames(m),p.push("day");var v=this.o.beforeShowDay(m);void 0===v?v={}:"boolean"==typeof v?v={enabled:v}:"string"==typeof v&&(v={classes:v}),v.enabled===!1&&p.push("disabled"),v.classes&&(p=p.concat(v.classes.split(/\s+/))),v.tooltip&&(c=v.tooltip),p=a.unique(p),q.push('<td class="'+p.join(" ")+'"'+(c?' title="'+c+'"':"")+">"+m.getUTCDate()+"</td>"),m.getUTCDay()==this.o.weekEnd&&q.push("</tr>"),m.setUTCDate(m.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").empty().append(q.join(""));var w=this.date&&this.date.getUTCFullYear(),x=this.picker.find(".datepicker-months").find("th:eq(1)").text(e).end().find("span").removeClass("active");w&&w==e&&x.eq(this.date.getUTCMonth()).addClass("active"),(g>e||e>i)&&x.addClass("disabled"),e==g&&x.slice(0,h).addClass("disabled"),e==i&&x.slice(j+1).addClass("disabled"),q="",e=10*parseInt(e/10,10);var y=this.picker.find(".datepicker-years").find("th:eq(1)").text(e+"-"+(e+9)).end().find("td");e-=1;for(var z=-1;11>z;z++)q+='<span class="year'+(-1==z?" old":10==z?" new":"")+(w==e?" active":"")+(g>e||e>i?" disabled":"")+'">'+e+"</span>",e+=1;y.html(q)},updateNavArrows:function(){if(this._allow_update){var a=new Date(this.viewDate),b=a.getUTCFullYear(),c=a.getUTCMonth();switch(this.viewMode){case 0:this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()&&c<=this.o.startDate.getUTCMonth()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()&&c>=this.o.endDate.getUTCMonth()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"});break;case 1:case 2:this.o.startDate!==-1/0&&b<=this.o.startDate.getUTCFullYear()?this.picker.find(".prev").css({visibility:"hidden"}):this.picker.find(".prev").css({visibility:"visible"}),1/0!==this.o.endDate&&b>=this.o.endDate.getUTCFullYear()?this.picker.find(".next").css({visibility:"hidden"}):this.picker.find(".next").css({visibility:"visible"})}}},click:function(c){c.preventDefault();var d=a(c.target).closest("span, td, th");if(1==d.length)switch(d[0].nodeName.toLowerCase()){case"th":switch(d[0].className){case"datepicker-switch":this.showMode(1);break;case"prev":case"next":var e=l.modes[this.viewMode].navStep*("prev"==d[0].className?-1:1);switch(this.viewMode){case 0:this.viewDate=this.moveMonth(this.viewDate,e);break;case 1:case 2:this.viewDate=this.moveYear(this.viewDate,e)}this.fill();break;case"today":var f=new Date;f=b(f.getFullYear(),f.getMonth(),f.getDate(),0,0,0),this.showMode(-2);var g="linked"==this.o.todayBtn?null:"view";this._setDate(f,g);break;case"clear":var h;this.isInput?h=this.element:this.component&&(h=this.element.find("input")),h&&h.val("").change(),this._trigger("changeDate"),this.update(),this.o.autoclose&&this.hide()}break;case"span":if(!d.is(".disabled")){if(this.viewDate.setUTCDate(1),d.is(".month")){var i=1,j=d.parent().find("span").index(d),k=this.viewDate.getUTCFullYear();this.viewDate.setUTCMonth(j),this._trigger("changeMonth",this.viewDate),1===this.o.minViewMode&&this._setDate(b(k,j,i,0,0,0,0))}else{var k=parseInt(d.text(),10)||0,i=1,j=0;this.viewDate.setUTCFullYear(k),this._trigger("changeYear",this.viewDate),2===this.o.minViewMode&&this._setDate(b(k,j,i,0,0,0,0))}this.showMode(-1),this.fill()}break;case"td":if(d.is(".day")&&!d.is(".disabled")){var i=parseInt(d.text(),10)||1,k=this.viewDate.getUTCFullYear(),j=this.viewDate.getUTCMonth();d.is(".old")?0===j?(j=11,k-=1):j-=1:d.is(".new")&&(11==j?(j=0,k+=1):j+=1),this._setDate(b(k,j,i,0,0,0,0))}}},_setDate:function(a,b){b&&"date"!=b||(this.date=new Date(a)),b&&"view"!=b||(this.viewDate=new Date(a)),this.fill(),this.setValue(),this._trigger("changeDate");var c;this.isInput?c=this.element:this.component&&(c=this.element.find("input")),c&&(c.change(),!this.o.autoclose||b&&"date"!=b||this.hide())},moveMonth:function(a,b){if(!b)return a;var c,d,e=new Date(a.valueOf()),f=e.getUTCDate(),g=e.getUTCMonth(),h=Math.abs(b);if(b=b>0?1:-1,1==h)d=-1==b?function(){return e.getUTCMonth()==g}:function(){return e.getUTCMonth()!=c},c=g+b,e.setUTCMonth(c),(0>c||c>11)&&(c=(c+12)%12);else{for(var i=0;h>i;i++)e=this.moveMonth(e,b);c=e.getUTCMonth(),e.setUTCDate(f),d=function(){return c!=e.getUTCMonth()}}for(;d();)e.setUTCDate(--f),e.setUTCMonth(c);return e},moveYear:function(a,b){return this.moveMonth(a,12*b)},dateWithinRange:function(a){return a>=this.o.startDate&&a<=this.o.endDate},keydown:function(a){if(this.picker.is(":not(:visible)"))return 27==a.keyCode&&this.show(),void 0;var b,c,d,e=!1;switch(a.keyCode){case 27:this.hide(),a.preventDefault();break;case 37:case 39:if(!this.o.keyboardNavigation)break;b=37==a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.date,b),d=this.moveYear(this.viewDate,b)):a.shiftKey?(c=this.moveMonth(this.date,b),d=this.moveMonth(this.viewDate,b)):(c=new Date(this.date),c.setUTCDate(this.date.getUTCDate()+b),d=new Date(this.viewDate),d.setUTCDate(this.viewDate.getUTCDate()+b)),this.dateWithinRange(c)&&(this.date=c,this.viewDate=d,this.setValue(),this.update(),a.preventDefault(),e=!0);break;case 38:case 40:if(!this.o.keyboardNavigation)break;b=38==a.keyCode?-1:1,a.ctrlKey?(c=this.moveYear(this.date,b),d=this.moveYear(this.viewDate,b)):a.shiftKey?(c=this.moveMonth(this.date,b),d=this.moveMonth(this.viewDate,b)):(c=new Date(this.date),c.setUTCDate(this.date.getUTCDate()+7*b),d=new Date(this.viewDate),d.setUTCDate(this.viewDate.getUTCDate()+7*b)),this.dateWithinRange(c)&&(this.date=c,this.viewDate=d,this.setValue(),this.update(),a.preventDefault(),e=!0);break;case 13:this.hide(),a.preventDefault();break;case 9:this.hide()}if(e){this._trigger("changeDate");var f;this.isInput?f=this.element:this.component&&(f=this.element.find("input")),f&&f.change()}},showMode:function(a){a&&(this.viewMode=Math.max(this.o.minViewMode,Math.min(2,this.viewMode+a))),this.picker.find(">div").hide().filter(".datepicker-"+l.modes[this.viewMode].clsName).css("display","block"),this.updateNavArrows()}};var f=function(b,c){this.element=a(b),this.inputs=a.map(c.inputs,function(a){return a.jquery?a[0]:a}),delete c.inputs,a(this.inputs).datepicker(c).bind("changeDate",a.proxy(this.dateUpdated,this)),this.pickers=a.map(this.inputs,function(b){return a(b).data("datepicker")}),this.updateDates()};f.prototype={updateDates:function(){this.dates=a.map(this.pickers,function(a){return a.date}),this.updateRanges()},updateRanges:function(){var b=a.map(this.dates,function(a){return a.valueOf()});a.each(this.pickers,function(a,c){c.setRange(b)})},dateUpdated:function(b){var c=a(b.target).data("datepicker"),d=c.getUTCDate(),e=a.inArray(b.target,this.inputs),f=this.inputs.length;if(-1!=e){if(d<this.dates[e])for(;e>=0&&d<this.dates[e];)this.pickers[e--].setUTCDate(d);else if(d>this.dates[e])for(;f>e&&d>this.dates[e];)this.pickers[e++].setUTCDate(d);this.updateDates()}},remove:function(){a.map(this.pickers,function(a){a.remove()}),delete this.element.data().datepicker}};var g=a.fn.datepicker,h=a.fn.datepicker=function(b){var g=Array.apply(null,arguments);g.shift();var h;return this.each(function(){var j=a(this),k=j.data("datepicker"),l="object"==typeof b&&b;if(!k){var m=c(this,"date"),n=a.extend({},i,m,l),o=d(n.language),p=a.extend({},i,o,m,l);if(j.is(".input-daterange")||p.inputs){var q={inputs:p.inputs||j.find("input").toArray()};j.data("datepicker",k=new f(this,a.extend(p,q)))}else j.data("datepicker",k=new e(this,p))}return"string"==typeof b&&"function"==typeof k[b]&&(h=k[b].apply(k,g),void 0!==h)?!1:void 0}),void 0!==h?h:this},i=a.fn.datepicker.defaults={autoclose:!1,beforeShowDay:a.noop,calendarWeeks:!1,clearBtn:!1,daysOfWeekDisabled:[],endDate:1/0,forceParse:!0,format:"mm/dd/yyyy",keyboardNavigation:!0,language:"en",minViewMode:0,rtl:!1,startDate:-1/0,startView:0,todayBtn:!1,todayHighlight:!1,weekStart:0},j=a.fn.datepicker.locale_opts=["format","rtl","weekStart"];a.fn.datepicker.Constructor=e;var k=a.fn.datepicker.dates={en:{days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],daysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat","Sun"],daysMin:["Su","Mo","Tu","We","Th","Fr","Sa","Su"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],today:"Today",clear:"Clear"}},l={modes:[{clsName:"days",navFnc:"Month",navStep:1},{clsName:"months",navFnc:"FullYear",navStep:1},{clsName:"years",navFnc:"FullYear",navStep:10}],isLeapYear:function(a){return 0===a%4&&0!==a%100||0===a%400
+},getDaysInMonth:function(a,b){return[31,l.isLeapYear(a)?29:28,31,30,31,30,31,31,30,31,30,31][b]},validParts:/dd?|DD?|mm?|MM?|yy(?:yy)?/g,nonpunctuation:/[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,parseFormat:function(a){var b=a.replace(this.validParts,"\0").split("\0"),c=a.match(this.validParts);if(!b||!b.length||!c||0===c.length)throw new Error("Invalid date format.");return{separators:b,parts:c}},parseDate:function(c,d,f){if(c instanceof Date)return c;if("string"==typeof d&&(d=l.parseFormat(d)),/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(c)){var g,h,i=/([\-+]\d+)([dmwy])/,j=c.match(/([\-+]\d+)([dmwy])/g);c=new Date;for(var m=0;m<j.length;m++)switch(g=i.exec(j[m]),h=parseInt(g[1]),g[2]){case"d":c.setUTCDate(c.getUTCDate()+h);break;case"m":c=e.prototype.moveMonth.call(e.prototype,c,h);break;case"w":c.setUTCDate(c.getUTCDate()+7*h);break;case"y":c=e.prototype.moveYear.call(e.prototype,c,h)}return b(c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate(),0,0,0)}var n,o,g,j=c&&c.match(this.nonpunctuation)||[],c=new Date,p={},q=["yyyy","yy","M","MM","m","mm","d","dd"],r={yyyy:function(a,b){return a.setUTCFullYear(b)},yy:function(a,b){return a.setUTCFullYear(2e3+b)},m:function(a,b){for(b-=1;0>b;)b+=12;for(b%=12,a.setUTCMonth(b);a.getUTCMonth()!=b;)a.setUTCDate(a.getUTCDate()-1);return a},d:function(a,b){return a.setUTCDate(b)}};r.M=r.MM=r.mm=r.m,r.dd=r.d,c=b(c.getFullYear(),c.getMonth(),c.getDate(),0,0,0);var s=d.parts.slice();if(j.length!=s.length&&(s=a(s).filter(function(b,c){return-1!==a.inArray(c,q)}).toArray()),j.length==s.length){for(var m=0,t=s.length;t>m;m++){if(n=parseInt(j[m],10),g=s[m],isNaN(n))switch(g){case"MM":o=a(k[f].months).filter(function(){var a=this.slice(0,j[m].length),b=j[m].slice(0,a.length);return a==b}),n=a.inArray(o[0],k[f].months)+1;break;case"M":o=a(k[f].monthsShort).filter(function(){var a=this.slice(0,j[m].length),b=j[m].slice(0,a.length);return a==b}),n=a.inArray(o[0],k[f].monthsShort)+1}p[g]=n}for(var u,m=0;m<q.length;m++)u=q[m],u in p&&!isNaN(p[u])&&r[u](c,p[u])}return c},formatDate:function(b,c,d){"string"==typeof c&&(c=l.parseFormat(c));var e={d:b.getUTCDate(),D:k[d].daysShort[b.getUTCDay()],DD:k[d].days[b.getUTCDay()],m:b.getUTCMonth()+1,M:k[d].monthsShort[b.getUTCMonth()],MM:k[d].months[b.getUTCMonth()],yy:b.getUTCFullYear().toString().substring(2),yyyy:b.getUTCFullYear()};e.dd=(e.d<10?"0":"")+e.d,e.mm=(e.m<10?"0":"")+e.m;for(var b=[],f=a.extend([],c.separators),g=0,h=c.parts.length;h>=g;g++)f.length&&b.push(f.shift()),b.push(e[c.parts[g]]);return b.join("")},headTemplate:'<thead><tr><th class="prev"><i class="icon-arrow-left"/></th><th colspan="5" class="datepicker-switch"></th><th class="next"><i class="icon-arrow-right"/></th></tr></thead>',contTemplate:'<tbody><tr><td colspan="7"></td></tr></tbody>',footTemplate:'<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'};l.template='<div class="datepicker"><div class="datepicker-days"><table class=" table-condensed">'+l.headTemplate+"<tbody></tbody>"+l.footTemplate+"</table>"+"</div>"+'<div class="datepicker-months">'+'<table class="table-condensed">'+l.headTemplate+l.contTemplate+l.footTemplate+"</table>"+"</div>"+'<div class="datepicker-years">'+'<table class="table-condensed">'+l.headTemplate+l.contTemplate+l.footTemplate+"</table>"+"</div>"+"</div>",a.fn.datepicker.DPGlobal=l,a.fn.datepicker.noConflict=function(){return a.fn.datepicker=g,this},a(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(b){var c=a(this);c.data("datepicker")||(b.preventDefault(),h.call(c,"show"))}),a(function(){h.call(a('[data-provide="datepicker-inline"]'))})}(window.jQuery),function(a){"use strict";a.fn.bdatepicker=a.fn.datepicker.noConflict(),a.fn.datepicker||(a.fn.datepicker=a.fn.bdatepicker);var b=function(a){this.init("date",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{initPicker:function(b,c){this.options.viewformat||(this.options.viewformat=this.options.format),b.datepicker=a.fn.editableutils.tryParseJson(b.datepicker,!0),this.options.datepicker=a.extend({},c.datepicker,b.datepicker,{format:this.options.viewformat}),this.options.datepicker.language=this.options.datepicker.language||"en",this.dpg=a.fn.bdatepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat)},render:function(){this.$input.bdatepicker(this.options.datepicker),this.options.clear&&(this.$clear=a('<a href="#"></a>').html(this.options.clear).click(a.proxy(function(a){a.preventDefault(),a.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(a('<div class="editable-clear">').append(this.$clear)))},value2html:function(a,c){var d=a?this.dpg.formatDate(a,this.parsedViewFormat,this.options.datepicker.language):"";b.superclass.value2html.call(this,d,c)},html2value:function(a){return this.parseDate(a,this.parsedViewFormat)},value2str:function(a){return a?this.dpg.formatDate(a,this.parsedFormat,this.options.datepicker.language):""},str2value:function(a){return this.parseDate(a,this.parsedFormat)},value2submit:function(a){return this.value2str(a)},value2input:function(a){this.$input.bdatepicker("update",a)},input2value:function(){return this.$input.data("datepicker").date},activate:function(){},clear:function(){this.$input.data("datepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".day",function(b){if(!a(b.currentTarget).is(".old")&&!a(b.currentTarget).is(".new")){var c=a(this).closest("form");setTimeout(function(){c.submit()},200)}})},parseDate:function(a,b){var c,d=null;return a&&(d=this.dpg.parseDate(a,b,this.options.datepicker.language),"string"==typeof a&&(c=this.dpg.formatDate(d,b,this.options.datepicker.language),a!==c&&(d=null))),d}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<div class="editable-date well"></div>',inputclass:null,format:"yyyy-mm-dd",viewformat:null,datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!1},clear:"&times; clear"}),a.fn.editabletypes.date=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datefield",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.date),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.bdatepicker(this.options.datepicker),this.$input.off("focus keydown"),this.$input.keyup(a.proxy(function(){this.$tpl.removeData("date"),this.$tpl.bdatepicker("update")},this))},value2input:function(a){this.$input.val(a?this.dpg.formatDate(a,this.parsedViewFormat,this.options.datepicker.language):""),this.$tpl.bdatepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.date.defaults,{tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',inputclass:"input-small",datepicker:{weekStart:0,startView:0,minViewMode:0,autoclose:!0}}),a.fn.editabletypes.datefield=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datetime",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{initPicker:function(b,c){this.options.viewformat||(this.options.viewformat=this.options.format),b.datetimepicker=a.fn.editableutils.tryParseJson(b.datetimepicker,!0),this.options.datetimepicker=a.extend({},c.datetimepicker,b.datetimepicker,{format:this.options.viewformat}),this.options.datetimepicker.language=this.options.datetimepicker.language||"en",this.dpg=a.fn.datetimepicker.DPGlobal,this.parsedFormat=this.dpg.parseFormat(this.options.format,this.options.formatType),this.parsedViewFormat=this.dpg.parseFormat(this.options.viewformat,this.options.formatType)},render:function(){this.$input.datetimepicker(this.options.datetimepicker),this.$input.on("changeMode",function(){var b=a(this).closest("form").parent();setTimeout(function(){b.triggerHandler("resize")},0)}),this.options.clear&&(this.$clear=a('<a href="#"></a>').html(this.options.clear).click(a.proxy(function(a){a.preventDefault(),a.stopPropagation(),this.clear()},this)),this.$tpl.parent().append(a('<div class="editable-clear">').append(this.$clear)))},value2html:function(a,c){var d=a?this.dpg.formatDate(this.toUTC(a),this.parsedViewFormat,this.options.datetimepicker.language,this.options.formatType):"";return c?(b.superclass.value2html.call(this,d,c),void 0):d},html2value:function(a){var b=this.parseDate(a,this.parsedViewFormat);return b?this.fromUTC(b):null},value2str:function(a){return a?this.dpg.formatDate(this.toUTC(a),this.parsedFormat,this.options.datetimepicker.language,this.options.formatType):""},str2value:function(a){var b=this.parseDate(a,this.parsedFormat);return b?this.fromUTC(b):null},value2submit:function(a){return this.value2str(a)},value2input:function(a){a&&this.$input.data("datetimepicker").setDate(a)},input2value:function(){var a=this.$input.data("datetimepicker");return a.date?a.getDate():null},activate:function(){},clear:function(){this.$input.data("datetimepicker").date=null,this.$input.find(".active").removeClass("active"),this.options.showbuttons||this.$input.closest("form").submit()},autosubmit:function(){this.$input.on("mouseup",".minute",function(){var b=a(this).closest("form");setTimeout(function(){b.submit()},200)})},toUTC:function(a){return a?new Date(a.valueOf()-6e4*a.getTimezoneOffset()):a},fromUTC:function(a){return a?new Date(a.valueOf()+6e4*a.getTimezoneOffset()):a},parseDate:function(a,b){var c,d=null;return a&&(d=this.dpg.parseDate(a,b,this.options.datetimepicker.language,this.options.formatType),"string"==typeof a&&(c=this.dpg.formatDate(d,b,this.options.datetimepicker.language,this.options.formatType),a!==c&&(d=null))),d}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'<div class="editable-date well"></div>',inputclass:null,format:"yyyy-mm-dd hh:ii",formatType:"standard",viewformat:null,datetimepicker:{todayHighlight:!1,autoclose:!1},clear:"&times; clear"}),a.fn.editabletypes.datetime=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("datetimefield",a,b.defaults),this.initPicker(a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.datetime),a.extend(b.prototype,{render:function(){this.$input=this.$tpl.find("input"),this.setClass(),this.setAttr("placeholder"),this.$tpl.datetimepicker(this.options.datetimepicker),this.$input.off("focus keydown"),this.$input.keyup(a.proxy(function(){this.$tpl.removeData("date"),this.$tpl.datetimepicker("update")},this))},value2input:function(a){this.$input.val(this.value2html(a)),this.$tpl.datetimepicker("update")},input2value:function(){return this.html2value(this.$input.val())},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)},autosubmit:function(){}}),b.defaults=a.extend({},a.fn.editabletypes.datetime.defaults,{tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',inputclass:"input-medium",datetimepicker:{todayHighlight:!1,autoclose:!0}}),a.fn.editabletypes.datetimefield=b}(window.jQuery); \ No newline at end of file