parent
67bf15454b
commit
68ac88ea40
@ -0,0 +1,860 @@
|
||||
/*!
|
||||
* bootstrap-select v1.4.2
|
||||
* http://silviomoreto.github.io/bootstrap-select/
|
||||
*
|
||||
* Copyright 2013 bootstrap-select
|
||||
* Licensed under the MIT license
|
||||
*/
|
||||
|
||||
!function($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.expr[':'].icontains = function(obj, index, meta) {
|
||||
return $(obj).text().toUpperCase().indexOf(meta[3].toUpperCase()) >= 0;
|
||||
};
|
||||
|
||||
var Selectpicker = function(element, options, e) {
|
||||
if (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
this.$element = $(element);
|
||||
this.$newElement = null;
|
||||
this.$button = null;
|
||||
this.$menu = null;
|
||||
|
||||
//Merge defaults, options and data-attributes to make our options
|
||||
this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options);
|
||||
|
||||
//If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute
|
||||
if (this.options.title === null) {
|
||||
this.options.title = this.$element.attr('title');
|
||||
}
|
||||
|
||||
//Expose public methods
|
||||
this.val = Selectpicker.prototype.val;
|
||||
this.render = Selectpicker.prototype.render;
|
||||
this.refresh = Selectpicker.prototype.refresh;
|
||||
this.setStyle = Selectpicker.prototype.setStyle;
|
||||
this.selectAll = Selectpicker.prototype.selectAll;
|
||||
this.deselectAll = Selectpicker.prototype.deselectAll;
|
||||
this.init();
|
||||
};
|
||||
|
||||
Selectpicker.prototype = {
|
||||
|
||||
constructor: Selectpicker,
|
||||
|
||||
init: function() {
|
||||
this.$element.hide();
|
||||
this.multiple = this.$element.prop('multiple');
|
||||
var id = this.$element.attr('id');
|
||||
this.$newElement = this.createView();
|
||||
this.$element.after(this.$newElement);
|
||||
this.$menu = this.$newElement.find('> .dropdown-menu');
|
||||
this.$button = this.$newElement.find('> button');
|
||||
this.$searchbox = this.$newElement.find('input');
|
||||
|
||||
if (id !== undefined) {
|
||||
var that = this;
|
||||
this.$button.attr('data-id', id);
|
||||
$('label[for="' + id + '"]').click(function(e) {
|
||||
e.preventDefault();
|
||||
that.$button.focus();
|
||||
});
|
||||
}
|
||||
|
||||
this.checkDisabled();
|
||||
this.clickListener();
|
||||
if (this.options.liveSearch) {
|
||||
this.liveSearchListener();
|
||||
}
|
||||
this.render();
|
||||
this.liHeight();
|
||||
this.setStyle();
|
||||
this.setWidth();
|
||||
if (this.options.container) {
|
||||
this.selectPosition();
|
||||
}
|
||||
this.$menu.data('this', this);
|
||||
this.$newElement.data('this', this);
|
||||
},
|
||||
|
||||
createDropdown: function() {
|
||||
//If we are multiple, then add the show-tick class by default
|
||||
var multiple = this.multiple ? ' show-tick' : '';
|
||||
var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>' + this.options.header + '</div>' : '';
|
||||
var searchbox = this.options.liveSearch ? '<div class="bootstrap-select-searchbox"><input type="text" class="input-block-level form-control" /></div>' : '';
|
||||
var drop =
|
||||
'<div class="btn-group bootstrap-select' + multiple + '">' +
|
||||
'<button type="button" class="btn dropdown-toggle selectpicker" data-toggle="dropdown">' +
|
||||
'<span class="filter-option pull-left"></span> ' +
|
||||
'<span class="caret"></span>' +
|
||||
'</button>' +
|
||||
'<div class="dropdown-menu open">' +
|
||||
header +
|
||||
searchbox +
|
||||
'<ul class="dropdown-menu inner selectpicker" role="menu">' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
return $(drop);
|
||||
},
|
||||
|
||||
createView: function() {
|
||||
var $drop = this.createDropdown();
|
||||
var $li = this.createLi();
|
||||
$drop.find('ul').append($li);
|
||||
return $drop;
|
||||
},
|
||||
|
||||
reloadLi: function() {
|
||||
//Remove all children.
|
||||
this.destroyLi();
|
||||
//Re build
|
||||
var $li = this.createLi();
|
||||
this.$menu.find('ul').append( $li );
|
||||
},
|
||||
|
||||
destroyLi: function() {
|
||||
this.$menu.find('li').remove();
|
||||
},
|
||||
|
||||
createLi: function() {
|
||||
var that = this,
|
||||
_liA = [],
|
||||
_liHtml = '';
|
||||
|
||||
this.$element.find('option').each(function() {
|
||||
var $this = $(this);
|
||||
|
||||
//Get the class and text for the option
|
||||
var optionClass = $this.attr('class') || '';
|
||||
var inline = $this.attr('style') || '';
|
||||
var text = $this.data('content') ? $this.data('content') : $this.html();
|
||||
var subtext = $this.data('subtext') !== undefined ? '<small class="muted text-muted">' + $this.data('subtext') + '</small>' : '';
|
||||
var icon = $this.data('icon') !== undefined ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : '';
|
||||
if (icon !== '' && ($this.is(':disabled') || $this.parent().is(':disabled'))) {
|
||||
icon = '<span>'+icon+'</span>';
|
||||
}
|
||||
|
||||
if (!$this.data('content')) {
|
||||
//Prepend any icon and append any subtext to the main text.
|
||||
text = icon + '<span class="text">' + text + subtext + '</span>';
|
||||
}
|
||||
|
||||
if (that.options.hideDisabled && ($this.is(':disabled') || $this.parent().is(':disabled'))) {
|
||||
_liA.push('<a style="min-height: 0; padding: 0"></a>');
|
||||
} else if ($this.parent().is('optgroup') && $this.data('divider') !== true) {
|
||||
if ($this.index() === 0) {
|
||||
//Get the opt group label
|
||||
var label = $this.parent().attr('label');
|
||||
var labelSubtext = $this.parent().data('subtext') !== undefined ? '<small class="muted text-muted">'+$this.parent().data('subtext')+'</small>' : '';
|
||||
var labelIcon = $this.parent().data('icon') ? '<i class="'+$this.parent().data('icon')+'"></i> ' : '';
|
||||
label = labelIcon + '<span class="text">' + label + labelSubtext + '</span>';
|
||||
|
||||
if ($this[0].index !== 0) {
|
||||
_liA.push(
|
||||
'<div class="div-contain"><div class="divider"></div></div>'+
|
||||
'<dt>'+label+'</dt>'+
|
||||
that.createA(text, 'opt ' + optionClass, inline )
|
||||
);
|
||||
} else {
|
||||
_liA.push(
|
||||
'<dt>'+label+'</dt>'+
|
||||
that.createA(text, 'opt ' + optionClass, inline ));
|
||||
}
|
||||
} else {
|
||||
_liA.push(that.createA(text, 'opt ' + optionClass, inline ));
|
||||
}
|
||||
} else if ($this.data('divider') === true) {
|
||||
_liA.push('<div class="div-contain"><div class="divider"></div></div>');
|
||||
} else if ($(this).data('hidden') === true) {
|
||||
_liA.push('');
|
||||
} else {
|
||||
_liA.push(that.createA(text, optionClass, inline ));
|
||||
}
|
||||
});
|
||||
|
||||
$.each(_liA, function(i, item) {
|
||||
_liHtml += '<li rel=' + i + '>' + item + '</li>';
|
||||
});
|
||||
|
||||
//If we are not multiple, and we dont have a selected item, and we dont have a title, select the first element so something is set in the button
|
||||
if (!this.multiple && this.$element.find('option:selected').length===0 && !this.options.title) {
|
||||
this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected');
|
||||
}
|
||||
|
||||
return $(_liHtml);
|
||||
},
|
||||
|
||||
createA: function(text, classes, inline) {
|
||||
return '<a tabindex="0" class="'+classes+'" style="'+inline+'">' +
|
||||
text +
|
||||
'<i class="' + this.options.iconBase + ' ' + this.options.tickIcon + ' icon-ok check-mark"></i>' +
|
||||
'</a>';
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var that = this;
|
||||
|
||||
//Update the LI to match the SELECT
|
||||
this.$element.find('option').each(function(index) {
|
||||
that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled') );
|
||||
that.setSelected(index, $(this).is(':selected') );
|
||||
});
|
||||
|
||||
this.tabIndex();
|
||||
|
||||
var selectedItems = this.$element.find('option:selected').map(function() {
|
||||
var $this = $(this);
|
||||
var icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : '';
|
||||
var subtext;
|
||||
if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) {
|
||||
subtext = ' <small class="muted text-muted">'+$this.data('subtext') +'</small>';
|
||||
} else {
|
||||
subtext = '';
|
||||
}
|
||||
if ($this.data('content') && that.options.showContent) {
|
||||
return $this.data('content');
|
||||
} else if ($this.attr('title') !== undefined) {
|
||||
return $this.attr('title');
|
||||
} else {
|
||||
return icon + $this.html() + subtext;
|
||||
}
|
||||
}).toArray();
|
||||
|
||||
//Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled
|
||||
//Convert all the values into a comma delimited string
|
||||
var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator);
|
||||
|
||||
//If this is multi select, and the selectText type is count, the show 1 of 2 selected etc..
|
||||
if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) {
|
||||
var max = this.options.selectedTextFormat.split('>');
|
||||
var notDisabled = this.options.hideDisabled ? ':not([disabled])' : '';
|
||||
if ( (max.length>1 && selectedItems.length > max[1]) || (max.length==1 && selectedItems.length>=2)) {
|
||||
title = this.options.countSelectedText.replace('{0}', selectedItems.length).replace('{1}', this.$element.find('option:not([data-divider="true"]):not([data-hidden="true"])'+notDisabled).length);
|
||||
}
|
||||
}
|
||||
|
||||
//If we dont have a title, then use the default, or if nothing is set at all, use the not selected text
|
||||
if (!title) {
|
||||
title = this.options.title !== undefined ? this.options.title : this.options.noneSelectedText;
|
||||
}
|
||||
|
||||
this.$button.attr('title', $.trim(title));
|
||||
this.$newElement.find('.filter-option').html(title);
|
||||
},
|
||||
|
||||
setStyle: function(style, status) {
|
||||
if (this.$element.attr('class')) {
|
||||
this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device/gi, ''));
|
||||
}
|
||||
|
||||
var buttonClass = style ? style : this.options.style;
|
||||
|
||||
if (status == 'add') {
|
||||
this.$button.addClass(buttonClass);
|
||||
} else if (status == 'remove') {
|
||||
this.$button.removeClass(buttonClass);
|
||||
} else {
|
||||
this.$button.removeClass(this.options.style);
|
||||
this.$button.addClass(buttonClass);
|
||||
}
|
||||
},
|
||||
|
||||
liHeight: function() {
|
||||
var $selectClone = this.$menu.parent().clone().appendTo('body'),
|
||||
$menuClone = $selectClone.addClass('open').find('> .dropdown-menu'),
|
||||
liHeight = $menuClone.find('li > a').outerHeight(),
|
||||
headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0,
|
||||
searchHeight = this.options.liveSearch ? $menuClone.find('.bootstrap-select-searchbox').outerHeight() : 0;
|
||||
|
||||
$selectClone.remove();
|
||||
|
||||
this.$newElement
|
||||
.data('liHeight', liHeight)
|
||||
.data('headerHeight', headerHeight)
|
||||
.data('searchHeight', searchHeight);
|
||||
},
|
||||
|
||||
setSize: function() {
|
||||
var that = this,
|
||||
menu = this.$menu,
|
||||
menuInner = menu.find('.inner'),
|
||||
selectHeight = this.$newElement.outerHeight(),
|
||||
liHeight = this.$newElement.data('liHeight'),
|
||||
headerHeight = this.$newElement.data('headerHeight'),
|
||||
searchHeight = this.$newElement.data('searchHeight'),
|
||||
divHeight = menu.find('li .divider').outerHeight(true),
|
||||
menuPadding = parseInt(menu.css('padding-top')) +
|
||||
parseInt(menu.css('padding-bottom')) +
|
||||
parseInt(menu.css('border-top-width')) +
|
||||
parseInt(menu.css('border-bottom-width')),
|
||||
notDisabled = this.options.hideDisabled ? ':not(.disabled)' : '',
|
||||
$window = $(window),
|
||||
menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2,
|
||||
menuHeight,
|
||||
selectOffsetTop,
|
||||
selectOffsetBot,
|
||||
posVert = function() {
|
||||
selectOffsetTop = that.$newElement.offset().top - $window.scrollTop();
|
||||
selectOffsetBot = $window.height() - selectOffsetTop - selectHeight;
|
||||
};
|
||||
posVert();
|
||||
if (this.options.header) menu.css('padding-top', 0);
|
||||
|
||||
if (this.options.size == 'auto') {
|
||||
var getSize = function() {
|
||||
var minHeight;
|
||||
posVert();
|
||||
menuHeight = selectOffsetBot - menuExtras;
|
||||
if (that.options.dropupAuto) {
|
||||
that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height()));
|
||||
}
|
||||
if (that.$newElement.hasClass('dropup')) {
|
||||
menuHeight = selectOffsetTop - menuExtras;
|
||||
}
|
||||
if ((menu.find('li').length + menu.find('dt').length) > 3) {
|
||||
minHeight = liHeight*3 + menuExtras - 2;
|
||||
} else {
|
||||
minHeight = 0;
|
||||
}
|
||||
menu.css({'max-height' : menuHeight + 'px', 'overflow' : 'hidden', 'min-height' : minHeight + 'px'});
|
||||
menuInner.css({'max-height' : menuHeight - headerHeight - searchHeight- menuPadding + 'px', 'overflow-y' : 'auto', 'min-height' : minHeight - menuPadding + 'px'});
|
||||
};
|
||||
getSize();
|
||||
$(window).resize(getSize);
|
||||
$(window).scroll(getSize);
|
||||
} else if (this.options.size && this.options.size != 'auto' && menu.find('li'+notDisabled).length > this.options.size) {
|
||||
var optIndex = menu.find('li'+notDisabled+' > *').filter(':not(.div-contain)').slice(0,this.options.size).last().parent().index();
|
||||
var divLength = menu.find('li').slice(0,optIndex + 1).find('.div-contain').length;
|
||||
menuHeight = liHeight*this.options.size + divLength*divHeight + menuPadding;
|
||||
if (that.options.dropupAuto) {
|
||||
this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height()));
|
||||
}
|
||||
menu.css({'max-height' : menuHeight + headerHeight + searchHeight + 'px', 'overflow' : 'hidden'});
|
||||
menuInner.css({'max-height' : menuHeight - menuPadding + 'px', 'overflow-y' : 'auto'});
|
||||
}
|
||||
},
|
||||
|
||||
setWidth: function() {
|
||||
if (this.options.width == 'auto') {
|
||||
this.$menu.css('min-width', '0');
|
||||
|
||||
// Get correct width if element hidden
|
||||
var selectClone = this.$newElement.clone().appendTo('body');
|
||||
var ulWidth = selectClone.find('> .dropdown-menu').css('width');
|
||||
selectClone.remove();
|
||||
|
||||
this.$newElement.css('width', ulWidth);
|
||||
} else if (this.options.width == 'fit') {
|
||||
// Remove inline min-width so width can be changed from 'auto'
|
||||
this.$menu.css('min-width', '');
|
||||
this.$newElement.css('width', '').addClass('fit-width');
|
||||
} else if (this.options.width) {
|
||||
// Remove inline min-width so width can be changed from 'auto'
|
||||
this.$menu.css('min-width', '');
|
||||
this.$newElement.css('width', this.options.width);
|
||||
} else {
|
||||
// Remove inline min-width/width so width can be changed
|
||||
this.$menu.css('min-width', '');
|
||||
this.$newElement.css('width', '');
|
||||
}
|
||||
// Remove fit-width class if width is changed programmatically
|
||||
if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') {
|
||||
this.$newElement.removeClass('fit-width');
|
||||
}
|
||||
},
|
||||
|
||||
selectPosition: function() {
|
||||
var that = this,
|
||||
drop = '<div />',
|
||||
$drop = $(drop),
|
||||
pos,
|
||||
actualHeight,
|
||||
getPlacement = function($element) {
|
||||
$drop.addClass($element.attr('class')).toggleClass('dropup', $element.hasClass('dropup'));
|
||||
pos = $element.offset();
|
||||
actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight;
|
||||
$drop.css({'top' : pos.top + actualHeight, 'left' : pos.left, 'width' : $element[0].offsetWidth, 'position' : 'absolute'});
|
||||
};
|
||||
this.$newElement.on('click', function() {
|
||||
getPlacement($(this));
|
||||
$drop.appendTo(that.options.container);
|
||||
$drop.toggleClass('open', !$(this).hasClass('open'));
|
||||
$drop.append(that.$menu);
|
||||
});
|
||||
$(window).resize(function() {
|
||||
getPlacement(that.$newElement);
|
||||
});
|
||||
$(window).on('scroll', function() {
|
||||
getPlacement(that.$newElement);
|
||||
});
|
||||
$('html').on('click', function(e) {
|
||||
if ($(e.target).closest(that.$newElement).length < 1) {
|
||||
$drop.removeClass('open');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
mobile: function() {
|
||||
this.$element.addClass('mobile-device').appendTo(this.$newElement);
|
||||
if (this.options.container) this.$menu.hide();
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.reloadLi();
|
||||
this.render();
|
||||
this.setWidth();
|
||||
this.setStyle();
|
||||
this.checkDisabled();
|
||||
this.liHeight();
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.reloadLi();
|
||||
this.setWidth();
|
||||
this.setStyle();
|
||||
this.checkDisabled();
|
||||
this.liHeight();
|
||||
},
|
||||
|
||||
setSelected: function(index, selected) {
|
||||
this.$menu.find('li').eq(index).toggleClass('selected', selected);
|
||||
},
|
||||
|
||||
setDisabled: function(index, disabled) {
|
||||
if (disabled) {
|
||||
this.$menu.find('li').eq(index).addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1);
|
||||
} else {
|
||||
this.$menu.find('li').eq(index).removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0);
|
||||
}
|
||||
},
|
||||
|
||||
isDisabled: function() {
|
||||
return this.$element.is(':disabled');
|
||||
},
|
||||
|
||||
checkDisabled: function() {
|
||||
var that = this;
|
||||
|
||||
if (this.isDisabled()) {
|
||||
this.$button.addClass('disabled').attr('tabindex', -1);
|
||||
} else {
|
||||
if (this.$button.hasClass('disabled')) {
|
||||
this.$button.removeClass('disabled');
|
||||
}
|
||||
|
||||
if (this.$button.attr('tabindex') == -1) {
|
||||
if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex');
|
||||
}
|
||||
}
|
||||
|
||||
this.$button.click(function() {
|
||||
return !that.isDisabled();
|
||||
});
|
||||
},
|
||||
|
||||
tabIndex: function() {
|
||||
if (this.$element.is('[tabindex]')) {
|
||||
this.$element.data('tabindex', this.$element.attr('tabindex'));
|
||||
this.$button.attr('tabindex', this.$element.data('tabindex'));
|
||||
}
|
||||
},
|
||||
|
||||
clickListener: function() {
|
||||
var that = this;
|
||||
|
||||
$('body').on('touchstart.dropdown', '.dropdown-menu', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
this.$newElement.on('click', function() {
|
||||
that.setSize();
|
||||
if (!that.options.liveSearch && !that.multiple) {
|
||||
setTimeout(function() {
|
||||
that.$menu.find('.selected a').focus();
|
||||
}, 10);
|
||||
}
|
||||
});
|
||||
|
||||
this.$menu.on('click', 'li a', function(e) {
|
||||
var clickedIndex = $(this).parent().index(),
|
||||
prevValue = that.$element.val(),
|
||||
prevIndex = that.$element.prop('selectedIndex');
|
||||
|
||||
//Dont close on multi choice menu
|
||||
if (that.multiple) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
//Dont run if we have been disabled
|
||||
if (!that.isDisabled() && !$(this).parent().hasClass('disabled')) {
|
||||
var $options = that.$element.find('option');
|
||||
var $option = $options.eq(clickedIndex);
|
||||
|
||||
//Deselect all others if not multi select box
|
||||
if (!that.multiple) {
|
||||
$options.prop('selected', false);
|
||||
$option.prop('selected', true);
|
||||
}
|
||||
//Else toggle the one we have chosen if we are multi select.
|
||||
else {
|
||||
var state = $option.prop('selected');
|
||||
|
||||
$option.prop('selected', !state);
|
||||
}
|
||||
|
||||
if (!that.multiple) {
|
||||
that.$button.focus();
|
||||
} else if (that.options.liveSearch) {
|
||||
that.$searchbox.focus();
|
||||
}
|
||||
|
||||
// Trigger select 'change'
|
||||
if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) {
|
||||
that.$element.change();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$menu.on('click', 'li.disabled a, li dt, li .div-contain, .popover-title, .popover-title :not(.close)', function(e) {
|
||||
if (e.target == this) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!that.options.liveSearch) {
|
||||
that.$button.focus();
|
||||
} else {
|
||||
that.$searchbox.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$menu.on('click', '.popover-title .close', function() {
|
||||
that.$button.focus();
|
||||
});
|
||||
|
||||
this.$searchbox.on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
this.$element.change(function() {
|
||||
that.render();
|
||||
});
|
||||
},
|
||||
|
||||
liveSearchListener: function() {
|
||||
var that = this,
|
||||
no_results = $('<li class="no-results"></li>');
|
||||
|
||||
this.$newElement.on('click.dropdown.data-api', function() {
|
||||
that.$menu.find('.active').removeClass('active');
|
||||
if (!!that.$searchbox.val()) {
|
||||
that.$searchbox.val('');
|
||||
that.$menu.find('li').show();
|
||||
if (!!no_results.parent().length) no_results.remove();
|
||||
}
|
||||
if (!that.multiple) that.$menu.find('.selected').addClass('active');
|
||||
setTimeout(function() {
|
||||
that.$searchbox.focus();
|
||||
}, 10);
|
||||
});
|
||||
|
||||
this.$searchbox.on('input propertychange', function() {
|
||||
if (that.$searchbox.val()) {
|
||||
that.$menu.find('li').show().not(':icontains(' + that.$searchbox.val() + ')').hide();
|
||||
|
||||
if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) {
|
||||
if (!!no_results.parent().length) no_results.remove();
|
||||
no_results.html('No results match "'+ that.$searchbox.val() + '"').show();
|
||||
that.$menu.find('li').last().after(no_results);
|
||||
} else if (!!no_results.parent().length) {
|
||||
no_results.remove();
|
||||
}
|
||||
|
||||
} else {
|
||||
that.$menu.find('li').show();
|
||||
if (!!no_results.parent().length) no_results.remove();
|
||||
}
|
||||
|
||||
that.$menu.find('li.active').removeClass('active');
|
||||
that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus();
|
||||
$(this).focus();
|
||||
});
|
||||
|
||||
this.$menu.on('mouseenter', 'a', function(e) {
|
||||
that.$menu.find('.active').removeClass('active');
|
||||
$(e.currentTarget).parent().not('.disabled').addClass('active');
|
||||
});
|
||||
|
||||
this.$menu.on('mouseleave', 'a', function() {
|
||||
that.$menu.find('.active').removeClass('active');
|
||||
});
|
||||
},
|
||||
|
||||
val: function(value) {
|
||||
|
||||
if (value !== undefined) {
|
||||
this.$element.val( value );
|
||||
|
||||
this.$element.change();
|
||||
return this.$element;
|
||||
} else {
|
||||
return this.$element.val();
|
||||
}
|
||||
},
|
||||
|
||||
selectAll: function() {
|
||||
this.$element.find('option').prop('selected', true).attr('selected', 'selected');
|
||||
this.render();
|
||||
},
|
||||
|
||||
deselectAll: function() {
|
||||
this.$element.find('option').prop('selected', false).removeAttr('selected');
|
||||
this.render();
|
||||
},
|
||||
|
||||
keydown: function(e) {
|
||||
var $this,
|
||||
$items,
|
||||
$parent,
|
||||
index,
|
||||
next,
|
||||
first,
|
||||
last,
|
||||
prev,
|
||||
nextPrev,
|
||||
that,
|
||||
prevIndex,
|
||||
isActive,
|
||||
keyCodeMap = {
|
||||
32:' ', 48:'0', 49:'1', 50:'2', 51:'3', 52:'4', 53:'5', 54:'6', 55:'7', 56:'8', 57:'9', 59:';',
|
||||
65:'a', 66:'b', 67:'c', 68:'d', 69:'e', 70:'f', 71:'g', 72:'h', 73:'i', 74:'j', 75:'k', 76:'l',
|
||||
77:'m', 78:'n', 79:'o', 80:'p', 81:'q', 82:'r', 83:'s', 84:'t', 85:'u', 86:'v', 87:'w', 88:'x',
|
||||
89:'y', 90:'z', 96:'0', 97:'1', 98:'2', 99:'3', 100:'4', 101:'5', 102:'6', 103:'7', 104:'8', 105:'9'
|
||||
};
|
||||
|
||||
$this = $(this);
|
||||
|
||||
$parent = $this.parent();
|
||||
|
||||
if ($this.is('input')) $parent = $this.parent().parent();
|
||||
|
||||
that = $parent.data('this');
|
||||
|
||||
if (that.options.liveSearch) $parent = $this.parent().parent();
|
||||
|
||||
if (that.options.container) $parent = that.$menu;
|
||||
|
||||
$items = $('[role=menu] li:not(.divider) a', $parent);
|
||||
|
||||
isActive = that.$menu.parent().hasClass('open');
|
||||
|
||||
if (that.options.liveSearch) {
|
||||
if (/(^9$|27)/.test(e.keyCode) && isActive && that.$menu.find('.active').length === 0) {
|
||||
e.preventDefault();
|
||||
that.$menu.parent().removeClass('open');
|
||||
that.$button.focus();
|
||||
}
|
||||
$items = $('[role=menu] li:not(.divider):visible', $parent);
|
||||
if (!$this.val() && !/(38|40)/.test(e.keyCode)) {
|
||||
if ($items.filter('.active').length === 0) {
|
||||
$items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$items.length) return;
|
||||
|
||||
if (/(38|40)/.test(e.keyCode)) {
|
||||
|
||||
if (!isActive) {
|
||||
that.$menu.parent().addClass('open');
|
||||
}
|
||||
|
||||
index = $items.index($items.filter(':focus'));
|
||||
first = $items.parent(':not(.disabled):visible').first().index();
|
||||
last = $items.parent(':not(.disabled):visible').last().index();
|
||||
next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index();
|
||||
prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index();
|
||||
nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index();
|
||||
|
||||
if (that.options.liveSearch) {
|
||||
$items.each(function(i) {
|
||||
if ($(this).is(':not(.disabled)')) {
|
||||
$(this).data('index', i);
|
||||
}
|
||||
});
|
||||
index = $items.index($items.filter('.active'));
|
||||
first = $items.filter(':not(.disabled):visible').first().data('index');
|
||||
last = $items.filter(':not(.disabled):visible').last().data('index');
|
||||
next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index');
|
||||
prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index');
|
||||
nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index');
|
||||
}
|
||||
|
||||
prevIndex = $this.data('prevIndex');
|
||||
|
||||
if (e.keyCode == 38) {
|
||||
if (that.options.liveSearch) index -= 1;
|
||||
if (index != nextPrev && index > prev) index = prev;
|
||||
if (index < first) index = first;
|
||||
if (index == prevIndex) index = last;
|
||||
}
|
||||
|
||||
if (e.keyCode == 40) {
|
||||
if (that.options.liveSearch) index += 1;
|
||||
if (index == -1) index = 0;
|
||||
if (index != nextPrev && index < next) index = next;
|
||||
if (index > last) index = last;
|
||||
if (index == prevIndex) index = first;
|
||||
}
|
||||
|
||||
$this.data('prevIndex', index);
|
||||
|
||||
if (!that.options.liveSearch) {
|
||||
$items.eq(index).focus();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
if (!$this.is('.dropdown-toggle')) {
|
||||
$items.removeClass('active');
|
||||
$items.eq(index).addClass('active').find('a').focus();
|
||||
$this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!$this.is('input')) {
|
||||
|
||||
var keyIndex = [],
|
||||
count,
|
||||
prevKey;
|
||||
|
||||
$items.each(function() {
|
||||
if ($(this).parent().is(':not(.disabled)')) {
|
||||
if ($.trim($(this).text().toLowerCase()).substring(0,1) == keyCodeMap[e.keyCode]) {
|
||||
keyIndex.push($(this).parent().index());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
count = $(document).data('keycount');
|
||||
count++;
|
||||
$(document).data('keycount',count);
|
||||
|
||||
prevKey = $.trim($(':focus').text().toLowerCase()).substring(0,1);
|
||||
|
||||
if (prevKey != keyCodeMap[e.keyCode]) {
|
||||
count = 1;
|
||||
$(document).data('keycount', count);
|
||||
} else if (count >= keyIndex.length) {
|
||||
$(document).data('keycount', 0);
|
||||
if (count > keyIndex.length) count = 1;
|
||||
}
|
||||
|
||||
$items.eq(keyIndex[count - 1]).focus();
|
||||
}
|
||||
|
||||
// Select focused option if "Enter", "Spacebar", "Tab" are pressed inside the menu.
|
||||
if (/(13|32|^9$)/.test(e.keyCode) && isActive) {
|
||||
if (!/(32)/.test(e.keyCode)) e.preventDefault();
|
||||
if (!that.options.liveSearch) {
|
||||
$(':focus').click();
|
||||
} else if (!/(32)/.test(e.keyCode)) {
|
||||
that.$menu.find('.active a').click();
|
||||
$this.focus();
|
||||
}
|
||||
$(document).data('keycount',0);
|
||||
}
|
||||
|
||||
if ((/(^9$|27)/.test(e.keyCode) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode) && !isActive)) {
|
||||
that.$menu.parent().removeClass('open');
|
||||
that.$button.focus();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.$newElement.hide();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.$newElement.show();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.$newElement.remove();
|
||||
this.$element.remove();
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.selectpicker = function(option, event) {
|
||||
//get the args of the outer function..
|
||||
var args = arguments;
|
||||
var value;
|
||||
var chain = this.each(function() {
|
||||
if ($(this).is('select')) {
|
||||
var $this = $(this),
|
||||
data = $this.data('selectpicker'),
|
||||
options = typeof option == 'object' && option;
|
||||
|
||||
if (!data) {
|
||||
$this.data('selectpicker', (data = new Selectpicker(this, options, event)));
|
||||
} else if (options) {
|
||||
for(var i in options) {
|
||||
data.options[i] = options[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof option == 'string') {
|
||||
//Copy the value of option, as once we shift the arguments
|
||||
//it also shifts the value of option.
|
||||
var property = option;
|
||||
if (data[property] instanceof Function) {
|
||||
[].shift.apply(args);
|
||||
value = data[property].apply(data, args);
|
||||
} else {
|
||||
value = data.options[property];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (value !== undefined) {
|
||||
return value;
|
||||
} else {
|
||||
return chain;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.selectpicker.defaults = {
|
||||
style: 'btn-default',
|
||||
size: 'auto',
|
||||
title: null,
|
||||
selectedTextFormat : 'values',
|
||||
noneSelectedText : 'Nothing selected',
|
||||
countSelectedText: '{0} of {1} selected',
|
||||
width: false,
|
||||
container: false,
|
||||
hideDisabled: false,
|
||||
showSubtext: false,
|
||||
showIcon: true,
|
||||
showContent: true,
|
||||
dropupAuto: true,
|
||||
header: false,
|
||||
liveSearch: false,
|
||||
multipleSeparator: ', ',
|
||||
iconBase: 'glyphicon',
|
||||
tickIcon: 'glyphicon-ok'
|
||||
};
|
||||
|
||||
$(document)
|
||||
.data('keycount', 0)
|
||||
.on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', Selectpicker.prototype.keydown)
|
||||
.on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bootstrap-select-searchbox input', function (e) { e.stopPropagation(); });
|
||||
|
||||
}(window.jQuery);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,464 @@
|
||||
/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
|
||||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||||
*
|
||||
* Version: 1.3.0
|
||||
*
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
jQuery.fn.extend({
|
||||
slimScroll: function(options) {
|
||||
|
||||
var defaults = {
|
||||
|
||||
// width in pixels of the visible scroll area
|
||||
width : 'auto',
|
||||
|
||||
// height in pixels of the visible scroll area
|
||||
height : '250px',
|
||||
|
||||
// width in pixels of the scrollbar and rail
|
||||
size : '7px',
|
||||
|
||||
// scrollbar color, accepts any hex/color value
|
||||
color: '#000',
|
||||
|
||||
// scrollbar position - left/right
|
||||
position : 'right',
|
||||
|
||||
// distance in pixels between the side edge and the scrollbar
|
||||
distance : '1px',
|
||||
|
||||
// default scroll position on load - top / bottom / $('selector')
|
||||
start : 'top',
|
||||
|
||||
// sets scrollbar opacity
|
||||
opacity : .4,
|
||||
|
||||
// enables always-on mode for the scrollbar
|
||||
alwaysVisible : false,
|
||||
|
||||
// check if we should hide the scrollbar when user is hovering over
|
||||
disableFadeOut : false,
|
||||
|
||||
// sets visibility of the rail
|
||||
railVisible : false,
|
||||
|
||||
// sets rail color
|
||||
railColor : '#333',
|
||||
|
||||
// sets rail opacity
|
||||
railOpacity : .2,
|
||||
|
||||
// whether we should use jQuery UI Draggable to enable bar dragging
|
||||
railDraggable : true,
|
||||
|
||||
// defautlt CSS class of the slimscroll rail
|
||||
railClass : 'slimScrollRail',
|
||||
|
||||
// defautlt CSS class of the slimscroll bar
|
||||
barClass : 'slimScrollBar',
|
||||
|
||||
// defautlt CSS class of the slimscroll wrapper
|
||||
wrapperClass : 'slimScrollDiv',
|
||||
|
||||
// check if mousewheel should scroll the window if we reach top/bottom
|
||||
allowPageScroll : false,
|
||||
|
||||
// scroll amount applied to each mouse wheel step
|
||||
wheelStep : 20,
|
||||
|
||||
// scroll amount applied when user is using gestures
|
||||
touchScrollStep : 200,
|
||||
|
||||
// sets border radius
|
||||
borderRadius: '7px',
|
||||
|
||||
// sets border radius of the rail
|
||||
railBorderRadius : '7px'
|
||||
};
|
||||
|
||||
var o = $.extend(defaults, options);
|
||||
|
||||
// do it for every element that matches selector
|
||||
this.each(function(){
|
||||
|
||||
var isOverPanel, isOverBar, isDragg, queueHide, touchDif,
|
||||
barHeight, percentScroll, lastScroll,
|
||||
divS = '<div></div>',
|
||||
minBarHeight = 30,
|
||||
releaseScroll = false;
|
||||
|
||||
// used in event handlers and for better minification
|
||||
var me = $(this);
|
||||
|
||||
// ensure we are not binding it again
|
||||
if (me.parent().hasClass(o.wrapperClass))
|
||||
{
|
||||
// start from last bar position
|
||||
var offset = me.scrollTop();
|
||||
|
||||
// find bar and rail
|
||||
bar = me.parent().find('.' + o.barClass);
|
||||
rail = me.parent().find('.' + o.railClass);
|
||||
|
||||
getBarHeight();
|
||||
|
||||
// check if we should scroll existing instance
|
||||
if ($.isPlainObject(options))
|
||||
{
|
||||
// Pass height: auto to an existing slimscroll object to force a resize after contents have changed
|
||||
if ( 'height' in options && options.height == 'auto' ) {
|
||||
me.parent().css('height', 'auto');
|
||||
me.css('height', 'auto');
|
||||
var height = me.parent().parent().height();
|
||||
me.parent().css('height', height);
|
||||
me.css('height', height);
|
||||
}
|
||||
|
||||
if ('scrollTo' in options)
|
||||
{
|
||||
// jump to a static point
|
||||
offset = parseInt(o.scrollTo);
|
||||
}
|
||||
else if ('scrollBy' in options)
|
||||
{
|
||||
// jump by value pixels
|
||||
offset += parseInt(o.scrollBy);
|
||||
}
|
||||
else if ('destroy' in options)
|
||||
{
|
||||
// remove slimscroll elements
|
||||
bar.remove();
|
||||
rail.remove();
|
||||
me.unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
// scroll content by the given offset
|
||||
scrollContent(offset, false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// optionally set height to the parent's height
|
||||
o.height = (o.height == 'auto') ? me.parent().height() : o.height;
|
||||
|
||||
// wrap content
|
||||
var wrapper = $(divS)
|
||||
.addClass(o.wrapperClass)
|
||||
.css({
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
width: o.width,
|
||||
height: o.height
|
||||
});
|
||||
|
||||
// update style for the div
|
||||
me.css({
|
||||
overflow: 'hidden',
|
||||
width: o.width,
|
||||
height: o.height
|
||||
});
|
||||
|
||||
// create scrollbar rail
|
||||
var rail = $(divS)
|
||||
.addClass(o.railClass)
|
||||
.css({
|
||||
width: o.size,
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
|
||||
'border-radius': o.railBorderRadius,
|
||||
background: o.railColor,
|
||||
opacity: o.railOpacity,
|
||||
zIndex: 90
|
||||
});
|
||||
|
||||
// create scrollbar
|
||||
var bar = $(divS)
|
||||
.addClass(o.barClass)
|
||||
.css({
|
||||
background: o.color,
|
||||
width: o.size,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
opacity: o.opacity,
|
||||
display: o.alwaysVisible ? 'block' : 'none',
|
||||
'border-radius' : o.borderRadius,
|
||||
BorderRadius: o.borderRadius,
|
||||
MozBorderRadius: o.borderRadius,
|
||||
WebkitBorderRadius: o.borderRadius,
|
||||
zIndex: 99
|
||||
});
|
||||
|
||||
// set position
|
||||
var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance };
|
||||
rail.css(posCss);
|
||||
bar.css(posCss);
|
||||
|
||||
// wrap it
|
||||
me.wrap(wrapper);
|
||||
|
||||
// append to parent div
|
||||
me.parent().append(bar);
|
||||
me.parent().append(rail);
|
||||
|
||||
// make it draggable and no longer dependent on the jqueryUI
|
||||
if (o.railDraggable){
|
||||
bar.bind("mousedown", function(e) {
|
||||
var $doc = $(document);
|
||||
isDragg = true;
|
||||
t = parseFloat(bar.css('top'));
|
||||
pageY = e.pageY;
|
||||
|
||||
$doc.bind("mousemove.slimscroll", function(e){
|
||||
currTop = t + e.pageY - pageY;
|
||||
bar.css('top', currTop);
|
||||
scrollContent(0, bar.position().top, false);// scroll content
|
||||
});
|
||||
|
||||
$doc.bind("mouseup.slimscroll", function(e) {
|
||||
isDragg = false;hideBar();
|
||||
$doc.unbind('.slimscroll');
|
||||
});
|
||||
return false;
|
||||
}).bind("selectstart.slimscroll", function(e){
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// on rail over
|
||||
rail.hover(function(){
|
||||
showBar();
|
||||
}, function(){
|
||||
hideBar();
|
||||
});
|
||||
|
||||
// on bar over
|
||||
bar.hover(function(){
|
||||
isOverBar = true;
|
||||
}, function(){
|
||||
isOverBar = false;
|
||||
});
|
||||
|
||||
// show on parent mouseover
|
||||
me.hover(function(){
|
||||
isOverPanel = true;
|
||||
showBar();
|
||||
hideBar();
|
||||
}, function(){
|
||||
isOverPanel = false;
|
||||
hideBar();
|
||||
});
|
||||
|
||||
// support for mobile
|
||||
me.bind('touchstart', function(e,b){
|
||||
if (e.originalEvent.touches.length)
|
||||
{
|
||||
// record where touch started
|
||||
touchDif = e.originalEvent.touches[0].pageY;
|
||||
}
|
||||
});
|
||||
|
||||
me.bind('touchmove', function(e){
|
||||
// prevent scrolling the page if necessary
|
||||
if(!releaseScroll)
|
||||
{
|
||||
e.originalEvent.preventDefault();
|
||||
}
|
||||
if (e.originalEvent.touches.length)
|
||||
{
|
||||
// see how far user swiped
|
||||
var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
|
||||
// scroll content
|
||||
scrollContent(diff, true);
|
||||
touchDif = e.originalEvent.touches[0].pageY;
|
||||
}
|
||||
});
|
||||
|
||||
// set up initial height
|
||||
getBarHeight();
|
||||
|
||||
// check start position
|
||||
if (o.start === 'bottom')
|
||||
{
|
||||
// scroll content to bottom
|
||||
bar.css({ top: me.outerHeight() - bar.outerHeight() });
|
||||
scrollContent(0, true);
|
||||
}
|
||||
else if (o.start !== 'top')
|
||||
{
|
||||
// assume jQuery selector
|
||||
scrollContent($(o.start).position().top, null, true);
|
||||
|
||||
// make sure bar stays hidden
|
||||
if (!o.alwaysVisible) { bar.hide(); }
|
||||
}
|
||||
|
||||
// attach scroll events
|
||||
attachWheel();
|
||||
|
||||
function _onWheel(e)
|
||||
{
|
||||
// use mouse wheel only when mouse is over
|
||||
if (!isOverPanel) { return; }
|
||||
|
||||
var e = e || window.event;
|
||||
|
||||
var delta = 0;
|
||||
if (e.wheelDelta) { delta = -e.wheelDelta/120; }
|
||||
if (e.detail) { delta = e.detail / 3; }
|
||||
|
||||
var target = e.target || e.srcTarget || e.srcElement;
|
||||
if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
|
||||
// scroll content
|
||||
scrollContent(delta, true);
|
||||
}
|
||||
|
||||
// stop window scroll
|
||||
if (e.preventDefault && !releaseScroll) { e.preventDefault(); }
|
||||
if (!releaseScroll) { e.returnValue = false; }
|
||||
}
|
||||
|
||||
function scrollContent(y, isWheel, isJump)
|
||||
{
|
||||
releaseScroll = false;
|
||||
var delta = y;
|
||||
var maxTop = me.outerHeight() - bar.outerHeight();
|
||||
|
||||
if (isWheel)
|
||||
{
|
||||
// move bar with mouse wheel
|
||||
delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
|
||||
|
||||
// move bar, make sure it doesn't go out
|
||||
delta = Math.min(Math.max(delta, 0), maxTop);
|
||||
|
||||
// if scrolling down, make sure a fractional change to the
|
||||
// scroll position isn't rounded away when the scrollbar's CSS is set
|
||||
// this flooring of delta would happened automatically when
|
||||
// bar.css is set below, but we floor here for clarity
|
||||
delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
|
||||
|
||||
// scroll the scrollbar
|
||||
bar.css({ top: delta + 'px' });
|
||||
}
|
||||
|
||||
// calculate actual scroll amount
|
||||
percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
|
||||
delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
|
||||
|
||||
if (isJump)
|
||||
{
|
||||
delta = y;
|
||||
var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
|
||||
offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
|
||||
bar.css({ top: offsetTop + 'px' });
|
||||
}
|
||||
|
||||
// scroll content
|
||||
me.scrollTop(delta);
|
||||
|
||||
// fire scrolling event
|
||||
me.trigger('slimscrolling', ~~delta);
|
||||
|
||||
// ensure bar is visible
|
||||
showBar();
|
||||
|
||||
// trigger hide when scroll is stopped
|
||||
hideBar();
|
||||
}
|
||||
|
||||
function attachWheel()
|
||||
{
|
||||
if (window.addEventListener)
|
||||
{
|
||||
this.addEventListener('DOMMouseScroll', _onWheel, false );
|
||||
this.addEventListener('mousewheel', _onWheel, false );
|
||||
this.addEventListener('MozMousePixelScroll', _onWheel, false );
|
||||
}
|
||||
else
|
||||
{
|
||||
document.attachEvent("onmousewheel", _onWheel)
|
||||
}
|
||||
}
|
||||
|
||||
function getBarHeight()
|
||||
{
|
||||
// calculate scrollbar height and make sure it is not too small
|
||||
barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
|
||||
bar.css({ height: barHeight + 'px' });
|
||||
|
||||
// hide scrollbar if content is not long enough
|
||||
var display = barHeight == me.outerHeight() ? 'none' : 'block';
|
||||
bar.css({ display: display });
|
||||
}
|
||||
|
||||
function showBar()
|
||||
{
|
||||
// recalculate bar height
|
||||
getBarHeight();
|
||||
clearTimeout(queueHide);
|
||||
|
||||
// when bar reached top or bottom
|
||||
if (percentScroll == ~~percentScroll)
|
||||
{
|
||||
//release wheel
|
||||
releaseScroll = o.allowPageScroll;
|
||||
|
||||
// publish approporiate event
|
||||
if (lastScroll != percentScroll)
|
||||
{
|
||||
var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
|
||||
me.trigger('slimscroll', msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
releaseScroll = false;
|
||||
}
|
||||
lastScroll = percentScroll;
|
||||
|
||||
// show only when required
|
||||
if(barHeight >= me.outerHeight()) {
|
||||
//allow window scroll
|
||||
releaseScroll = true;
|
||||
return;
|
||||
}
|
||||
bar.stop(true,true).fadeIn('fast');
|
||||
if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); }
|
||||
}
|
||||
|
||||
function hideBar()
|
||||
{
|
||||
// only hide when options allow it
|
||||
if (!o.alwaysVisible)
|
||||
{
|
||||
queueHide = setTimeout(function(){
|
||||
if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
|
||||
{
|
||||
bar.fadeOut('slow');
|
||||
rail.fadeOut('slow');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// maintain chainability
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery.fn.extend({
|
||||
slimscroll: jQuery.fn.slimScroll
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
@ -0,0 +1,160 @@
|
||||
/*!
|
||||
* jQuery UI Touch Punch 0.2.2
|
||||
*
|
||||
* Copyright 2011, Dave Furfero
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
// Detect touch support
|
||||
$.support.touch = 'ontouchend' in document;
|
||||
|
||||
// Ignore browsers without touch support
|
||||
if (!$.support.touch) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mouseProto = $.ui.mouse.prototype,
|
||||
_mouseInit = mouseProto._mouseInit,
|
||||
touchHandled;
|
||||
|
||||
/**
|
||||
* Simulate a mouse event based on a corresponding touch event
|
||||
* @param {Object} event A touch event
|
||||
* @param {String} simulatedType The corresponding mouse event
|
||||
*/
|
||||
function simulateMouseEvent (event, simulatedType) {
|
||||
|
||||
// Ignore multi-touch events
|
||||
if (event.originalEvent.touches.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
var touch = event.originalEvent.changedTouches[0],
|
||||
simulatedEvent = document.createEvent('MouseEvents');
|
||||
|
||||
// Initialize the simulated mouse event using the touch event's coordinates
|
||||
simulatedEvent.initMouseEvent(
|
||||
simulatedType, // type
|
||||
true, // bubbles
|
||||
true, // cancelable
|
||||
window, // view
|
||||
1, // detail
|
||||
touch.screenX, // screenX
|
||||
touch.screenY, // screenY
|
||||
touch.clientX, // clientX
|
||||
touch.clientY, // clientY
|
||||
false, // ctrlKey
|
||||
false, // altKey
|
||||
false, // shiftKey
|
||||
false, // metaKey
|
||||
0, // button
|
||||
null // relatedTarget
|
||||
);
|
||||
|
||||
// Dispatch the simulated event to the target element
|
||||
event.target.dispatchEvent(simulatedEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchstart events
|
||||
* @param {Object} event The widget element's touchstart event
|
||||
*/
|
||||
mouseProto._touchStart = function (event) {
|
||||
|
||||
var self = this;
|
||||
|
||||
// Ignore the event if another widget is already being handled
|
||||
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to prevent other widgets from inheriting the touch event
|
||||
touchHandled = true;
|
||||
|
||||
// Track movement to determine if interaction was a click
|
||||
self._touchMoved = false;
|
||||
|
||||
// Simulate the mouseover event
|
||||
simulateMouseEvent(event, 'mouseover');
|
||||
|
||||
// Simulate the mousemove event
|
||||
simulateMouseEvent(event, 'mousemove');
|
||||
|
||||
// Simulate the mousedown event
|
||||
simulateMouseEvent(event, 'mousedown');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchmove events
|
||||
* @param {Object} event The document's touchmove event
|
||||
*/
|
||||
mouseProto._touchMove = function (event) {
|
||||
|
||||
// Ignore event if not handled
|
||||
if (!touchHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Interaction was not a click
|
||||
this._touchMoved = true;
|
||||
|
||||
// Simulate the mousemove event
|
||||
simulateMouseEvent(event, 'mousemove');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchend events
|
||||
* @param {Object} event The document's touchend event
|
||||
*/
|
||||
mouseProto._touchEnd = function (event) {
|
||||
|
||||
// Ignore event if not handled
|
||||
if (!touchHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simulate the mouseup event
|
||||
simulateMouseEvent(event, 'mouseup');
|
||||
|
||||
// Simulate the mouseout event
|
||||
simulateMouseEvent(event, 'mouseout');
|
||||
|
||||
// If the touch interaction did not move, it should trigger a click
|
||||
if (!this._touchMoved) {
|
||||
|
||||
// Simulate the click event
|
||||
simulateMouseEvent(event, 'click');
|
||||
}
|
||||
|
||||
// Unset the flag to allow other widgets to inherit the touch event
|
||||
touchHandled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
|
||||
* This method extends the widget with bound touch event handlers that
|
||||
* translate touch events to mouse events and pass them to the widget's
|
||||
* original mouse event handling methods.
|
||||
*/
|
||||
mouseProto._mouseInit = function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
// Delegate the touch handlers to the widget's element
|
||||
self.element
|
||||
.bind('touchstart', $.proxy(self, '_touchStart'))
|
||||
.bind('touchmove', $.proxy(self, '_touchMove'))
|
||||
.bind('touchend', $.proxy(self, '_touchEnd'));
|
||||
|
||||
// Call the original $.ui.mouse init method
|
||||
_mouseInit.call(self);
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* jQuery Hotkeys Plugin
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Based upon the plugin by Tzury Bar Yochay:
|
||||
* http://github.com/tzuryby/hotkeys
|
||||
*
|
||||
* Original idea by:
|
||||
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
||||
*/
|
||||
|
||||
(function(jQuery){
|
||||
|
||||
jQuery.hotkeys = {
|
||||
version: "0.8+",
|
||||
|
||||
specialKeys: {
|
||||
8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause",
|
||||
20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home",
|
||||
37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del",
|
||||
96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7",
|
||||
104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/",
|
||||
112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8",
|
||||
120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 188: ",", 190: ".",
|
||||
191: "/", 224: "meta"
|
||||
},
|
||||
|
||||
shiftNums: {
|
||||
"`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&",
|
||||
"8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<",
|
||||
".": ">", "/": "?", "\\": "|"
|
||||
}
|
||||
};
|
||||
|
||||
function keyHandler( handleObj ) {
|
||||
|
||||
var origHandler = handleObj.handler,
|
||||
//use namespace as keys so it works with event delegation as well
|
||||
//will also allow removing listeners of a specific key combination
|
||||
//and support data objects
|
||||
keys = (handleObj.namespace || "").toLowerCase().split(" ");
|
||||
keys = jQuery.map(keys, function(key) { return key.split("."); });
|
||||
|
||||
//no need to modify handler if no keys specified
|
||||
//Added keys[0].substring(0, 12) to work with jQuery ui 1.9.0
|
||||
//Added accordion, tabs and menu, then jquery ui can use keys.
|
||||
|
||||
if (keys.length === 1 && (keys[0] === "" ||
|
||||
keys[0].substring(0, 12) === "autocomplete" ||
|
||||
keys[0].substring(0, 9) === "accordion" ||
|
||||
keys[0].substring(0, 4) === "tabs" ||
|
||||
keys[0].substring(0, 4) === "menu")) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleObj.handler = function( event ) {
|
||||
// Don't fire in text-accepting inputs that we didn't directly bind to
|
||||
// important to note that $.fn.prop is only available on jquery 1.6+
|
||||
if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) ||
|
||||
event.target.type === "text" || $(event.target).prop('contenteditable') == 'true' )) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keypress represents characters, not special keys
|
||||
var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ],
|
||||
character = String.fromCharCode( event.which ).toLowerCase(),
|
||||
key, modif = "", possible = {};
|
||||
|
||||
// check combinations (alt|ctrl|shift+anything)
|
||||
if ( event.altKey && special !== "alt" ) {
|
||||
modif += "alt_";
|
||||
}
|
||||
|
||||
if ( event.ctrlKey && special !== "ctrl" ) {
|
||||
modif += "ctrl_";
|
||||
}
|
||||
|
||||
// TODO: Need to make sure this works consistently across platforms
|
||||
if ( event.metaKey && !event.ctrlKey && special !== "meta" ) {
|
||||
modif += "meta_";
|
||||
}
|
||||
|
||||
if ( event.shiftKey && special !== "shift" ) {
|
||||
modif += "shift_";
|
||||
}
|
||||
|
||||
if ( special ) {
|
||||
possible[ modif + special ] = true;
|
||||
|
||||
} else {
|
||||
possible[ modif + character ] = true;
|
||||
possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true;
|
||||
|
||||
// "$" can be triggered as "Shift+4" or "Shift+$" or just "$"
|
||||
if ( modif === "shift_" ) {
|
||||
possible[ jQuery.hotkeys.shiftNums[ character ] ] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ( var i = 0, l = keys.length; i < l; i++ ) {
|
||||
if ( possible[ keys[i] ] ) {
|
||||
return origHandler.apply( this, arguments );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
jQuery.each([ "keydown", "keyup", "keypress" ], function() {
|
||||
jQuery.event.special[ this ] = { add: keyHandler };
|
||||
});
|
||||
|
||||
})( jQuery );
|
||||
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* jquery.flot.tooltip
|
||||
*
|
||||
* description: easy-to-use tooltips for Flot charts
|
||||
* version: 0.4.4
|
||||
* author: Krzysztof Urbas @krzysu [myviews.pl]
|
||||
* website: https://github.com/krzysu/flot.tooltip
|
||||
*
|
||||
* build on 2012-12-29
|
||||
* released under MIT License, 2012
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
var options = {
|
||||
tooltip: false, //boolean
|
||||
tooltipOpts: {
|
||||
content: "%s | X: %x | Y: %y.2", //%s -> series label, %x -> X value, %y -> Y value, %x.2 -> precision of X value, %p -> percent
|
||||
dateFormat: "%y-%0m-%0d",
|
||||
shifts: {
|
||||
x: 10,
|
||||
y: 20
|
||||
},
|
||||
defaultTheme: true
|
||||
}
|
||||
};
|
||||
|
||||
var init = function(plot) {
|
||||
|
||||
var tipPosition = {x: 0, y: 0};
|
||||
var opts = plot.getOptions();
|
||||
|
||||
var updateTooltipPosition = function(pos) {
|
||||
tipPosition.x = pos.x;
|
||||
tipPosition.y = pos.y;
|
||||
};
|
||||
|
||||
var onMouseMove = function(e) {
|
||||
|
||||
var pos = {x: 0, y: 0};
|
||||
|
||||
pos.x = e.pageX;
|
||||
pos.y = e.pageY;
|
||||
|
||||
updateTooltipPosition(pos);
|
||||
};
|
||||
|
||||
var timestampToDate = function(tmst) {
|
||||
|
||||
var theDate = new Date(tmst);
|
||||
|
||||
return $.plot.formatDate(theDate, opts.tooltipOpts.dateFormat);
|
||||
};
|
||||
|
||||
plot.hooks.bindEvents.push(function (plot, eventHolder) {
|
||||
|
||||
var to = opts.tooltipOpts;
|
||||
var placeholder = plot.getPlaceholder();
|
||||
var $tip;
|
||||
|
||||
if (opts.tooltip === false) return;
|
||||
|
||||
if( $('#flotTip').length > 0 ){
|
||||
$tip = $('#flotTip');
|
||||
}
|
||||
else {
|
||||
$tip = $('<div />').attr('id', 'flotTip');
|
||||
$tip.appendTo('body').hide().css({position: 'absolute'});
|
||||
|
||||
if(to.defaultTheme) {
|
||||
$tip.css({
|
||||
'background': '#fff',
|
||||
'z-index': '100',
|
||||
'padding': '0.4em 0.6em',
|
||||
'border-radius': '0.5em',
|
||||
'font-size': '0.8em',
|
||||
'border': '1px solid #111'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(placeholder).bind("plothover", function (event, pos, item) {
|
||||
if (item) {
|
||||
var tipText;
|
||||
|
||||
if(opts.xaxis.mode === "time" || opts.xaxes[0].mode === "time") {
|
||||
tipText = stringFormat(to.content, item, timestampToDate);
|
||||
}
|
||||
else {
|
||||
tipText = stringFormat(to.content, item);
|
||||
}
|
||||
|
||||
$tip.html( tipText ).css({left: tipPosition.x + to.shifts.x, top: tipPosition.y + to.shifts.y}).show();
|
||||
}
|
||||
else {
|
||||
$tip.hide().html('');
|
||||
}
|
||||
});
|
||||
|
||||
eventHolder.mousemove(onMouseMove);
|
||||
});
|
||||
|
||||
var stringFormat = function(content, item, fnct) {
|
||||
|
||||
var percentPattern = /%p\.{0,1}(\d{0,})/;
|
||||
var seriesPattern = /%s/;
|
||||
var xPattern = /%x\.{0,1}(\d{0,})/;
|
||||
var yPattern = /%y\.{0,1}(\d{0,})/;
|
||||
|
||||
//percent match
|
||||
if( typeof (item.series.percent) !== 'undefined' ) {
|
||||
content = adjustValPrecision(percentPattern, content, item.series.percent);
|
||||
}
|
||||
//series match
|
||||
if( typeof(item.series.label) !== 'undefined' ) {
|
||||
content = content.replace(seriesPattern, item.series.label);
|
||||
}
|
||||
// xVal match
|
||||
if( typeof(fnct) === 'function' ) {
|
||||
content = content.replace(xPattern, fnct(item.series.data[item.dataIndex][0]) );
|
||||
}
|
||||
else if( typeof item.series.data[item.dataIndex][0] === 'number' ) {
|
||||
content = adjustValPrecision(xPattern, content, item.series.data[item.dataIndex][0]);
|
||||
}
|
||||
// yVal match
|
||||
if( typeof item.series.data[item.dataIndex][1] === 'number' ) {
|
||||
content = adjustValPrecision(yPattern, content, item.series.data[item.dataIndex][1]);
|
||||
}
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
var adjustValPrecision = function(pattern, content, value) {
|
||||
|
||||
var precision;
|
||||
if( content.match(pattern) !== 'null' ) {
|
||||
if(RegExp.$1 !== '') {
|
||||
precision = RegExp.$1;
|
||||
value = value.toFixed(precision)
|
||||
}
|
||||
content = content.replace(pattern, value);
|
||||
}
|
||||
|
||||
return content;
|
||||
};
|
||||
}
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: options,
|
||||
name: 'tooltip',
|
||||
version: '0.4.4'
|
||||
});
|
||||
})(jQuery);
|
||||
@ -0,0 +1,924 @@
|
||||
// Copyright 2006 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
|
||||
// Known Issues:
|
||||
//
|
||||
// * Patterns are not implemented.
|
||||
// * Radial gradient are not implemented. The VML version of these look very
|
||||
// different from the canvas one.
|
||||
// * Clipping paths are not implemented.
|
||||
// * Coordsize. The width and height attribute have higher priority than the
|
||||
// width and height style values which isn't correct.
|
||||
// * Painting mode isn't implemented.
|
||||
// * Canvas width/height should is using content-box by default. IE in
|
||||
// Quirks mode will draw the canvas using border-box. Either change your
|
||||
// doctype to HTML5
|
||||
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
|
||||
// or use Box Sizing Behavior from WebFX
|
||||
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
|
||||
// * Non uniform scaling does not correctly scale strokes.
|
||||
// * Optimize. There is always room for speed improvements.
|
||||
|
||||
// Only add this code if we do not already have a canvas implementation
|
||||
if (!document.createElement('canvas').getContext) {
|
||||
|
||||
(function() {
|
||||
|
||||
// alias some functions to make (compiled) code shorter
|
||||
var m = Math;
|
||||
var mr = m.round;
|
||||
var ms = m.sin;
|
||||
var mc = m.cos;
|
||||
var abs = m.abs;
|
||||
var sqrt = m.sqrt;
|
||||
|
||||
// this is used for sub pixel precision
|
||||
var Z = 10;
|
||||
var Z2 = Z / 2;
|
||||
|
||||
/**
|
||||
* This funtion is assigned to the <canvas> elements as element.getContext().
|
||||
* @this {HTMLElement}
|
||||
* @return {CanvasRenderingContext2D_}
|
||||
*/
|
||||
function getContext() {
|
||||
return this.context_ ||
|
||||
(this.context_ = new CanvasRenderingContext2D_(this));
|
||||
}
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* Binds a function to an object. The returned function will always use the
|
||||
* passed in {@code obj} as {@code this}.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* g = bind(f, obj, a, b)
|
||||
* g(c, d) // will do f.call(obj, a, b, c, d)
|
||||
*
|
||||
* @param {Function} f The function to bind the object to
|
||||
* @param {Object} obj The object that should act as this when the function
|
||||
* is called
|
||||
* @param {*} var_args Rest arguments that will be used as the initial
|
||||
* arguments when the function is called
|
||||
* @return {Function} A new function that has bound this
|
||||
*/
|
||||
function bind(f, obj, var_args) {
|
||||
var a = slice.call(arguments, 2);
|
||||
return function() {
|
||||
return f.apply(obj, a.concat(slice.call(arguments)));
|
||||
};
|
||||
}
|
||||
|
||||
var G_vmlCanvasManager_ = {
|
||||
init: function(opt_doc) {
|
||||
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
|
||||
var doc = opt_doc || document;
|
||||
// Create a dummy element so that IE will allow canvas elements to be
|
||||
// recognized.
|
||||
doc.createElement('canvas');
|
||||
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));
|
||||
}
|
||||
},
|
||||
|
||||
init_: function(doc) {
|
||||
// create xmlns
|
||||
if (!doc.namespaces['g_vml_']) {
|
||||
doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',
|
||||
'#default#VML');
|
||||
|
||||
}
|
||||
if (!doc.namespaces['g_o_']) {
|
||||
doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',
|
||||
'#default#VML');
|
||||
}
|
||||
|
||||
// Setup default CSS. Only add one style sheet per document
|
||||
if (!doc.styleSheets['ex_canvas_']) {
|
||||
var ss = doc.createStyleSheet();
|
||||
ss.owningElement.id = 'ex_canvas_';
|
||||
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +
|
||||
// default size is 300x150 in Gecko and Opera
|
||||
'text-align:left;width:300px;height:150px}' +
|
||||
'g_vml_\\:*{behavior:url(#default#VML)}' +
|
||||
'g_o_\\:*{behavior:url(#default#VML)}';
|
||||
|
||||
}
|
||||
|
||||
// find all canvas elements
|
||||
var els = doc.getElementsByTagName('canvas');
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
this.initElement(els[i]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Public initializes a canvas element so that it can be used as canvas
|
||||
* element from now on. This is called automatically before the page is
|
||||
* loaded but if you are creating elements using createElement you need to
|
||||
* make sure this is called on the element.
|
||||
* @param {HTMLElement} el The canvas element to initialize.
|
||||
* @return {HTMLElement} the element that was created.
|
||||
*/
|
||||
initElement: function(el) {
|
||||
if (!el.getContext) {
|
||||
|
||||
el.getContext = getContext;
|
||||
|
||||
// Remove fallback content. There is no way to hide text nodes so we
|
||||
// just remove all childNodes. We could hide all elements and remove
|
||||
// text nodes but who really cares about the fallback content.
|
||||
el.innerHTML = '';
|
||||
|
||||
// do not use inline function because that will leak memory
|
||||
el.attachEvent('onpropertychange', onPropertyChange);
|
||||
el.attachEvent('onresize', onResize);
|
||||
|
||||
var attrs = el.attributes;
|
||||
if (attrs.width && attrs.width.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setWidth_(attrs.width.nodeValue);
|
||||
el.style.width = attrs.width.nodeValue + 'px';
|
||||
} else {
|
||||
el.width = el.clientWidth;
|
||||
}
|
||||
if (attrs.height && attrs.height.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setHeight_(attrs.height.nodeValue);
|
||||
el.style.height = attrs.height.nodeValue + 'px';
|
||||
} else {
|
||||
el.height = el.clientHeight;
|
||||
}
|
||||
//el.getContext().setCoordsize_()
|
||||
}
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
||||
function onPropertyChange(e) {
|
||||
var el = e.srcElement;
|
||||
|
||||
switch (e.propertyName) {
|
||||
case 'width':
|
||||
el.style.width = el.attributes.width.nodeValue + 'px';
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
case 'height':
|
||||
el.style.height = el.attributes.height.nodeValue + 'px';
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onResize(e) {
|
||||
var el = e.srcElement;
|
||||
if (el.firstChild) {
|
||||
el.firstChild.style.width = el.clientWidth + 'px';
|
||||
el.firstChild.style.height = el.clientHeight + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
G_vmlCanvasManager_.init();
|
||||
|
||||
// precompute "00" to "FF"
|
||||
var dec2hex = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
function createMatrixIdentity() {
|
||||
return [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
function matrixMultiply(m1, m2) {
|
||||
var result = createMatrixIdentity();
|
||||
|
||||
for (var x = 0; x < 3; x++) {
|
||||
for (var y = 0; y < 3; y++) {
|
||||
var sum = 0;
|
||||
|
||||
for (var z = 0; z < 3; z++) {
|
||||
sum += m1[x][z] * m2[z][y];
|
||||
}
|
||||
|
||||
result[x][y] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function copyState(o1, o2) {
|
||||
o2.fillStyle = o1.fillStyle;
|
||||
o2.lineCap = o1.lineCap;
|
||||
o2.lineJoin = o1.lineJoin;
|
||||
o2.lineWidth = o1.lineWidth;
|
||||
o2.miterLimit = o1.miterLimit;
|
||||
o2.shadowBlur = o1.shadowBlur;
|
||||
o2.shadowColor = o1.shadowColor;
|
||||
o2.shadowOffsetX = o1.shadowOffsetX;
|
||||
o2.shadowOffsetY = o1.shadowOffsetY;
|
||||
o2.strokeStyle = o1.strokeStyle;
|
||||
o2.globalAlpha = o1.globalAlpha;
|
||||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
o2.lineScale_ = o1.lineScale_;
|
||||
}
|
||||
|
||||
function processStyle(styleString) {
|
||||
var str, alpha = 1;
|
||||
|
||||
styleString = String(styleString);
|
||||
if (styleString.substring(0, 3) == 'rgb') {
|
||||
var start = styleString.indexOf('(', 3);
|
||||
var end = styleString.indexOf(')', start + 1);
|
||||
var guts = styleString.substring(start + 1, end).split(',');
|
||||
|
||||
str = '#';
|
||||
for (var i = 0; i < 3; i++) {
|
||||
str += dec2hex[Number(guts[i])];
|
||||
}
|
||||
|
||||
if (guts.length == 4 && styleString.substr(3, 1) == 'a') {
|
||||
alpha = guts[3];
|
||||
}
|
||||
} else {
|
||||
str = styleString;
|
||||
}
|
||||
|
||||
return {color: str, alpha: alpha};
|
||||
}
|
||||
|
||||
function processLineCap(lineCap) {
|
||||
switch (lineCap) {
|
||||
case 'butt':
|
||||
return 'flat';
|
||||
case 'round':
|
||||
return 'round';
|
||||
case 'square':
|
||||
default:
|
||||
return 'square';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements CanvasRenderingContext2D interface as described by
|
||||
* the WHATWG.
|
||||
* @param {HTMLElement} surfaceElement The element that the 2D context should
|
||||
* be associated with
|
||||
*/
|
||||
function CanvasRenderingContext2D_(surfaceElement) {
|
||||
this.m_ = createMatrixIdentity();
|
||||
|
||||
this.mStack_ = [];
|
||||
this.aStack_ = [];
|
||||
this.currentPath_ = [];
|
||||
|
||||
// Canvas context properties
|
||||
this.strokeStyle = '#000';
|
||||
this.fillStyle = '#000';
|
||||
|
||||
this.lineWidth = 1;
|
||||
this.lineJoin = 'miter';
|
||||
this.lineCap = 'butt';
|
||||
this.miterLimit = Z * 1;
|
||||
this.globalAlpha = 1;
|
||||
this.canvas = surfaceElement;
|
||||
|
||||
var el = surfaceElement.ownerDocument.createElement('div');
|
||||
el.style.width = surfaceElement.clientWidth + 'px';
|
||||
el.style.height = surfaceElement.clientHeight + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
el.style.position = 'absolute';
|
||||
surfaceElement.appendChild(el);
|
||||
|
||||
this.element_ = el;
|
||||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
this.lineScale_ = 1;
|
||||
}
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
contextPrototype.clearRect = function() {
|
||||
this.element_.innerHTML = '';
|
||||
};
|
||||
|
||||
contextPrototype.beginPath = function() {
|
||||
// TODO: Branch current matrix so that save/restore has no effect
|
||||
// as per safari docs.
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.moveTo = function(aX, aY) {
|
||||
var p = this.getCoords_(aX, aY);
|
||||
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});
|
||||
this.currentX_ = p.x;
|
||||
this.currentY_ = p.y;
|
||||
};
|
||||
|
||||
contextPrototype.lineTo = function(aX, aY) {
|
||||
var p = this.getCoords_(aX, aY);
|
||||
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});
|
||||
|
||||
this.currentX_ = p.x;
|
||||
this.currentY_ = p.y;
|
||||
};
|
||||
|
||||
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
|
||||
aCP2x, aCP2y,
|
||||
aX, aY) {
|
||||
var p = this.getCoords_(aX, aY);
|
||||
var cp1 = this.getCoords_(aCP1x, aCP1y);
|
||||
var cp2 = this.getCoords_(aCP2x, aCP2y);
|
||||
bezierCurveTo(this, cp1, cp2, p);
|
||||
};
|
||||
|
||||
// Helper function that takes the already fixed cordinates.
|
||||
function bezierCurveTo(self, cp1, cp2, p) {
|
||||
self.currentPath_.push({
|
||||
type: 'bezierCurveTo',
|
||||
cp1x: cp1.x,
|
||||
cp1y: cp1.y,
|
||||
cp2x: cp2.x,
|
||||
cp2y: cp2.y,
|
||||
x: p.x,
|
||||
y: p.y
|
||||
});
|
||||
self.currentX_ = p.x;
|
||||
self.currentY_ = p.y;
|
||||
}
|
||||
|
||||
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
|
||||
// the following is lifted almost directly from
|
||||
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
|
||||
|
||||
var cp = this.getCoords_(aCPx, aCPy);
|
||||
var p = this.getCoords_(aX, aY);
|
||||
|
||||
var cp1 = {
|
||||
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),
|
||||
y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)
|
||||
};
|
||||
var cp2 = {
|
||||
x: cp1.x + (p.x - this.currentX_) / 3.0,
|
||||
y: cp1.y + (p.y - this.currentY_) / 3.0
|
||||
};
|
||||
|
||||
bezierCurveTo(this, cp1, cp2, p);
|
||||
};
|
||||
|
||||
contextPrototype.arc = function(aX, aY, aRadius,
|
||||
aStartAngle, aEndAngle, aClockwise) {
|
||||
aRadius *= Z;
|
||||
var arcType = aClockwise ? 'at' : 'wa';
|
||||
|
||||
var xStart = aX + mc(aStartAngle) * aRadius - Z2;
|
||||
var yStart = aY + ms(aStartAngle) * aRadius - Z2;
|
||||
|
||||
var xEnd = aX + mc(aEndAngle) * aRadius - Z2;
|
||||
var yEnd = aY + ms(aEndAngle) * aRadius - Z2;
|
||||
|
||||
// IE won't render arches drawn counter clockwise if xStart == xEnd.
|
||||
if (xStart == xEnd && !aClockwise) {
|
||||
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
|
||||
// that can be represented in binary
|
||||
}
|
||||
|
||||
var p = this.getCoords_(aX, aY);
|
||||
var pStart = this.getCoords_(xStart, yStart);
|
||||
var pEnd = this.getCoords_(xEnd, yEnd);
|
||||
|
||||
this.currentPath_.push({type: arcType,
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
radius: aRadius,
|
||||
xStart: pStart.x,
|
||||
yStart: pStart.y,
|
||||
xEnd: pEnd.x,
|
||||
yEnd: pEnd.y});
|
||||
|
||||
};
|
||||
|
||||
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
};
|
||||
|
||||
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
|
||||
var oldPath = this.currentPath_;
|
||||
this.beginPath();
|
||||
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.stroke();
|
||||
|
||||
this.currentPath_ = oldPath;
|
||||
};
|
||||
|
||||
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
|
||||
var oldPath = this.currentPath_;
|
||||
this.beginPath();
|
||||
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.fill();
|
||||
|
||||
this.currentPath_ = oldPath;
|
||||
};
|
||||
|
||||
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
|
||||
var gradient = new CanvasGradient_('gradient');
|
||||
gradient.x0_ = aX0;
|
||||
gradient.y0_ = aY0;
|
||||
gradient.x1_ = aX1;
|
||||
gradient.y1_ = aY1;
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.createRadialGradient = function(aX0, aY0, aR0,
|
||||
aX1, aY1, aR1) {
|
||||
var gradient = new CanvasGradient_('gradientradial');
|
||||
gradient.x0_ = aX0;
|
||||
gradient.y0_ = aY0;
|
||||
gradient.r0_ = aR0;
|
||||
gradient.x1_ = aX1;
|
||||
gradient.y1_ = aY1;
|
||||
gradient.r1_ = aR1;
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.drawImage = function(image, var_args) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
|
||||
// to find the original width we overide the width and height
|
||||
var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
image.runtimeStyle.width = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
|
||||
// and remove overides
|
||||
image.runtimeStyle.width = oldRuntimeWidth;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
sx = sy = 0;
|
||||
sw = dw = w;
|
||||
sh = dh = h;
|
||||
} else if (arguments.length == 5) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
dw = arguments[3];
|
||||
dh = arguments[4];
|
||||
sx = sy = 0;
|
||||
sw = w;
|
||||
sh = h;
|
||||
} else if (arguments.length == 9) {
|
||||
sx = arguments[1];
|
||||
sy = arguments[2];
|
||||
sw = arguments[3];
|
||||
sh = arguments[4];
|
||||
dx = arguments[5];
|
||||
dy = arguments[6];
|
||||
dw = arguments[7];
|
||||
dh = arguments[8];
|
||||
} else {
|
||||
throw Error('Invalid number of arguments');
|
||||
}
|
||||
|
||||
var d = this.getCoords_(dx, dy);
|
||||
|
||||
var w2 = sw / 2;
|
||||
var h2 = sh / 2;
|
||||
|
||||
var vmlStr = [];
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
// For some reason that I've now forgotten, using divs didn't work
|
||||
vmlStr.push(' <g_vml_:group',
|
||||
' coordsize="', Z * W, ',', Z * H, '"',
|
||||
' coordorigin="0,0"' ,
|
||||
' style="width:', W, 'px;height:', H, 'px;position:absolute;');
|
||||
|
||||
// If filters are necessary (rotation exists), create them
|
||||
// filters are bog-slow, so only create them if abbsolutely necessary
|
||||
// The following check doesn't account for skews (which don't exist
|
||||
// in the canvas spec (yet) anyway.
|
||||
|
||||
if (this.m_[0][0] != 1 || this.m_[0][1]) {
|
||||
var filter = [];
|
||||
|
||||
// Note the 12/21 reversal
|
||||
filter.push('M11=', this.m_[0][0], ',',
|
||||
'M12=', this.m_[1][0], ',',
|
||||
'M21=', this.m_[0][1], ',',
|
||||
'M22=', this.m_[1][1], ',',
|
||||
'Dx=', mr(d.x / Z), ',',
|
||||
'Dy=', mr(d.y / Z), '');
|
||||
|
||||
// Bounding box calculation (need to minimize displayed area so that
|
||||
// filters don't waste time on unused pixels.
|
||||
var max = d;
|
||||
var c2 = this.getCoords_(dx + dw, dy);
|
||||
var c3 = this.getCoords_(dx, dy + dh);
|
||||
var c4 = this.getCoords_(dx + dw, dy + dh);
|
||||
|
||||
max.x = m.max(max.x, c2.x, c3.x, c4.x);
|
||||
max.y = m.max(max.y, c2.y, c3.y, c4.y);
|
||||
|
||||
vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),
|
||||
'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',
|
||||
filter.join(''), ", sizingmethod='clip');")
|
||||
} else {
|
||||
vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');
|
||||
}
|
||||
|
||||
vmlStr.push(' ">' ,
|
||||
'<g_vml_:image src="', image.src, '"',
|
||||
' style="width:', Z * dw, 'px;',
|
||||
' height:', Z * dh, 'px;"',
|
||||
' cropleft="', sx / w, '"',
|
||||
' croptop="', sy / h, '"',
|
||||
' cropright="', (w - sx - sw) / w, '"',
|
||||
' cropbottom="', (h - sy - sh) / h, '"',
|
||||
' />',
|
||||
'</g_vml_:group>');
|
||||
|
||||
this.element_.insertAdjacentHTML('BeforeEnd',
|
||||
vmlStr.join(''));
|
||||
};
|
||||
|
||||
contextPrototype.stroke = function(aFill) {
|
||||
var lineStr = [];
|
||||
var lineOpen = false;
|
||||
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
|
||||
var color = a.color;
|
||||
var opacity = a.alpha * this.globalAlpha;
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
lineStr.push('<g_vml_:shape',
|
||||
' filled="', !!aFill, '"',
|
||||
' style="position:absolute;width:', W, 'px;height:', H, 'px;"',
|
||||
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
|
||||
' stroked="', !aFill, '"',
|
||||
' path="');
|
||||
|
||||
var newSeq = false;
|
||||
var min = {x: null, y: null};
|
||||
var max = {x: null, y: null};
|
||||
|
||||
for (var i = 0; i < this.currentPath_.length; i++) {
|
||||
var p = this.currentPath_[i];
|
||||
var c;
|
||||
|
||||
switch (p.type) {
|
||||
case 'moveTo':
|
||||
c = p;
|
||||
lineStr.push(' m ', mr(p.x), ',', mr(p.y));
|
||||
break;
|
||||
case 'lineTo':
|
||||
lineStr.push(' l ', mr(p.x), ',', mr(p.y));
|
||||
break;
|
||||
case 'close':
|
||||
lineStr.push(' x ');
|
||||
p = null;
|
||||
break;
|
||||
case 'bezierCurveTo':
|
||||
lineStr.push(' c ',
|
||||
mr(p.cp1x), ',', mr(p.cp1y), ',',
|
||||
mr(p.cp2x), ',', mr(p.cp2y), ',',
|
||||
mr(p.x), ',', mr(p.y));
|
||||
break;
|
||||
case 'at':
|
||||
case 'wa':
|
||||
lineStr.push(' ', p.type, ' ',
|
||||
mr(p.x - this.arcScaleX_ * p.radius), ',',
|
||||
mr(p.y - this.arcScaleY_ * p.radius), ' ',
|
||||
mr(p.x + this.arcScaleX_ * p.radius), ',',
|
||||
mr(p.y + this.arcScaleY_ * p.radius), ' ',
|
||||
mr(p.xStart), ',', mr(p.yStart), ' ',
|
||||
mr(p.xEnd), ',', mr(p.yEnd));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Following is broken for curves due to
|
||||
// move to proper paths.
|
||||
|
||||
// Figure out dimensions so we can do gradient fills
|
||||
// properly
|
||||
if (p) {
|
||||
if (min.x == null || p.x < min.x) {
|
||||
min.x = p.x;
|
||||
}
|
||||
if (max.x == null || p.x > max.x) {
|
||||
max.x = p.x;
|
||||
}
|
||||
if (min.y == null || p.y < min.y) {
|
||||
min.y = p.y;
|
||||
}
|
||||
if (max.y == null || p.y > max.y) {
|
||||
max.y = p.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
lineStr.push(' ">');
|
||||
|
||||
if (!aFill) {
|
||||
var lineWidth = this.lineScale_ * this.lineWidth;
|
||||
|
||||
// VML cannot correctly render a line if the width is less than 1px.
|
||||
// In that case, we dilute the color to make the line look thinner.
|
||||
if (lineWidth < 1) {
|
||||
opacity *= lineWidth;
|
||||
}
|
||||
|
||||
lineStr.push(
|
||||
'<g_vml_:stroke',
|
||||
' opacity="', opacity, '"',
|
||||
' joinstyle="', this.lineJoin, '"',
|
||||
' miterlimit="', this.miterLimit, '"',
|
||||
' endcap="', processLineCap(this.lineCap), '"',
|
||||
' weight="', lineWidth, 'px"',
|
||||
' color="', color, '" />'
|
||||
);
|
||||
} else if (typeof this.fillStyle == 'object') {
|
||||
var fillStyle = this.fillStyle;
|
||||
var angle = 0;
|
||||
var focus = {x: 0, y: 0};
|
||||
|
||||
// additional offset
|
||||
var shift = 0;
|
||||
// scale factor for offset
|
||||
var expansion = 1;
|
||||
|
||||
if (fillStyle.type_ == 'gradient') {
|
||||
var x0 = fillStyle.x0_ / this.arcScaleX_;
|
||||
var y0 = fillStyle.y0_ / this.arcScaleY_;
|
||||
var x1 = fillStyle.x1_ / this.arcScaleX_;
|
||||
var y1 = fillStyle.y1_ / this.arcScaleY_;
|
||||
var p0 = this.getCoords_(x0, y0);
|
||||
var p1 = this.getCoords_(x1, y1);
|
||||
var dx = p1.x - p0.x;
|
||||
var dy = p1.y - p0.y;
|
||||
angle = Math.atan2(dx, dy) * 180 / Math.PI;
|
||||
|
||||
// The angle should be a non-negative number.
|
||||
if (angle < 0) {
|
||||
angle += 360;
|
||||
}
|
||||
|
||||
// Very small angles produce an unexpected result because they are
|
||||
// converted to a scientific notation string.
|
||||
if (angle < 1e-6) {
|
||||
angle = 0;
|
||||
}
|
||||
} else {
|
||||
var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_);
|
||||
var width = max.x - min.x;
|
||||
var height = max.y - min.y;
|
||||
focus = {
|
||||
x: (p0.x - min.x) / width,
|
||||
y: (p0.y - min.y) / height
|
||||
};
|
||||
|
||||
width /= this.arcScaleX_ * Z;
|
||||
height /= this.arcScaleY_ * Z;
|
||||
var dimension = m.max(width, height);
|
||||
shift = 2 * fillStyle.r0_ / dimension;
|
||||
expansion = 2 * fillStyle.r1_ / dimension - shift;
|
||||
}
|
||||
|
||||
// We need to sort the color stops in ascending order by offset,
|
||||
// otherwise IE won't interpret it correctly.
|
||||
var stops = fillStyle.colors_;
|
||||
stops.sort(function(cs1, cs2) {
|
||||
return cs1.offset - cs2.offset;
|
||||
});
|
||||
|
||||
var length = stops.length;
|
||||
var color1 = stops[0].color;
|
||||
var color2 = stops[length - 1].color;
|
||||
var opacity1 = stops[0].alpha * this.globalAlpha;
|
||||
var opacity2 = stops[length - 1].alpha * this.globalAlpha;
|
||||
|
||||
var colors = [];
|
||||
for (var i = 0; i < length; i++) {
|
||||
var stop = stops[i];
|
||||
colors.push(stop.offset * expansion + shift + ' ' + stop.color);
|
||||
}
|
||||
|
||||
// When colors attribute is used, the meanings of opacity and o:opacity2
|
||||
// are reversed.
|
||||
lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"',
|
||||
' method="none" focus="100%"',
|
||||
' color="', color1, '"',
|
||||
' color2="', color2, '"',
|
||||
' colors="', colors.join(','), '"',
|
||||
' opacity="', opacity2, '"',
|
||||
' g_o_:opacity2="', opacity1, '"',
|
||||
' angle="', angle, '"',
|
||||
' focusposition="', focus.x, ',', focus.y, '" />');
|
||||
} else {
|
||||
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity,
|
||||
'" />');
|
||||
}
|
||||
|
||||
lineStr.push('</g_vml_:shape>');
|
||||
|
||||
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));
|
||||
};
|
||||
|
||||
contextPrototype.fill = function() {
|
||||
this.stroke(true);
|
||||
}
|
||||
|
||||
contextPrototype.closePath = function() {
|
||||
this.currentPath_.push({type: 'close'});
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
contextPrototype.getCoords_ = function(aX, aY) {
|
||||
var m = this.m_;
|
||||
return {
|
||||
x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,
|
||||
y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2
|
||||
}
|
||||
};
|
||||
|
||||
contextPrototype.save = function() {
|
||||
var o = {};
|
||||
copyState(this, o);
|
||||
this.aStack_.push(o);
|
||||
this.mStack_.push(this.m_);
|
||||
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.restore = function() {
|
||||
copyState(this.aStack_.pop(), this);
|
||||
this.m_ = this.mStack_.pop();
|
||||
};
|
||||
|
||||
function matrixIsFinite(m) {
|
||||
for (var j = 0; j < 3; j++) {
|
||||
for (var k = 0; k < 2; k++) {
|
||||
if (!isFinite(m[j][k]) || isNaN(m[j][k])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function setM(ctx, m, updateLineScale) {
|
||||
if (!matrixIsFinite(m)) {
|
||||
return;
|
||||
}
|
||||
ctx.m_ = m;
|
||||
|
||||
if (updateLineScale) {
|
||||
// Get the line scale.
|
||||
// Determinant of this.m_ means how much the area is enlarged by the
|
||||
// transformation. So its square root can be used as a scale factor
|
||||
// for width.
|
||||
var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];
|
||||
ctx.lineScale_ = sqrt(abs(det));
|
||||
}
|
||||
}
|
||||
|
||||
contextPrototype.translate = function(aX, aY) {
|
||||
var m1 = [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[aX, aY, 1]
|
||||
];
|
||||
|
||||
setM(this, matrixMultiply(m1, this.m_), false);
|
||||
};
|
||||
|
||||
contextPrototype.rotate = function(aRot) {
|
||||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
setM(this, matrixMultiply(m1, this.m_), false);
|
||||
};
|
||||
|
||||
contextPrototype.scale = function(aX, aY) {
|
||||
this.arcScaleX_ *= aX;
|
||||
this.arcScaleY_ *= aY;
|
||||
var m1 = [
|
||||
[aX, 0, 0],
|
||||
[0, aY, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
setM(this, matrixMultiply(m1, this.m_), true);
|
||||
};
|
||||
|
||||
contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {
|
||||
var m1 = [
|
||||
[m11, m12, 0],
|
||||
[m21, m22, 0],
|
||||
[dx, dy, 1]
|
||||
];
|
||||
|
||||
setM(this, matrixMultiply(m1, this.m_), true);
|
||||
};
|
||||
|
||||
contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {
|
||||
var m = [
|
||||
[m11, m12, 0],
|
||||
[m21, m22, 0],
|
||||
[dx, dy, 1]
|
||||
];
|
||||
|
||||
setM(this, m, true);
|
||||
};
|
||||
|
||||
/******** STUBS ********/
|
||||
contextPrototype.clip = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.arcTo = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.createPattern = function() {
|
||||
return new CanvasPattern_;
|
||||
};
|
||||
|
||||
// Gradient / Pattern Stubs
|
||||
function CanvasGradient_(aType) {
|
||||
this.type_ = aType;
|
||||
this.x0_ = 0;
|
||||
this.y0_ = 0;
|
||||
this.r0_ = 0;
|
||||
this.x1_ = 0;
|
||||
this.y1_ = 0;
|
||||
this.r1_ = 0;
|
||||
this.colors_ = [];
|
||||
}
|
||||
|
||||
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
|
||||
aColor = processStyle(aColor);
|
||||
this.colors_.push({offset: aOffset,
|
||||
color: aColor.color,
|
||||
alpha: aColor.alpha});
|
||||
};
|
||||
|
||||
function CanvasPattern_() {}
|
||||
|
||||
// set up externs
|
||||
G_vmlCanvasManager = G_vmlCanvasManager_;
|
||||
CanvasRenderingContext2D = CanvasRenderingContext2D_;
|
||||
CanvasGradient = CanvasGradient_;
|
||||
CanvasPattern = CanvasPattern_;
|
||||
|
||||
})();
|
||||
|
||||
} // if
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue