Merge branch 'release-1.4.2'

This commit is contained in:
vitalets
2013-03-07 13:29:10 +04:00
41 changed files with 986 additions and 112 deletions

3
.gitignore vendored

@ -1,3 +1,4 @@
node_modules node_modules
dist dist
test/instrumented/ test/instrumented/
*.nupkg

@ -1,6 +1,22 @@
X-editable changelog X-editable changelog
============================= =============================
Version 1.4.2 Mar 7, 2013
----------------------------
[enh #132] combodate options can be defined via data-combodate json string (vitalets)
[enh] source defined as function now has scope of element and can return string used as url (vitalets)
[bug #99] select2 with Hierarchical Data (kev360)
[bug #81] wysihtml5: fix inserting image (vitalets)
[bug] remove $.browser from wysihtml5 input to support jQuery 1.9 (vitalets)
[bug #142] editable poshytip jquery 1.9+ compatibility (spiderpug)
[enh #126] Update bootstrap datepicker library and add minViewMode to options (kev360)
[enh #150] select2 with showbuttons = false (vitalets)
[bug #149] datepicker not shown when showbuttons = false (vitalets)
[bug #133] clear button incorect position due to parent line-height property (vitalets)
[bug #141] data-value ignored for empty elements (vitalets)
[bug #137] fix empty class for delegated element (vitalets)
[enh #121] add support of momentjs 2.0.0 in combodate (vitalets)
Version 1.4.1 Jan 18, 2013 Version 1.4.1 Jan 18, 2013
---------------------------- ----------------------------

21
Package.nuspec Normal file

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>x-editable</id>
<version>1.4.2</version>
<authors>Vitaliy Potapov</authors>
<owners>Vitaliy Potapov</owners>
<licenseUrl>https://github.com/vitalets/x-editable/blob/master/LICENSE-MIT</licenseUrl>
<projectUrl>http://vitalets.github.com/x-editable</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery</description>
<releaseNotes>Added support of jQuery 1.9 and several bugfixes.</releaseNotes>
<copyright>Copyright 2012-2013</copyright>
<tags>edit-in-place editable bootstrap jquery</tags>
<dependencies>
</dependencies>
</metadata>
<files>
<file src="dist\**" target="\" />
</files>
</package>

@ -106,10 +106,11 @@ module.exports = function(grunt) {
//module = '&module=select'; //module = '&module=select';
//module = '&module=text'; //module = '&module=text';
//test on several jquery versions
var qunit_testover = []; var qunit_testover = [];
['bootstrap', 'jqueryui', 'plain'].forEach(function(f){ ['bootstrap', 'jqueryui', 'plain'].forEach(function(f){
['popup', 'inline'].forEach(function(c){ ['popup', 'inline'].forEach(function(c){
['1.6.4', '1.7.1', '1.7.2', '1.8.2', '1.8.3'].forEach(function(jqver) { ['1.7.2', '1.8.3', '1.9.1'].forEach(function(jqver) {
qunit_testover.push('http://localhost:8000/test/index.html?f='+f+'&c='+c+'&jquery='+jqver+module); qunit_testover.push('http://localhost:8000/test/index.html?f='+f+'&c='+c+'&jquery='+jqver+module);
}); });
}); });

@ -2,7 +2,7 @@
"name": "X-editable", "name": "X-editable",
"title": "X-editable", "title": "X-editable",
"description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery", "description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery",
"version": "1.4.1", "version": "1.4.2",
"homepage": "http://github.com/vitalets/x-editable", "homepage": "http://github.com/vitalets/x-editable",
"author": { "author": {
"name": "Vitaliy Potapov", "name": "Vitaliy Potapov",

@ -47,7 +47,8 @@ Applied as jQuery method.
} }
}); });
//close containers when click outside //close containers when click outside
//(mousedown could be better than click, it closes everything also on drag drop)
$(document).on('click.editable', function(e) { $(document).on('click.editable', function(e) {
var $target = $(e.target), i, var $target = $(e.target), i,
exclude_classes = ['.editable-container', exclude_classes = ['.editable-container',
@ -125,8 +126,8 @@ Applied as jQuery method.
@param {Object} event event object @param {Object} event event object
@example @example
$('#username').on('shown', function() { $('#username').on('shown', function() {
var $tip = $(this).data('editableContainer').tip(); var editable = $(this).data('editable');
$tip.find('input').val('overwriting value of input..'); editable.input.$input.val('overwriting value of input..');
}); });
**/ **/
this.$element.triggerHandler('shown'); this.$element.triggerHandler('shown');
@ -427,9 +428,9 @@ Applied as jQuery method.
Animation speed (inline mode) Animation speed (inline mode)
@property anim @property anim
@type string @type string
@default 'fast' @default false
**/ **/
anim: 'fast', anim: false,
/** /**
Mode of editable, can be `popup` or `inline` Mode of editable, can be `popup` or `inline`

@ -108,7 +108,7 @@
var tips = [], var tips = [],
reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i, reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i,
rePNG = /\.png$/i, rePNG = /\.png$/i,
ie6 = $.browser.msie && $.browser.version == 6; ie6 = !!window.createPopup && document.documentElement.currentStyle.minWidth == 'undefined';
$.Poshytip.prototype.refresh = function(async) { $.Poshytip.prototype.refresh = function(async) {
if (this.disabled) if (this.disabled)

@ -145,7 +145,7 @@
$.each(sourceData, function(i, o) { $.each(sourceData, function(i, o) {
if(o.children) { if(o.children) {
result = result.concat(that.itemsByValue(value, o.children)); result = result.concat(that.itemsByValue(value, o.children, valueProp));
} else { } else {
/*jslint eqeq: true*/ /*jslint eqeq: true*/
if(isValArray) { if(isValArray) {

@ -5,6 +5,7 @@
.editableform .control-group { .editableform .control-group {
margin-bottom: 0; /* overwrites bootstrap margin */ margin-bottom: 0; /* overwrites bootstrap margin */
white-space: nowrap; /* prevent wrapping buttons on new line */ white-space: nowrap; /* prevent wrapping buttons on new line */
line-height: 20px; /* overwriting bootstrap line-height. See #133 */
} }
.editable-buttons { .editable-buttons {
@ -106,6 +107,7 @@
position: absolute; position: absolute;
opacity: 0.6; opacity: 0.6;
z-index: 100; z-index: 100;
} }
.editable-clear-x:hover { .editable-clear-x:hover {

@ -28,3 +28,10 @@ a.editable-click.editable-disabled:hover {
/* content: '*'*/ /* content: '*'*/
} }
/*see https://github.com/vitalets/x-editable/issues/139 */
.form-horizontal .editable
{
padding-top: 5px;
display:inline-block;
}

@ -26,7 +26,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
//name //name
this.options.name = this.options.name || this.$element.attr('id'); this.options.name = this.options.name || this.$element.attr('id');
//create input of specified type. Input will be used for converting value, not in form //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); this.input = $.fn.editableutils.createInput(this.options);
if(!this.input) { if(!this.input) {
return; return;
@ -77,9 +79,19 @@ Makes editable any HTML element on the page. Applied as jQuery method.
} }
//check conditions for autotext: //check conditions for autotext:
//if value was generated by text or value is empty, no sense to run autotext switch(this.options.autotext) {
doAutotext = !isValueByText && this.value !== null && this.value !== undefined; case 'always':
doAutotext &= (this.options.autotext === 'always') || (this.options.autotext === 'auto' && !this.$element.text().length); 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() { $.when(doAutotext ? this.render() : true).then($.proxy(function() {
if(this.options.disabled) { if(this.options.disabled) {
this.disable(); this.disable();
@ -115,6 +127,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){ this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
var $target = $(e.target); var $target = $(e.target);
if(!$target.data('editable')) { 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); $target.editable(this.options).trigger(e);
} }
}, this)); }, this));
@ -236,7 +253,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
if(this.options.display === false) { if(this.options.display === false) {
return; return;
} }
this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === ''; this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
//emptytext shown only for enabled //emptytext shown only for enabled
@ -457,11 +474,14 @@ Makes editable any HTML element on the page. Applied as jQuery method.
return result; return result;
/** /**
Returns current values of editable elements. If value is <code>null</code> or <code>undefined</code> it will not be returned 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.
@method getValue() @method getValue()
@returns {Object} object of element names and values @returns {Object} object of element names and values
@example @example
$('#username, #fullname').editable('validate'); $('#username, #fullname').editable('getValue');
// possible result: // possible result:
{ {
username: "superuser", username: "superuser",
@ -598,8 +618,20 @@ Makes editable any HTML element on the page. Applied as jQuery method.
**/ **/
autotext: 'auto', autotext: 'auto',
/** /**
Initial value of input. If not set, taken from element's text. 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 @property value
@type mixed @type mixed
@default element's text @default element's text
@ -610,12 +642,12 @@ Makes editable any HTML element on the page. Applied as jQuery method.
If `null`, default input's display used. If `null`, default input's display used.
If `false`, no displaying methods will be called, element's text will never change. If `false`, no displaying methods will be called, element's text will never change.
Runs under element's scope. Runs under element's scope.
_Parameters:_ _**Parameters:**_
* `value` current value to be displayed * `value` current value to be displayed
* `response` server response (if display called after ajax submit), since 1.4.0 * `response` server response (if display called after ajax submit), since 1.4.0
For **inputs with source** (select, checklist) parameters are different: For _inputs with source_ (select, checklist) parameters are different:
* `value` current value to be displayed * `value` current value to be displayed
* `sourceData` array of items for current input (e.g. dropdown items) * `sourceData` array of items for current input (e.g. dropdown items)
@ -662,10 +694,12 @@ Makes editable any HTML element on the page. Applied as jQuery method.
**/ **/
unsavedclass: 'editable-unsaved', unsavedclass: 'editable-unsaved',
/** /**
If a css selector is provided, editable will be delegated to the specified targets. If selector is provided, editable will be delegated to the specified targets.
Usefull for dynamically generated DOM elements. Usefull for dynamically generated DOM elements.
**Please note**, that delegated targets can't use `emptytext` and `autotext` options, **Please note**, that delegated targets can't be initialized with `emptytext` and `autotext` options,
as they are initialized after first click. 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 @property selector
@type string @type string
@ -673,8 +707,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
@default null @default null
@example @example
<div id="user"> <div id="user">
<a href="#" data-name="username" data-type="text" title="Username">awesome</a> <!-- empty -->
<a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a> <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> </div>
<script> <script>

@ -111,6 +111,9 @@ $(function(){
@param {mixed} value @param {mixed} value
**/ **/
value2input: function(value) { value2input: function(value) {
if(!value) {
return;
}
this.$input.filter('[name="city"]').val(value.city); this.$input.filter('[name="city"]').val(value.city);
this.$input.filter('[name="street"]').val(value.street); this.$input.filter('[name="street"]').val(value.street);
this.$input.filter('[name="building"]').val(value.building); this.$input.filter('[name="building"]').val(value.building);

@ -0,0 +1,499 @@
!function($, wysi) {
"use strict";
var tpl = {
"font-styles": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li class='dropdown'>" +
"<a class='btn dropdown-toggle" + size + "' data-toggle='dropdown' href='#'>" +
"<i class='icon-font'></i>&nbsp;<span class='current-font'>" + locale.font_styles.normal + "</span>&nbsp;<b class='caret'></b>" +
"</a>" +
"<ul class='dropdown-menu'>" +
"<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='div' tabindex='-1'>" + locale.font_styles.normal + "</a></li>" +
"<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h1' tabindex='-1'>" + locale.font_styles.h1 + "</a></li>" +
"<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h2' tabindex='-1'>" + locale.font_styles.h2 + "</a></li>" +
"<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h3' tabindex='-1'>" + locale.font_styles.h3 + "</a></li>" +
"</ul>" +
"</li>";
},
"emphasis": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li>" +
"<div class='btn-group'>" +
"<a class='btn" + size + "' data-wysihtml5-command='bold' title='CTRL+B' tabindex='-1'>" + locale.emphasis.bold + "</a>" +
"<a class='btn" + size + "' data-wysihtml5-command='italic' title='CTRL+I' tabindex='-1'>" + locale.emphasis.italic + "</a>" +
"<a class='btn" + size + "' data-wysihtml5-command='underline' title='CTRL+U' tabindex='-1'>" + locale.emphasis.underline + "</a>" +
"</div>" +
"</li>";
},
"lists": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li>" +
"<div class='btn-group'>" +
"<a class='btn" + size + "' data-wysihtml5-command='insertUnorderedList' title='" + locale.lists.unordered + "' tabindex='-1'><i class='icon-list'></i></a>" +
"<a class='btn" + size + "' data-wysihtml5-command='insertOrderedList' title='" + locale.lists.ordered + "' tabindex='-1'><i class='icon-th-list'></i></a>" +
"<a class='btn" + size + "' data-wysihtml5-command='Outdent' title='" + locale.lists.outdent + "' tabindex='-1'><i class='icon-indent-right'></i></a>" +
"<a class='btn" + size + "' data-wysihtml5-command='Indent' title='" + locale.lists.indent + "' tabindex='-1'><i class='icon-indent-left'></i></a>" +
"</div>" +
"</li>";
},
"link": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li>" +
"<div class='bootstrap-wysihtml5-insert-link-modal modal hide fade'>" +
"<div class='modal-header'>" +
"<a class='close' data-dismiss='modal'>&times;</a>" +
"<h3>" + locale.link.insert + "</h3>" +
"</div>" +
"<div class='modal-body'>" +
"<input value='http://' class='bootstrap-wysihtml5-insert-link-url input-xlarge'>" +
"</div>" +
"<div class='modal-footer'>" +
"<a href='#' class='btn' data-dismiss='modal'>" + locale.link.cancel + "</a>" +
"<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.link.insert + "</a>" +
"</div>" +
"</div>" +
"<a class='btn" + size + "' data-wysihtml5-command='createLink' title='" + locale.link.insert + "' tabindex='-1'><i class='icon-share'></i></a>" +
"</li>";
},
"image": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li>" +
"<div class='bootstrap-wysihtml5-insert-image-modal modal hide fade'>" +
"<div class='modal-header'>" +
"<a class='close' data-dismiss='modal'>&times;</a>" +
"<h3>" + locale.image.insert + "</h3>" +
"</div>" +
"<div class='modal-body'>" +
"<input value='http://' class='bootstrap-wysihtml5-insert-image-url input-xlarge'>" +
"</div>" +
"<div class='modal-footer'>" +
"<a href='#' class='btn' data-dismiss='modal'>" + locale.image.cancel + "</a>" +
"<a href='#' class='btn btn-primary' data-dismiss='modal'>" + locale.image.insert + "</a>" +
"</div>" +
"</div>" +
"<a class='btn" + size + "' data-wysihtml5-command='insertImage' title='" + locale.image.insert + "' tabindex='-1'><i class='icon-picture'></i></a>" +
"</li>";
},
"html": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li>" +
"<div class='btn-group'>" +
"<a class='btn" + size + "' data-wysihtml5-action='change_view' title='" + locale.html.edit + "' tabindex='-1'><i class='icon-pencil'></i></a>" +
"</div>" +
"</li>";
},
"color": function(locale, options) {
var size = (options && options.size) ? ' btn-'+options.size : '';
return "<li class='dropdown'>" +
"<a class='btn dropdown-toggle" + size + "' data-toggle='dropdown' href='#' tabindex='-1'>" +
"<span class='current-color'>" + locale.colours.black + "</span>&nbsp;<b class='caret'></b>" +
"</a>" +
"<ul class='dropdown-menu'>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='black'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='black'>" + locale.colours.black + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='silver'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='silver'>" + locale.colours.silver + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='gray'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='gray'>" + locale.colours.gray + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='maroon'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='maroon'>" + locale.colours.maroon + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='red'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='red'>" + locale.colours.red + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='purple'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='purple'>" + locale.colours.purple + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='green'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='green'>" + locale.colours.green + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='olive'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='olive'>" + locale.colours.olive + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='navy'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='navy'>" + locale.colours.navy + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='blue'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='blue'>" + locale.colours.blue + "</a></li>" +
"<li><div class='wysihtml5-colors' data-wysihtml5-command-value='orange'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='orange'>" + locale.colours.orange + "</a></li>" +
"</ul>" +
"</li>";
}
};
var templates = function(key, locale, options) {
return tpl[key](locale, options);
};
var Wysihtml5 = function(el, options) {
this.el = el;
var toolbarOpts = options || defaultOptions;
for(var t in toolbarOpts.customTemplates) {
tpl[t] = toolbarOpts.customTemplates[t];
}
this.toolbar = this.createToolbar(el, toolbarOpts);
this.editor = this.createEditor(options);
window.editor = this.editor;
$('iframe.wysihtml5-sandbox').each(function(i, el){
$(el.contentWindow).off('focus.wysihtml5').on({
'focus.wysihtml5' : function(){
$('li.dropdown').removeClass('open');
}
});
});
};
Wysihtml5.prototype = {
constructor: Wysihtml5,
createEditor: function(options) {
options = options || {};
// Add the toolbar to a clone of the options object so multiple instances
// of the WYISYWG don't break because "toolbar" is already defined
options = $.extend(true, {}, options);
options.toolbar = this.toolbar[0];
var editor = new wysi.Editor(this.el[0], options);
if(options && options.events) {
for(var eventName in options.events) {
editor.on(eventName, options.events[eventName]);
}
}
return editor;
},
createToolbar: function(el, options) {
var self = this;
var toolbar = $("<ul/>", {
'class' : "wysihtml5-toolbar",
'style': "display:none"
});
var culture = options.locale || defaultOptions.locale || "en";
for(var key in defaultOptions) {
var value = false;
if(options[key] !== undefined) {
if(options[key] === true) {
value = true;
}
} else {
value = defaultOptions[key];
}
if(value === true) {
toolbar.append(templates(key, locale[culture], options));
if(key === "html") {
this.initHtml(toolbar);
}
if(key === "link") {
this.initInsertLink(toolbar);
}
if(key === "image") {
this.initInsertImage(toolbar);
}
}
}
if(options.toolbar) {
for(key in options.toolbar) {
toolbar.append(options.toolbar[key]);
}
}
toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
var target = e.target || e.srcElement;
var el = $(target);
self.toolbar.find('.current-font').text(el.html());
});
toolbar.find("a[data-wysihtml5-command='foreColor']").click(function(e) {
var target = e.target || e.srcElement;
var el = $(target);
self.toolbar.find('.current-color').text(el.html());
});
this.el.before(toolbar);
return toolbar;
},
initHtml: function(toolbar) {
var changeViewSelector = "a[data-wysihtml5-action='change_view']";
toolbar.find(changeViewSelector).click(function(e) {
toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
});
},
initInsertImage: function(toolbar) {
var self = this;
var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
var insertButton = insertImageModal.find('a.btn-primary');
var initialValue = urlInput.val();
var caretBookmark;
var insertImage = function() {
var url = urlInput.val();
urlInput.val(initialValue);
self.editor.currentView.element.focus();
if (caretBookmark) {
self.editor.composer.selection.setBookmark(caretBookmark);
caretBookmark = null;
}
self.editor.composer.commands.exec("insertImage", url);
};
urlInput.keypress(function(e) {
if(e.which == 13) {
insertImage();
insertImageModal.modal('hide');
}
});
insertButton.click(insertImage);
insertImageModal.on('shown', function() {
urlInput.focus();
});
insertImageModal.on('hide', function() {
self.editor.currentView.element.focus();
});
toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
var activeButton = $(this).hasClass("wysihtml5-command-active");
if (!activeButton) {
self.editor.currentView.element.focus(false);
caretBookmark = self.editor.composer.selection.getBookmark();
insertImageModal.appendTo('body').modal('show');
insertImageModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
e.stopPropagation();
});
return false;
}
else {
return true;
}
});
},
initInsertLink: function(toolbar) {
var self = this;
var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
var insertButton = insertLinkModal.find('a.btn-primary');
var initialValue = urlInput.val();
var caretBookmark;
var insertLink = function() {
var url = urlInput.val();
urlInput.val(initialValue);
self.editor.currentView.element.focus();
if (caretBookmark) {
self.editor.composer.selection.setBookmark(caretBookmark);
caretBookmark = null;
}
self.editor.composer.commands.exec("createLink", {
href: url,
target: "_blank",
rel: "nofollow"
});
};
var pressedEnter = false;
urlInput.keypress(function(e) {
if(e.which == 13) {
insertLink();
insertLinkModal.modal('hide');
}
});
insertButton.click(insertLink);
insertLinkModal.on('shown', function() {
urlInput.focus();
});
insertLinkModal.on('hide', function() {
self.editor.currentView.element.focus();
});
toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
var activeButton = $(this).hasClass("wysihtml5-command-active");
if (!activeButton) {
self.editor.currentView.element.focus(false);
caretBookmark = self.editor.composer.selection.getBookmark();
insertLinkModal.appendTo('body').modal('show');
insertLinkModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
e.stopPropagation();
});
return false;
}
else {
return true;
}
});
}
};
// these define our public api
var methods = {
resetDefaults: function() {
$.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
},
bypassDefaults: function(options) {
return this.each(function () {
var $this = $(this);
$this.data('wysihtml5', new Wysihtml5($this, options));
});
},
shallowExtend: function (options) {
var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {});
var that = this;
return methods.bypassDefaults.apply(that, [settings]);
},
deepExtend: function(options) {
var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
var that = this;
return methods.bypassDefaults.apply(that, [settings]);
},
init: function(options) {
var that = this;
return methods.shallowExtend.apply(that, [options]);
}
};
$.fn.wysihtml5 = function ( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.wysihtml5' );
}
};
$.fn.wysihtml5.Constructor = Wysihtml5;
var defaultOptions = $.fn.wysihtml5.defaultOptions = {
"font-styles": true,
"color": false,
"emphasis": true,
"lists": true,
"html": false,
"link": true,
"image": true,
events: {},
parserRules: {
classes: {
// (path_to_project/lib/css/wysiwyg-color.css)
"wysiwyg-color-silver" : 1,
"wysiwyg-color-gray" : 1,
"wysiwyg-color-white" : 1,
"wysiwyg-color-maroon" : 1,
"wysiwyg-color-red" : 1,
"wysiwyg-color-purple" : 1,
"wysiwyg-color-fuchsia" : 1,
"wysiwyg-color-green" : 1,
"wysiwyg-color-lime" : 1,
"wysiwyg-color-olive" : 1,
"wysiwyg-color-yellow" : 1,
"wysiwyg-color-navy" : 1,
"wysiwyg-color-blue" : 1,
"wysiwyg-color-teal" : 1,
"wysiwyg-color-aqua" : 1,
"wysiwyg-color-orange" : 1
},
tags: {
"b": {},
"i": {},
"br": {},
"ol": {},
"ul": {},
"li": {},
"h1": {},
"h2": {},
"h3": {},
"blockquote": {},
"u": 1,
"img": {
"check_attributes": {
"width": "numbers",
"alt": "alt",
"src": "url",
"height": "numbers"
}
},
"a": {
set_attributes: {
target: "_blank",
rel: "nofollow"
},
check_attributes: {
href: "url" // important to avoid XSS
}
},
"span": 1,
"div": 1,
// to allow save and edit files with code tag hacks
"code": 1,
"pre": 1
}
},
stylesheets: ["./lib/css/wysiwyg-color.css"], // (path_to_project/lib/css/wysiwyg-color.css)
locale: "en"
};
if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
$.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
}
var locale = $.fn.wysihtml5.locale = {
en: {
font_styles: {
normal: "Normal text",
h1: "Heading 1",
h2: "Heading 2",
h3: "Heading 3"
},
emphasis: {
bold: "Bold",
italic: "Italic",
underline: "Underline"
},
lists: {
unordered: "Unordered list",
ordered: "Ordered list",
outdent: "Outdent",
indent: "Indent"
},
link: {
insert: "Insert link",
cancel: "Cancel"
},
image: {
insert: "Insert image",
cancel: "Cancel"
},
html: {
edit: "Edit HTML"
},
colours: {
black: "Black",
silver: "Silver",
gray: "Grey",
maroon: "Maroon",
red: "Red",
purple: "Purple",
green: "Green",
olive: "Olive",
navy: "Navy",
blue: "Blue",
orange: "Orange"
}
}
};
}(window.jQuery, window.wysihtml5);

File diff suppressed because one or more lines are too long

@ -7,6 +7,8 @@ You should include this input **manually** with dependent js and css files from
<script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min.js"></script> <script src="js/inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min.js"></script>
<script src="js/inputs-ext/wysihtml5/wysihtml5.js"></script> <script src="js/inputs-ext/wysihtml5/wysihtml5.js"></script>
**Note:** It's better to use fresh bootstrap-wysihtml5 from it's [master branch](https://github.com/jhollingworth/bootstrap-wysihtml5/tree/master/src) as there is update for correct image insertion.
@class wysihtml5 @class wysihtml5
@extends abstractinput @extends abstractinput
@final @final
@ -35,8 +37,9 @@ $(function(){
$.extend(Wysihtml5.prototype, { $.extend(Wysihtml5.prototype, {
render: function () { render: function () {
var deferred = $.Deferred(); var deferred = $.Deferred(),
msieOld;
//generate unique id as it required for wysihtml5 //generate unique id as it required for wysihtml5
this.$input.attr('id', 'textarea_'+(new Date()).getTime()); this.$input.attr('id', 'textarea_'+(new Date()).getTime());
@ -56,9 +59,10 @@ $(function(){
/* /*
In IE8 wysihtml5 iframe stays on the same line with buttons toolbar (inside popover). In IE8 wysihtml5 iframe stays on the same line with buttons toolbar (inside popover).
Not pretty but working solution is to add <br>. If you fine better way, please send PR. The only solution I found is to add <br>. If you fine better way, please send PR.
*/ */
if($.browser.msie && parseInt($.browser.version, 10) <= 8) { msieOld = /msie\s*(8|7|6)/.test(navigator.userAgent.toLowerCase());
if(msieOld) {
this.$input.before('<br><br>'); this.$input.before('<br><br>');
} }

@ -159,7 +159,7 @@ To create your own input you can inherit from this class.
}, },
setAttr: function(attr) { setAttr: function(attr) {
if (this.options[attr]) { if (this.options[attr] !== undefined && this.options[attr] !== null) {
this.$input.attr(attr, this.options[attr]); this.$input.attr(attr, this.options[attr]);
} }
}, },
@ -186,7 +186,10 @@ To create your own input you can inherit from this class.
@type string @type string
@default input-medium @default input-medium
**/ **/
inputclass: 'input-medium' inputclass: 'input-medium',
//scope for external methods (e.g. source defined as function)
//for internal use only
scope: null
}; };
$.extend($.fn.editabletypes, {abstractinput: AbstractInput}); $.extend($.fn.editabletypes, {abstractinput: AbstractInput});

@ -16,7 +16,6 @@ $(function(){
{value: 2, text: 'option2'}, {value: 2, text: 'option2'},
{value: 3, text: 'option3'} {value: 3, text: 'option3'}
] ]
}
}); });
}); });
</script> </script>

@ -1,6 +1,6 @@
/** /**
Combodate input - dropdown date and time picker. Combodate input - dropdown date and time picker.
Based on [combodate](http://vitalets.github.com/combodate) plugin. To use it you should manually include [momentjs](http://momentjs.com). 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> <script src="js/moment.min.js"></script>
@ -48,6 +48,9 @@ $(function(){
this.options.viewformat = this.options.format; 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) //overriding combodate config (as by default jQuery extend() is not recursive)
this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, { this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, {
format: this.options.format, format: this.options.format,

@ -1,5 +1,5 @@
/** /**
* Combodate - 1.0.1 * Combodate - 1.0.2
* Dropdown date and time picker. * Dropdown date and time picker.
* Converts text input into dropdowns to pick day, month, year, hour, minute and second. * Converts text input into dropdowns to pick day, month, year, hour, minute and second.
* Uses momentjs as datetime library http://momentjs.com. * Uses momentjs as datetime library http://momentjs.com.
@ -102,9 +102,13 @@
Initialize items of combos. Handles `firstItem` option Initialize items of combos. Handles `firstItem` option
*/ */
initItems: function(key) { initItems: function(key) {
var values = []; var values = [],
relTime;
if(this.options.firstItem === 'name') { if(this.options.firstItem === 'name') {
var header = typeof moment.relativeTime[key] === 'function' ? moment.relativeTime[key](1, true, key, false) : moment.relativeTime[key]; //need both to suuport 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) //take last entry (see momentjs lang files structure)
header = header.split(' ').reverse()[0]; header = header.split(' ').reverse()[0];
values.push(['', header]); values.push(['', header]);
@ -150,9 +154,9 @@
for(i=0; i<=11; i++) { for(i=0; i<=11; i++) {
if(longNames) { if(longNames) {
name = moment.months[i]; name = moment().month(i).format('MMMM');
} else if(shortNames) { } else if(shortNames) {
name = moment.monthsShort[i]; name = moment().month(i).format('MMM');
} else if(twoDigit) { } else if(twoDigit) {
name = this.leadZero(i+1); name = this.leadZero(i+1);
} else { } else {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -282,6 +282,16 @@
.datepicker tfoot tr:first-child th:hover { .datepicker tfoot tr:first-child th:hover {
background: #eeeeee; 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-append.date .add-on i,
.input-prepend.date .add-on i { .input-prepend.date .add-on i {
display: block; display: block;

@ -35,9 +35,10 @@
this.element = $(element); this.element = $(element);
this.language = options.language||this.element.data('date-language')||"en"; this.language = options.language||this.element.data('date-language')||"en";
this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de"
this.language = this.language in dates ? this.language : "en"; this.language = this.language in dates ? this.language : "en";
this.isRTL = dates[this.language].rtl||false; this.isRTL = dates[this.language].rtl||false;
this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy'); this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy');
this.isInline = false; this.isInline = false;
this.isInput = this.element.is('input'); this.isInput = this.element.is('input');
this.component = this.element.is('.date') ? this.element.find('.add-on') : false; this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
@ -53,7 +54,7 @@
} else if ('dateForceParse' in this.element.data()) { } else if ('dateForceParse' in this.element.data()) {
this.forceParse = this.element.data('date-force-parse'); this.forceParse = this.element.data('date-force-parse');
} }
this.picker = $(DPGlobal.template) this.picker = $(DPGlobal.template)
.appendTo(this.isInline ? this.element : 'body') .appendTo(this.isInline ? this.element : 'body')
@ -74,7 +75,7 @@
} }
$(document).on('mousedown', function (e) { $(document).on('mousedown', function (e) {
// Clicked outside the datepicker, hide it // Clicked outside the datepicker, hide it
if ($(e.target).closest('.datepicker').length === 0) { if ($(e.target).closest('.datepicker.datepicker-inline, .datepicker.datepicker-dropdown').length === 0) {
that.hide(); that.hide();
} }
}); });
@ -105,9 +106,38 @@
break; break;
} }
this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0;
if (typeof this.minViewMode === 'string') {
switch (this.minViewMode) {
case 'months':
this.minViewMode = 1;
break;
case 'years':
this.minViewMode = 2;
break;
default:
this.minViewMode = 0;
break;
}
}
this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode);
this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false);
this.calendarWeeks = false;
if ('calendarWeeks' in options) {
this.calendarWeeks = options.calendarWeeks;
} else if ('dateCalendarWeeks' in this.element.data()) {
this.calendarWeeks = this.element.data('date-calendar-weeks');
}
if (this.calendarWeeks)
this.picker.find('tfoot th.today')
.attr('colspan', function(i, val){
return parseInt(val) + 1;
});
this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7);
this.weekEnd = ((this.weekStart + 6) % 7); this.weekEnd = ((this.weekStart + 6) % 7);
this.startDate = -Infinity; this.startDate = -Infinity;
@ -197,6 +227,7 @@
hide: function(e){ hide: function(e){
if(this.isInline) return; if(this.isInline) return;
if (!this.picker.is(':visible')) return;
this.picker.hide(); this.picker.hide();
$(window).off('resize', this.place); $(window).off('resize', this.place);
this.viewMode = this.startViewMode; this.viewMode = this.startViewMode;
@ -318,7 +349,6 @@
if(fromArgs) this.setValue(); if(fromArgs) this.setValue();
var oldViewDate = this.viewDate;
if (this.date < this.startDate) { if (this.date < this.startDate) {
this.viewDate = new Date(this.startDate); this.viewDate = new Date(this.startDate);
} else if (this.date > this.endDate) { } else if (this.date > this.endDate) {
@ -326,19 +356,17 @@
} else { } else {
this.viewDate = new Date(this.date); this.viewDate = new Date(this.date);
} }
if (oldViewDate && oldViewDate.getTime() != this.viewDate.getTime()){
this.element.trigger({
type: 'changeDate',
date: this.viewDate
});
}
this.fill(); this.fill();
}, },
fillDow: function(){ fillDow: function(){
var dowCnt = this.weekStart, var dowCnt = this.weekStart,
html = '<tr>'; html = '<tr>';
if(this.calendarWeeks){
var cell = '<th class="cw">&nbsp;</th>';
html += cell;
this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
}
while (dowCnt < this.weekStart + 7) { while (dowCnt < this.weekStart + 7) {
html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>'; html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
} }
@ -365,7 +393,7 @@
endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
currentDate = this.date && this.date.valueOf(), currentDate = this.date && this.date.valueOf(),
today = new Date(); today = new Date();
this.picker.find('.datepicker-days thead th:eq(1)') this.picker.find('.datepicker-days thead th.switch')
.text(dates[this.language].months[month]+' '+year); .text(dates[this.language].months[month]+' '+year);
this.picker.find('tfoot th.today') this.picker.find('tfoot th.today')
.text(dates[this.language].today) .text(dates[this.language].today)
@ -384,6 +412,21 @@
while(prevMonth.valueOf() < nextMonth) { while(prevMonth.valueOf() < nextMonth) {
if (prevMonth.getUTCDay() == this.weekStart) { if (prevMonth.getUTCDay() == this.weekStart) {
html.push('<tr>'); html.push('<tr>');
if(this.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.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 = ''; clsName = '';
if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) { if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
@ -519,19 +562,29 @@
if (!target.is('.disabled')) { if (!target.is('.disabled')) {
this.viewDate.setUTCDate(1); this.viewDate.setUTCDate(1);
if (target.is('.month')) { if (target.is('.month')) {
var day = 1;
var month = target.parent().find('span').index(target); var month = target.parent().find('span').index(target);
var year = this.viewDate.getUTCFullYear();
this.viewDate.setUTCMonth(month); this.viewDate.setUTCMonth(month);
this.element.trigger({ this.element.trigger({
type: 'changeMonth', type: 'changeMonth',
date: this.viewDate date: this.viewDate
}); });
if ( this.minViewMode == 1 ) {
this._setDate(UTCDate(year, month, day,0,0,0,0));
}
} else { } else {
var year = parseInt(target.text(), 10)||0; var year = parseInt(target.text(), 10)||0;
var day = 1;
var month = 0;
this.viewDate.setUTCFullYear(year); this.viewDate.setUTCFullYear(year);
this.element.trigger({ this.element.trigger({
type: 'changeYear', type: 'changeYear',
date: this.viewDate date: this.viewDate
}); });
if ( this.minViewMode == 2 ) {
this._setDate(UTCDate(year, month, day,0,0,0,0));
}
} }
this.showMode(-1); this.showMode(-1);
this.fill(); this.fill();
@ -728,7 +781,7 @@
showMode: function(dir) { showMode: function(dir) {
if (dir) { if (dir) {
this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir)); this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
} }
/* /*
vitalets: fixing bug of very special conditions: vitalets: fixing bug of very special conditions:

@ -111,12 +111,24 @@ $(function(){
}, },
autosubmit: function() { 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){ this.$input.on('changeDate', function(e){
var $form = $(this).closest('form'); var $form = $(this).closest('form');
setTimeout(function() { setTimeout(function() {
$form.submit(); $form.submit();
}, 200); }, 200);
}); });
*/
} }
}); });
@ -159,12 +171,14 @@ $(function(){
@default { @default {
weekStart: 0, weekStart: 0,
startView: 0, startView: 0,
minViewMode: 0,
autoclose: false autoclose: false
} }
**/ **/
datepicker:{ datepicker:{
weekStart: 0, weekStart: 0,
startView: 0, startView: 0,
minViewMode: 0,
autoclose: false autoclose: false
}, },
/** /**

@ -69,6 +69,7 @@ Automatically shown in inline mode.
datepicker: { datepicker: {
weekStart: 0, weekStart: 0,
startView: 0, startView: 0,
minViewMode: 0,
autoclose: true autoclose: true
} }
}); });

@ -70,12 +70,19 @@ List - abstract class for inputs that have source option loaded from js array or
error.call(this); error.call(this);
return; return;
} }
var source = this.options.source;
//run source if it function
if ($.isFunction(source)) {
source = source.call(this.options.scope);
}
//loading from url //loading from url
if (typeof this.options.source === 'string') { if (typeof source === 'string') {
//try to get from cache //try to get from cache
if(this.options.sourceCache) { if(this.options.sourceCache) {
var cacheID = this.options.source, var cacheID = source,
cache; cache;
if (!$(document).data(cacheID)) { if (!$(document).data(cacheID)) {
@ -108,7 +115,7 @@ List - abstract class for inputs that have source option loaded from js array or
//loading sourceData from server //loading sourceData from server
$.ajax({ $.ajax({
url: this.options.source, url: source,
type: 'get', type: 'get',
cache: false, cache: false,
dataType: 'json', dataType: 'json',
@ -143,12 +150,8 @@ List - abstract class for inputs that have source option loaded from js array or
} }
}, this) }, this)
}); });
} else { //options as json/array/function } else { //options as json/array
if ($.isFunction(this.options.source)) { this.sourceData = this.makeArray(source);
this.sourceData = this.makeArray(this.options.source());
} else {
this.sourceData = this.makeArray(this.options.source);
}
if($.isArray(this.sourceData)) { if($.isArray(this.sourceData)) {
this.doPrepend(); this.doPrepend();
@ -165,16 +168,20 @@ List - abstract class for inputs that have source option loaded from js array or
} }
if(!$.isArray(this.prependData)) { 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 //try parse json in single quotes
this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true); this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
//convert prepend from string to object
if (typeof this.options.prepend === 'string') { if (typeof this.options.prepend === 'string') {
this.options.prepend = {'': this.options.prepend}; this.options.prepend = {'': this.options.prepend};
}
if (typeof this.options.prepend === 'function') {
this.prependData = this.makeArray(this.options.prepend());
} else {
this.prependData = this.makeArray(this.options.prepend);
} }
this.prependData = this.makeArray(this.options.prepend);
} }
if($.isArray(this.prependData) && $.isArray(this.sourceData)) { if($.isArray(this.prependData) && $.isArray(this.sourceData)) {

@ -15,7 +15,6 @@ $(function(){
{value: 2, text: 'Blocked'}, {value: 2, text: 'Blocked'},
{value: 3, text: 'Deleted'} {value: 3, text: 'Deleted'}
] ]
}
}); });
}); });
</script> </script>
@ -85,4 +84,4 @@ $(function(){
$.fn.editabletypes.select = Select; $.fn.editabletypes.select = Select;
}(window.jQuery)); }(window.jQuery));

@ -6,6 +6,9 @@ You should manually include select2 distributive:
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link> <link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
<script src="select2/select2.js"></script> <script src="select2/select2.js"></script>
**Note:** currently `ajax` source for select2 is not supported, as it's not possible to load it in closed select2 state.
The solution is to load source manually and assign statically.
@class select2 @class select2
@extends abstractinput @extends abstractinput
@since 1.4.1 @since 1.4.1
@ -35,14 +38,14 @@ $(function(){
options.select2 = options.select2 || {}; options.select2 = options.select2 || {};
var that = this, var that = this,
mixin = { mixin = { //mixin to select2 options
placeholder: options.placeholder placeholder: options.placeholder
}; };
//detect whether it is multi-valued //detect whether it is multi-valued
this.isMultiple = options.select2.tags || options.select2.multiple; this.isMultiple = options.select2.tags || options.select2.multiple;
//if not `tags` mode, we need define init set data from source //if not `tags` mode, we need define initSelection to set data from source
if(!options.select2.tags) { if(!options.select2.tags) {
if(options.source) { if(options.source) {
mixin.data = options.source; mixin.data = options.source;
@ -50,6 +53,23 @@ $(function(){
//this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710 //this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
mixin.initSelection = function (element, callback) { mixin.initSelection = function (element, callback) {
//temp: try update results
/*
if(options.select2 && options.select2.ajax) {
console.log('attached');
var original = $(element).data('select2').postprocessResults;
console.log(original);
$(element).data('select2').postprocessResults = function(data, initial) {
console.log('postprocess');
// this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
// $(element).on('loaded', function(){console.log('loaded');});
$(element).data('select2').updateResults(true);
}
*/
var val = that.str2value(element.val()), var val = that.str2value(element.val()),
data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id'); data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
@ -74,18 +94,30 @@ $(function(){
//apply select2 //apply select2
this.$input.select2(this.options.select2); this.$input.select2(this.options.select2);
//when data is loaded via ajax, we need to know when it's done
if('ajax' in this.options.select2) {
/*
console.log('attached');
var original = this.$input.data('select2').postprocessResults;
this.$input.data('select2').postprocessResults = function(data, initial) {
this.element.triggerHandler('loaded', [data]);
original.apply(this, arguments);
}
*/
}
//trigger resize of editableform to re-position container in multi-valued mode //trigger resize of editableform to re-position container in multi-valued mode
if(this.isMultiple) { if(this.isMultiple) {
this.$input.on('change', function() { this.$input.on('change', function() {
$(this).closest('form').parent().triggerHandler('resize'); $(this).closest('form').parent().triggerHandler('resize');
}); });
} }
},
},
value2html: function(value, element) { value2html: function(value, element) {
var text = '', data; var text = '', data;
if(this.$input) { //when submitting form if(this.$input) { //called when submitting form and select2 already exists
data = this.$input.select2('data'); data = this.$input.select2('data');
} else { //on init (autotext) } else { //on init (autotext)
//here select2 instance not created yet and data may be even not loaded. //here select2 instance not created yet and data may be even not loaded.
@ -94,6 +126,8 @@ $(function(){
data = value; data = value;
} else if(this.options.select2.data) { } else if(this.options.select2.data) {
data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id'); data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
} else {
//if('ajax' in this.options.select2) {
} }
} }
@ -117,7 +151,7 @@ $(function(){
}, },
value2input: function(value) { value2input: function(value) {
this.$input.val(value).trigger('change'); this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
}, },
input2value: function() { input2value: function() {
@ -142,7 +176,15 @@ $(function(){
} }
return val; return val;
} },
autosubmit: function() {
this.$input.on('change', function(e, isInitial){
if(!isInitial) {
$(this).closest('form').submit();
}
});
}
}); });

@ -44,8 +44,20 @@ $(function(){
if (this.options.clear) { if (this.options.clear) {
this.$clear = $('<span class="editable-clear-x"></span>'); this.$clear = $('<span class="editable-clear-x"></span>');
this.$input.after(this.$clear) this.$input.after(this.$clear)
.css('padding-right', 20) .css('padding-right', 24)
.keyup($.proxy(this.toggleClear, this)) .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'); .parent().css('position', 'relative');
this.$clear.click($.proxy(this.clear, this)); this.$clear.click($.proxy(this.clear, this));
@ -63,19 +75,24 @@ $(function(){
delta = 3; delta = 3;
} }
this.$clear.css({top: delta, right: delta}); this.$clear.css({bottom: delta, right: delta});
} }
}, },
//show / hide clear button //show / hide clear button
toggleClear: function() { toggleClear: function(e) {
if(!this.$clear) { if(!this.$clear) {
return; return;
} }
if(this.$input.val().length) { var len = this.$input.val().length,
visible = this.$clear.is(':visible');
if(len && !visible) {
this.$clear.show(); this.$clear.show();
} else { }
if(!len && visible) {
this.$clear.hide(); this.$clear.hide();
} }
}, },

@ -25,7 +25,6 @@ $(function(){
{value: 'us', text: 'United States'}, {value: 'us', text: 'United States'},
{value: 'ru', text: 'Russia'} {value: 'ru', text: 'Russia'}
] ]
}
}); });
}); });
</script> </script>
@ -57,8 +56,11 @@ $(function(){
//apply typeahead //apply typeahead
this.$input.typeahead(this.options.typeahead); this.$input.typeahead(this.options.typeahead);
//attach own render method //patch some methods in typeahead
this.$input.data('typeahead').render = $.proxy(this.typeaheadRender, this.$input.data('typeahead')); var ta = this.$input.data('typeahead');
ta.render = $.proxy(this.typeaheadRender, ta);
ta.select = $.proxy(this.typeaheadSelect, ta);
ta.move = $.proxy(this.typeaheadMove, ta);
this.renderClear(); this.renderClear();
this.setClass(); this.setClass();
@ -137,7 +139,7 @@ $(function(){
/* /*
Typeahead option methods used as defaults Typeahead option methods used as defaults
*/ */
/*jshint eqeqeq:false, curly: false, laxcomma: true*/ /*jshint eqeqeq:false, curly: false, laxcomma: true, asi: true*/
matcher: function (item) { matcher: function (item) {
return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text); return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text);
}, },
@ -161,7 +163,6 @@ $(function(){
return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text); return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text);
}, },
updater: function (item) { updater: function (item) {
item = this.$menu.find('.active').data('item');
this.$element.data('value', item.value); this.$element.data('value', item.value);
return item.text; return item.text;
}, },
@ -172,7 +173,7 @@ $(function(){
There are a lot of disscussion in bootstrap repo on this point and still no result. There are a lot of disscussion in bootstrap repo on this point and still no result.
See https://github.com/twitter/bootstrap/issues/5967 See https://github.com/twitter/bootstrap/issues/5967
This function just store item in via jQuery data() method instead of attr('data-value') This function just store item via jQuery data() method instead of attr('data-value')
*/ */
typeaheadRender: function (items) { typeaheadRender: function (items) {
var that = this; var that = this;
@ -184,11 +185,57 @@ $(function(){
return i[0]; return i[0];
}); });
items.first().addClass('active'); //add option to disable autoselect of first line
//see https://github.com/twitter/bootstrap/pull/4164
if (this.options.autoSelect) {
items.first().addClass('active');
}
this.$menu.html(items); this.$menu.html(items);
return this; return this;
},
//add option to disable autoselect of first line
//see https://github.com/twitter/bootstrap/pull/4164
typeaheadSelect: function () {
var val = this.$menu.find('.active').data('item')
if(this.options.autoSelect || val){
this.$element
.val(this.updater(val))
.change()
}
return this.hide()
},
/*
if autoSelect = false and nothing matched we need extra press onEnter that is not convinient.
This patch fixes it.
*/
typeaheadMove: function (e) {
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
if (!this.$menu.find('.active').length) return
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
e.stopPropagation()
} }
/*jshint eqeqeq: true, curly: true, laxcomma: false*/
/*jshint eqeqeq: true, curly: true, laxcomma: false, asi: false*/
}); });

4
test/libs/jquery/jquery-1.9.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

5
test/libs/jquery/jquery-1.9.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

@ -84,12 +84,14 @@ define(function () {
}, },
//wysihtml5 //wysihtml5
'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min': ['inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/wysihtml5-0.3.0.min'], // 'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min': ['inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/wysihtml5-0.3.0.min'],
'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2': ['inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/wysihtml5-0.3.0'],
'inputs-ext/wysihtml5/wysihtml5': { 'inputs-ext/wysihtml5/wysihtml5': {
deps: ['require', deps: ['require',
'bootstrap/js/bootstrap', 'bootstrap/js/bootstrap',
'inputs/abstract', 'inputs/abstract',
'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min'], // 'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.min'],
'inputs-ext/wysihtml5/bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2'],
init: function(require) { init: function(require) {
loadCss(require.toUrl("./bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.css")); loadCss(require.toUrl("./bootstrap-wysihtml5-0.0.2/bootstrap-wysihtml5-0.0.2.css"));
//loadCss(require.toUrl("./bootstrap-wysihtml5-0.0.2/wysiwyg-color.css")); //loadCss(require.toUrl("./bootstrap-wysihtml5-0.0.2/wysiwyg-color.css"));

@ -1,6 +1,6 @@
//detect version of jquery from url param, e.g. 'jquery=1.7.2' //detect version of jquery from url param, e.g. 'jquery=1.7.2'
var jqver = decodeURIComponent((new RegExp('[?|&]' + 'jquery' + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null; var jqver = decodeURIComponent((new RegExp('[?|&]' + 'jquery' + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, '%20'))||null;
jqurl = jqver ? "http://ajax.googleapis.com/ajax/libs/jquery/"+jqver+"/jquery.min.js" : "libs/jquery/jquery-1.8.2.min.js"; jqurl = jqver ? "http://code.jquery.com/jquery-"+jqver+".min.js" : "libs/jquery/jquery-1.9.1.min.js";
require(["loader", jqurl], function(loader) { require(["loader", jqurl], function(loader) {

@ -5,9 +5,7 @@ $(function () {
$.mockjax({ $.mockjax({
url: 'post.php', url: 'post.php',
responseText: { responseText: '{"success": true}'
success: true
}
}); });
$.mockjax({ $.mockjax({

@ -13,6 +13,20 @@ $(function () {
} }
}); });
test("combodate options can be defined in data-combodate param", function () {
var e = $('<a href="#" data-type="combodate" data-combodate="{minYear: 2000, maxYear: 2001}" data-pk="1" data-url="/combodate"></a>').appendTo('#qunit-fixture').editable({
format: fd,
viewformat: vfd,
template: fd
}),
m = moment(vd, vfd);
e.click();
var p = tip(e);
equal(p.find('.year option').length, 3, 'years applied correct');
});
asyncTest("container should contain combodate and save new value (date)", function () { asyncTest("container should contain combodate and save new value (date)", function () {
var e = $('<a href="#" data-type="combodate" data-pk="1" data-url="/combodate">'+vd+'</a>').appendTo(fx).editable({ var e = $('<a href="#" data-type="combodate" data-pk="1" data-url="/combodate">'+vd+'</a>').appendTo(fx).editable({

@ -80,8 +80,8 @@
var p = tip(e); var p = tip(e);
ok(p.is(':visible'), 'popover shown'); ok(p.is(':visible'), 'popover shown');
//todo: for jqueryui phantomjs calcs wrong position. Need investigation //todo: for jqueryui phantomjs calcs wrong position. Skip this test..
if(!$.browser.webkit && e.data('editableContainer').containerName !== 'tooltip') { if(!/phantom/i.test(navigator.userAgent) && e.data('editableContainer').containerName !== 'tooltip') {
ok(p.offset().top > e.offset().top, 'placement ok'); ok(p.offset().top > e.offset().top, 'placement ok');
} }
@ -276,8 +276,9 @@
test("should not wrap buttons when parent has position:absolute (except ie7)", function () { test("should not wrap buttons when parent has position:absolute (except ie7)", function () {
//skip this for: ie7 + bootstrap + popup //skip this for: ie7 + bootstrap + popup
if($.browser.msie && parseInt($.browser.version, 10) <= 8 && $.fn.editable.defaults.mode === 'popup' && $.fn.editableContainer.Popup.prototype.containerName === 'popover') { var msieOld = /msie\s*(8|7|6)/i.test(navigator.userAgent);
if(msieOld && $.fn.editable.defaults.mode === 'popup' && $.fn.editableContainer.Popup.prototype.containerName === 'popover') {
expect(0); expect(0);
return; return;
} }

@ -105,11 +105,15 @@ $(function () {
ok(!p.is(':visible'), 'popover was removed'); ok(!p.is(':visible'), 'popover was removed');
}); });
test("load options from function", function () { test("load options from function returning array", function () {
var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({ var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({
pk: 1, pk: 1,
prepend: 'prepend', prepend: function() {
equal(this, e[0], 'prepend scope is element');
return 'prepend';
},
source: function() { source: function() {
equal(this, e[0], 'source scope is element');
return groups; return groups;
} }
}); });
@ -124,6 +128,40 @@ $(function () {
ok(!p.is(':visible'), 'popover was removed'); ok(!p.is(':visible'), 'popover was removed');
}); });
asyncTest("load options from function returning URL", function () {
var e = $('<a href="#" data-type="select" data-value="2" data-url="post.php">customer</a>').appendTo('#qunit-fixture').editable({
pk: 1,
//need to disable cache to force request
sourceCache: false,
source: function() {
equal(this, e[0], 'source scope is element');
return 'groups.php';
}
});
e.click();
var p = tip(e);
setTimeout(function() {
ok(p.is(':visible'), 'popover visible');
ok(p.find('select').length, 'select exists');
equal(p.find('select').find('option').length, size, 'options loaded');
equal(p.find('select').val(), e.data('editable').value, 'selected value correct') ;
p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed');
//open second time: items should not dublicate
e.click();
ok(p.find('select').length, 'select exists');
equal(p.find('select').find('option').length, size, 'options loaded');
equal(p.find('select').val(), e.data('editable').value, 'selected value correct') ;
e.remove();
start();
}, timeout);
});
test("load options from html (single quotes)", function () { test("load options from html (single quotes)", function () {
var e = $('<a href="#" data-type="select" data-value="M" data-source=\'{"L":"Low", "": "None", "M": "Medium", "H": "High"}\'>customer</a>').appendTo('#qunit-fixture').editable({ var e = $('<a href="#" data-type="select" data-value="M" data-source=\'{"L":"Low", "": "None", "M": "Medium", "H": "High"}\'>customer</a>').appendTo('#qunit-fixture').editable({
pk: 1 pk: 1
@ -453,7 +491,7 @@ $(function () {
expect(3); expect(3);
//auto, text->empty, source->array //auto, text->empty, source->array
var e = $('<a href="#" data-type="select" data-value="3"></a>').appendTo(sfx).editable({ var e = $('<a href="#" data-type="select" data-value="3"> </a>').appendTo(sfx).editable({
source: groups, source: groups,
autotext: 'auto' autotext: 'auto'
}), }),
@ -566,6 +604,9 @@ $(function () {
e = $('<a href="#" data-type="select" data-value="2" data-url="post.php"></a>').appendTo(fx).editable({ e = $('<a href="#" data-type="select" data-value="2" data-url="post.php"></a>').appendTo(fx).editable({
pk: 1, pk: 1,
source: groups, source: groups,
ajaxOptions: {
dataType: 'json'
},
display: function(value, sourceData, response) { display: function(value, sourceData, response) {
var els = $.grep(sourceData, function(o) {return o.value == value;}); var els = $.grep(sourceData, function(o) {return o.value == value;});
$(this).text('qq' + els[0].text); $(this).text('qq' + els[0].text);

@ -15,8 +15,9 @@ $(function () {
equal(p.find('input[type=text]').attr('placeholder'), 'abc', 'placeholder exists'); equal(p.find('input[type=text]').attr('placeholder'), 'abc', 'placeholder exists');
p.find('.editable-cancel').click(); p.find('.editable-cancel').click();
ok(!p.is(':visible'), 'popover was removed'); ok(!p.is(':visible'), 'popover was removed');
}); });
asyncTest("should load correct value and save new entered text (and value)", function () { asyncTest("should load correct value and save new entered text (and value)", function () {
var v = 'ab<b>"', var v = 'ab<b>"',
esc_v = $('<div>').text(v).html(), esc_v = $('<div>').text(v).html(),
@ -344,7 +345,10 @@ $(function () {
asyncTest("'display' callback", function () { asyncTest("'display' callback", function () {
var newText = 'cd<e>;"', var newText = 'cd<e>;"',
e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(fx).editable({ e = $('<a href="#" data-pk="1" data-url="post.php" data-name="text1">abc</a>').appendTo(fx).editable({
display: function(value, response) { ajaxOptions: {
dataType: 'json'
},
display: function(value, response) {
ok(this === e[0], 'scope is ok'); ok(this === e[0], 'scope is ok');
ok(response.success, 'response param ok'); ok(response.success, 'response param ok');
$(this).text('qq'+value); $(this).text('qq'+value);
@ -461,12 +465,17 @@ $(function () {
e.click() e.click()
var p = tip(e); var p = tip(e);
var c = p.find('.editable-clear-x'); var c = p.find('.editable-clear-x');
ok(c.is(':visible'), 'clear shown'); ok(c.is(':visible'), 'clear shown');
//now clear shown with delay..
/*
p.find('input').val('').trigger('keyup'); p.find('input').val('').trigger('keyup');
ok(!c.is(':visible'), 'clear hidden for empty input'); ok(!c.is(':visible'), 'clear hidden for empty input');
p.find('input').val('cde').trigger('keyup'); p.find('input').val('cde').trigger('keyup');
ok(c.is(':visible'), 'clear shown on keyboard input'); ok(c.is(':visible'), 'clear shown on keyboard input');
*/
c.click(); c.click();
ok(!c.is(':visible'), 'clear hidden after click'); ok(!c.is(':visible'), 'clear hidden after click');
ok(!p.find('input').val(), 'input empty'); ok(!p.find('input').val(), 'input empty');

@ -33,11 +33,14 @@ $(function () {
ok($input.typeahead, 'typeahead applied to input'); ok($input.typeahead, 'typeahead applied to input');
$input.val(newText).keyup(); $input.val(newText).keyup();
ok(p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown visible'); ok(p.find('.typeahead.dropdown-menu').is(':visible'), 'dropdown visible');
//select `Admin` //select `Admin`
v = 5; v = 5;
p.find('.typeahead.dropdown-menu').find('.active').click();
ok(p.find('.typeahead.dropdown-menu').find('li').length, 'active item exists');
p.find('.typeahead.dropdown-menu').find('li').mouseover().click();
equal($input.val(), groups[v], 'input contain correct text'); equal($input.val(), groups[v], 'input contain correct text');
p.find('form').submit(); p.find('form').submit();
@ -91,7 +94,7 @@ $(function () {
//select `ab` //select `ab`
v = 'ab'; v = 'ab';
p.find('.typeahead.dropdown-menu').find('.active').click(); p.find('.typeahead.dropdown-menu').find('li').mouseover().click();
equal($input.val(), v, 'input contain correct text'); equal($input.val(), v, 'input contain correct text');
p.find('form').submit(); p.find('form').submit();

@ -10,7 +10,8 @@ $(function () {
asyncTest("should load correct value and save new entered value", function () { asyncTest("should load correct value and save new entered value", function () {
//skip test for ie7 as it is not supported by wysihtml5 //skip test for ie7 as it is not supported by wysihtml5
if($.browser.msie && parseInt($.browser.version, 10) <= 8) { var msieOld = /msie\s*(7|6)/i.test(navigator.userAgent);
if(msieOld) {
expect(0); expect(0);
start(); start();
return; return;