Undo & Redo function not working as expect - javascript

I'm trying to create an undo/redo function for jQueryUI .draggable at the moment I have a function but doesn't work as expect, here is my funcion:
$(document).ready(function() {
$('.editable').draggable({
stop: stopHandlerDrag,
start: startHandlerDrag
});
});
var historyApp = {
stackStyle: [],
stackId: [],
counter: -1,
add: function(style, id) {
++this.counter;
this.stackStyle[this.counter] = style;
this.stackId[this.counter] = id;
this.doSomethingWith(style, id);
// delete anything forward of the counter
this.stackStyle.splice(this.counter + 1);
},
undo: function() {
--this.counter;
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
redo: function() {
++this.counter;
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
doSomethingWith: function(style, id) {
//Check if make buttons undo/redo disabled or enabled
if (this.counter <= -1) {
$('#undo').addClass('disabled');
$('#redo').removeClass('disabled');
return;
} else {
$('#undo').removeClass('disabled');
}
if (this.counter == this.stackStyle.length) {
$('#redo').addClass('disabled');
$('#undo').removeClass('disabled');
return;
} else {
$('#redo').removeClass('disabled');
}
console.log(style + ' - ' + id);
//Apply history style
$('#' + id).attr('style', style);
console.log(this.counter + ' - ' + this.stackStyle.length);
}
};
//Stop Handler Drag
function stopHandlerDrag(event, ui) {
console.log('stop drag');
var style = $(ui.helper).attr('style');
var id = $(ui.helper).attr('id');
historyApp.add(style, id);
}
//Star Handler Drag
function startHandlerDrag(event, ui) {
console.log('start drag');
var style = $(ui.helper).attr('style');
var id = $(ui.helper).attr('id');
historyApp.add(style, id);
//Dettach all events
$('#' + id).draggable("option", "revert", false);
//reassign stop events
$('#' + id).draggable({
stop: stopHandlerDrag,
start: ''
});
}
//Click Events For Redo and Undo
$(document).on('click', '#redo', function() {
historyApp.redo();
});
$(document).on('click', '#undo', function() {
historyApp.undo();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script>
<span id="undo" class="btn btn-sm btn-success disable">Undo</span>
<span id="redo" class="btn btn-sm btn-danger disable ">Redo</span>
<p id="P1" class="editable" style="left: auto; top:auto;">Drag #1</<p>
<p id="P2" class="editable" style="left: auto; top:auto;">Drag #2</<p>
<p id="P3" class="editable" style="left: auto; top:auto;">Drag #3</<p>
And here is a Working code demo
Now, the problem is that for example if you run my code and drag the elements in the following order (Drag #1, Drag #2, Drag #3), and then you click on undo sometimes you will have to click twice, and then if you drag in that order and the click on undo to restore all the elements as at te beginning and then drag elements #1 and #2 and then click on undo you will notice that only the #1 element is going to comeback to his possition, so my question is what I'm doing wrong and how can I solve this? I think that maybe my counter is wrong or something is wrong with my var historyApp

You probably need to append a logic to check if the style to undo or redo differs from the prev/next state:
add: function (style, id) {
var preIndex = this.counter;
if (this.counter == this.stackStyle.length - 1)
{
++this.counter;
}
else
{
this.counter = this.stackStyle.length;
}
this.stackStyle[this.counter] = style;
this.stackId[this.counter] = id;
this.doSomethingWith(style, id);
},
undo: function () {
--this.counter;
while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter])
{
--this.counter;
}
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},
redo: function () {
++this.counter;
while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter]) {
++this.counter;
}
this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]);
},

There's something wrong with your logic. Here's a StateMaker I made myself. Don't copy it letter for letter, but it shows you the logic.
function StateMaker(initialState){
var o = initialState;
if(o){
this.initialState = o; this.states = [o];
}
else{
this.states = [];
}
this.savedStates = []; this.canUndo = this.canRedo = false; this.undoneStates = [];
this.addState = function(state){
this.states.push(state); this.undoneStates = []; this.canUndo = true; this.canRedo = false;
return this;
}
this.undo = function(){
var sl = this.states.length;
if(this.initialState){
if(sl > 1){
this.undoneStates.push(this.states.pop()); this.canRedo = true;
if(this.states.length < 2){
this.canUndo = false;
}
}
else{
this.canUndo = false;
}
}
else if(sl > 0){
this.undoneStates.push(this.states.pop()); this.canRedo = true;
}
else{
this.canUndo = false;
}
return this;
}
this.redo = function(){
if(this.undoneStates.length > 0){
this.states.push(this.undoneStates.pop()); this.canUndo = true;
if(this.undoneStates.length < 1){
this.canRedo = false;
}
}
else{
this.canRedo = false;
}
return this;
}
this.save = function(){
this.savedStates = this.states.slice();
return this;
}
this.isSavedState = function(){
var st = this.states, l = st.length; sv = this.savedStates;
if(l !== sv.length){
return false;
}
function rec(o, c){
var x, z;
if(typeof o !== typeof c){
return false;
}
else if(o instanceof Array){
for(var n=0,q=o.length; n<q; n++){
x = o[n]; z = c[n];
if(x && typeof x === 'object'){
return rec(x, z);
}
else if(o !== c){
return false;
}
}
}
else if(o && typeof o === 'object'){
for(var n in o){
x = o[n]; z = c[n];
if(x && typeof x === 'object'){
return rec(x, z);
}
else if(o !== c){
return false;
}
}
}
else if(o !== c){
return false;
}
return true;
}
for(var i=0; i<l; i++){
if(!rec(st[i], sv[i])){
return false;
}
}
return true;
}
}
Implementation with jQuery UI would look something like:
var dragging;
function DragMaster(yourDraggable){
var d = yourDraggable, p = d.offset(), sm = new StateMaker({left:p.left, top:p.top})), states = sm.states, t = this;
function ls(){
if(states.length){
return states.slice(-1)[0];
}
return false;
}
this.update = function(){
var cp = d.offset();
sm.addState({left:cp.left, top:cp.top});
return this;
}
this.undo = function(){
sm.undo(); d.offset(ls());
return this;
}
this.redo = function(){
sm.redo(); d.offset(ls());
return this;
}
this.save = function(){
sm.save();
return this;
}
this.getStates = function(){
return states;
}
this.getSavedStates = function(){
return sm.savedStates;
}
this.init = function(){
return {
start: function(){
dragging = d;
},
stop: function(){
t.update();
}
}
}
}
var yd = $('#yourDraggable'), dm = new DragMaster(yd);
yd.draggable(dm.init());
$('#undo').click(function(){
if(dragging === yd)dm.undo();
});
$('#redo').click(function(){
if(dragging === yd)dm.redo();
});
Now you can create new DragMaster()s and send .init() into draggable.
Note:
If you want to see if the current state is the saved state to change the color of your save button then StateMakerInstance.isSavedState() is what you can use.

Related

Using the input box instead of pop function

How can I use an input box instead of prompt on this code?
Code is
here.
Once I click on the link; a prompt pops up and asks to type a number or Q to quit. How would I change that to input box?
function Stack() {
var items = [];
this.push = function(element){
items.push(element);
};
this.pop = function(){
return items.pop();
};
this.peek = function(){
return items[items.length-1];
};
this.isEmpty = function(){
return items.length == 0;
};
this.size = function(){
return items.length;
};
this.clear = function(){
items = [];
};
this.print = function(){
alert("Stack Elements are:"+items.toString());
};
}
Declare stack object:
var stack = new Stack();
while(1)
{
var element = prompt("Enter stack element,(q or Q to exit)", "");
if(element == 'q' || element =='Q')
{
break;
}
else
{
if(element=='*'
|| element=='+' || element=='-' || element=='/')
{
//Check if stack is empty
if(stack.isEmpty() || stack.size<2 )
{
alert("Invalid Operation, Stack is empty or size is less than 2");
}
else
{
var op1 = stack.pop();
var op2 = stack.pop();
if(element=='*')
{
var res = op1 * op2;
}
else if(element=='+')
{
var res = op1 + op2;
}
else if(element=='-')
{
var res = op1 - op2;
}
else if(element=='/')
{
var res = op1 / op2;
}
stack.push(res);
stack.print();
}
}
else
{
stack.push(element);
stack.print();
}
}
}
try following example :
FIDDLE
var stack = new Stack();
function Stack() {
var items = [];
this.push = function(element) {
items.push(element);
};
this.pop = function() {
return items.pop();
};
this.peek = function() {
return items[items.length - 1];
};
this.isEmpty = function() {
return items.length == 0;
};
this.size = function() {
return items.length;
};
this.clear = function() {
items = [];
};
this.print = function() {
theList.options.length = 0;
for (var i = 0; i < items.length; i++) {
var theOption = new Option(items[i]);
theList.options[theList.options.length] = theOption;
}
};
}
function pushStack(newVal) {
if (newVal == '*' || newVal == '+' || newVal == '-' || newVal == '/') {
//Check if stack is empty
if (stack.isEmpty() || stack.size < 2) {
alert("Invalid Operation, Stack is empty or size is less than 2");
} else {
var op1 = stack.pop();
var op2 = stack.pop();
if (newVal == '*') {
var res = op1 * op2;
} else if (newVal == '+') {
var res = op1 + op2;
} else if (newVal == '-') {
var res = op1 - op2;
} else if (newVal == '/') {
var res = op1 / op2;
}
stack.push(res);
}
} else {
stack.push(newVal);
}
}
function popStack() {
var popVal = stack.pop();
if (popVal == undefined)
return "Empty stack!";
else
return popVal;
}
function showStack() {
stack.print();
}
<FORM>
<INPUT type="text" name="txtPush" id="txtPush" />
<INPUT type=button value="Push" onClick='pushStack(txtPush.value); txtPush.value=""; showStack(theList);' />
<SELECT name="theList" id="theList" size=12>
<OPTION>Displays the current state of the stack!</OPTION>
</SELECT>
</FORM>

selections validation by value with jquery

I have 5 select fields with id's id1, id2,.. id5
and i need check if values (selected) not equal then highlight green and if equal then highlight red
but is look crazy validate each field 5 times? is posible use special functions ir validate easy than with:
if
if
if
if
I dont need a code just idea.
You can loop through each select to compare the values
$("select").change(function () {
flag = false;
var value = $(this).val();
$("select").each(function () {
if ($(this).val() != value)
flag = true;
});
if (flag)
$("select").css("color", "red");
else
$("select").css("color", "green");
});
Demo
Edit
$("select").change(function () {
var flag = true;
$("select").each(function () {
var outer = this;
$("select").not(outer).each(function () {
if ($(outer).val() == $(this).val()) {
flag = false;
return false;
}
});
});
if (flag)
$("select").css("color", "green");
else
$("select").css("color", "red");
});
Updated Fiddle
New update
I've simplified the code like this. YOu dont have to use nested loop if you do like this
$("select").change(function () {
var flag = true;
$("select").each(function () {
if ($("select").find("option:selected[value=" + this.value + "]").length > 1) {
flag = false;
return false;
}
});
if (flag)
$("select").css("color", "green");
else
$("select").css("color", "red");
});
Updated Fiddle
Try this one..
$("select").change(function(){
var selected = [];
var valiSel = [];
$('select > option:selected').each(function() {
if($(this).val() != 0){
selected.push( $(this).val() );
}
valiSel.push( $(this).val() );
});
var unique = unique12(selected);
var uniqueLength = unique.length;
var valiSelUnique = unique12(valiSel);
var selectedLength = selected.length;
if( unique.length != selected.length ){
alert( 'Two Selected value cannot be same' );
return false;
}
//return true;
});
function unique12(sel) {
var r = new Array();
o:for(var i = 0, n = sel.length; i < n; i++)
{
for(var x = 0, y = r.length; x < y; x++)
{
if(r[x]==sel[i])
{
//alert('this is a DUPE!');
continue o;
}
}
r[r.length] = sel[i];
}
return r;
}
DEMO

Validate form function jump to error?

I have a form with a validation script that works perfectly. I would however like the form to jump to the fields that doesn't validate or display the name of the fields in the error message.
The code I use to validate is:
else
{
var valid = document.formvalidator.isValid(f);
}
if (flag == 0 || valid == true) {
f.check.value = '<?php echo JUtility::getToken(); ?>';//send token
}
else {
alert('There was an error with the fields..');
return false;
}
return true;
How can I get the alert to name the fields that need to be filled in correctly or jump to the specific field?
Edited ----------
Hi,
Thanks for help so far. I'm very new to JS. The form is in a component of Joomla.
The full function that validates the form is
function validateForm(f){
var browser = navigator.appName;
if (browser == "Microsoft Internet Explorer"){
var flag = 0;
for (var i=0;i < f.elements.length; i++) {
el = f.elements[i];
if ($(el).hasClass('required')) {
var idz= $(el).getProperty('id');
if(document.getElementById(idz)){
if (!document.getElementById(idz).value) {
document.formvalidator.handleResponse(false, el);
flag = flag + 1;
}
}
}
}
}
else {
var valid = document.formvalidator.isValid(f);
}
if(flag == 0 || valid == true){
f.check.value='<?php echo JUtility::getToken(); ?>';//send token
}
else {
alert('<?php echo JText::_('JBJOBS_FIEDS_HIGHLIGHTED_RED_COMPULSORY'); ?>');
return false;
}
return true;
}
External js file:
var JFormValidator = new Class(
{
initialize : function() {
this.handlers = Object();
this.custom = Object();
this.setHandler("username", function(b) {
regex = new RegExp("[<|>|\"|'|%|;|(|)|&]", "i");
return !regex.test(b)
});
this.setHandler("password", function(b) {
regex = /^\S[\S ]{2,98}\S$/;
return regex.test(b)
});
this.setHandler('passverify',
function (value) {
return ($('password').value == value);
}
); // added March 2011
this.setHandler("numeric", function(b) {
regex = /^(\d|-)?(\d|,)*\.?\d*$/;
return regex.test(b)
});
this
.setHandler(
"email",
function(b) {
regex = /^[a-zA-Z0-9._-]+(\+[a-zA-Z0-9._-]+)*#([a-zA-Z0-9.-]+\.)+[a-zA-Z0-9.-]{2,4}$/;
return regex.test(b)
});
var a = $$("form.form-validate");
a.each(function(b) {
this.attachToForm(b)
}, this)
},
setHandler : function(b, c, a) {
a = (a == "") ? true : a;
this.handlers[b] = {
enabled : a,
exec : c
}
},
attachToForm : function(a) {
a.getElements("input,textarea,select")
.each(
function(b) {
if (($(b).get("tag") == "input" || $(b)
.get("tag") == "button")
&& $(b).get("type") == "submit") {
if (b.hasClass("validate")) {
b.onclick = function() {
return document.formvalidator
.isValid(this.form)
}
}
} else {
b.addEvent("blur", function() {
return document.formvalidator
.validate(this)
})
}
})
},
validate : function(c) {
c = $(c);
if (c.get("disabled")) {
this.handleResponse(true, c);
return true
}
if (c.hasClass("required")) {
if (c.get("tag") == "fieldset"
&& (c.hasClass("radio") || c.hasClass("checkboxes"))) {
for ( var a = 0;; a++) {
if (document.id(c.get("id") + a)) {
if (document.id(c.get("id") + a).checked) {
break
}
} else {
this.handleResponse(false, c);
return false
}
}
} else {
if (!(c.get("value"))) {
this.handleResponse(false, c);
return false
}
}
}
var b = (c.className && c.className
.search(/validate-([a-zA-Z0-9\_\-]+)/) != -1) ? c.className
.match(/validate-([a-zA-Z0-9\_\-]+)/)[1]
: "";
if (b == "") {
this.handleResponse(true, c);
return true
}
if ((b) && (b != "none") && (this.handlers[b])
&& c.get("value")) {
if (this.handlers[b].exec(c.get("value")) != true) {
this.handleResponse(false, c);
return false
}
}
this.handleResponse(true, c);
return true
},
isValid : function(c) {
var b = true;
var d = c.getElements("fieldset").concat($A(c.elements));
for ( var a = 0; a < d.length; a++) {
if (this.validate(d[a]) == false) {
b = false
}
}
new Hash(this.custom).each(function(e) {
if (e.exec() != true) {
b = false
}
});
return b
},
handleResponse : function(b, a) {
if (!(a.labelref)) {
var c = $$("label");
c.each(function(d) {
if (d.get("for") == a.get("id")) {
a.labelref = d
}
})
}
if (b == false) {
a.addClass("invalid");
a.set("aria-invalid", "true");
if (a.labelref) {
document.id(a.labelref).addClass("invalid");
document.id(a.labelref).set("aria-invalid", "true");
}
} else {
a.removeClass("invalid");
a.set("aria-invalid", "false");
if (a.labelref) {
document.id(a.labelref).removeClass("invalid");
document.id(a.labelref).set("aria-invalid", "false");
}
}
}
});
document.formvalidator = null;
window.addEvent("domready", function() {
document.formvalidator = new JFormValidator()
});
Where would I edit the code as some of you have answered below?
with jquery js library, scroll to element (id selector or class)
<p class="error">There was a problem with this element.</p>
This gets passed to the ScrollTo plugin in the following way.
$.scrollTo($('p.error:1'));
see source
Using jQuery's .each, loop over the fields. On every iteration the item that is being invesitigated will be under the this variable.
Therefore, this.id gives the id of the element you're looking for. Store these to collect all the incorrect fields, then highlight them or print their names in a message.
Keep in mind, this is the basic idea, I cannot give an actual answer until you show the code that handles the form.
Kind regards,
D.
You can have your isValid routine return the error message instead of returning a boolean.
In isValid, you can build up the error message to include the field names with errors.
Instead of checking "valid == true", you will check "errorMessage.length == 0".
If you want to focus on an error field (you can only focus on one), then do that in the isValid routine as well.
function isValid(f) {
var errorMessage = "";
var errorFields = "";
var isFocused = false;
...
if (field has an error) {
errorFields += " " + field.name;
if (!isFocused) {
field.focus();
isFocused = true;
}
}
...
if (errorFields.length > 0) {
errorMessage = "Errors in fields: " + errorFields;
}
return (errorMessage);
}
then, in your calling routine:
var errorMessage = isValid(f);
if (flag == 0 || errorMessage.length == 0) {
f.check.value='<?php echo JUtility::getToken(); ?>';//send token
}
else {
alert(errorMessage);
return false;
}
return true;

How to remove a property from an item which was added using jquery

The following jquery I am using in my jsp page for adding an autocomplete option to a text field which is having an id mytextfield.
jQuery(function(){
$("#mytextfield").autocomplete("popuppages/listall.jsp");
});
Within the same page, there are some cases in which I will have to remove this autocomplete feature from this text field. ( That is the same field will have to act as a textfield without autocomplete based on the user's inputs to previous fields and options)
Is there any way so that I could remove this newly added 'autocomplete' property from the particular item, that is from $("#mytextfield").
What actually I want to know is is there any option for removing added property
Incase anyone want to refer that autocomplete code, I have attached it below..
;(function($) {
$.fn.extend({
autocomplete: function(urlOrData, options) {
var isUrl = typeof urlOrData == "string";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
max: options && !options.scroll ? 10 : 150
}, options);
// if highlight is set to false, replace it with a do-nothing function
options.highlight = options.highlight || function(value) { return value; };
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
options.formatMatch = options.formatMatch || options.formatItem;
return this.each(function() {
new $.Autocompleter(this, options);
});
},
result: function(handler) {
return this.bind("result", handler);
},
search: function(handler) {
return this.trigger("search", [handler]);
},
flushCache: function() {
return this.trigger("flushCache");
},
setOptions: function(options){
return this.trigger("setOptions", [options]);
},
unautocomplete: function() {
return this.trigger("unautocomplete");
}
});
$.Autocompleter = function(input, options) {
var KEY = {
UP: 38,
DOWN: 40,
DEL: 46,
TAB: 9,
RETURN: 13,
ESC: 27,
COMMA: 188,
PAGEUP: 33,
PAGEDOWN: 34,
BACKSPACE: 8
};
// Create $ object for input element
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
var timeout;
var previousValue = "";
var cache = $.Autocompleter.Cache(options);
var hasFocus = 0;
var lastKeyPressCode;
var config = {
mouseDownOnSelect: false
};
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
var blockSubmit;
// prevent form submit in opera when selecting with return key
$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
if (blockSubmit) {
blockSubmit = false;
return false;
}
});
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
// a keypress means the input has focus
// avoids issue where input had focus before the autocomplete was applied
hasFocus = 1;
// track last key pressed
lastKeyPressCode = event.keyCode;
switch(event.keyCode) {
case KEY.UP:
event.preventDefault();
if ( select.visible() ) {
select.prev();
} else {
onChange(0, true);
}
break;
case KEY.DOWN:
event.preventDefault();
if ( select.visible() ) {
select.next();
} else {
onChange(0, true);
}
break;
case KEY.PAGEUP:
event.preventDefault();
if ( select.visible() ) {
select.pageUp();
} else {
onChange(0, true);
}
break;
case KEY.PAGEDOWN:
event.preventDefault();
if ( select.visible() ) {
select.pageDown();
} else {
onChange(0, true);
}
break;
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
case KEY.RETURN:
if( selectCurrent() ) {
// stop default to prevent a form submit, Opera needs special handling
event.preventDefault();
blockSubmit = true;
return false;
}
break;
case KEY.ESC:
select.hide();
break;
default:
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
break;
}
}).focus(function(){
// track whether the field has focus, we shouldn't process any
// results if the field no longer has focus
hasFocus++;
}).blur(function() {
hasFocus = 0;
if (!config.mouseDownOnSelect) {
hideResults();
}
}).click(function() {
// show select when clicking in a focused field
if ( hasFocus++ > 1 && !select.visible() ) {
onChange(0, true);
}
}).bind("search", function() {
// TODO why not just specifying both arguments?
var fn = (arguments.length > 1) ? arguments[1] : null;
function findValueCallback(q, data) {
var result;
if( data && data.length ) {
for (var i=0; i < data.length; i++) {
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
result = data[i];
break;
}
}
}
if( typeof fn == "function" ) fn(result);
else $input.trigger("result", result && [result.data, result.value]);
}
$.each(trimWords($input.val()), function(i, value) {
request(value, findValueCallback, findValueCallback);
});
}).bind("flushCache", function() {
cache.flush();
}).bind("setOptions", function() {
$.extend(options, arguments[1]);
// if we've updated the data, repopulate
if ( "data" in arguments[1] )
cache.populate();
}).bind("unautocomplete", function() {
select.unbind();
$input.unbind();
$(input.form).unbind(".autocomplete");
});
function selectCurrent() {
var selected = select.selected();
if( !selected )
return false;
var v = selected.result;
previousValue = v;
if ( options.multiple ) {
var words = trimWords($input.val());
if ( words.length > 1 ) {
var seperator = options.multipleSeparator.length;
var cursorAt = $(input).selection().start;
var wordAt, progress = 0;
$.each(words, function(i, word) {
progress += word.length;
if (cursorAt <= progress) {
wordAt = i;
return false;
}
progress += seperator;
});
words[wordAt] = v;
// TODO this should set the cursor to the right position, but it gets overriden somewhere
//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
v = words.join( options.multipleSeparator );
}
v += options.multipleSeparator;
}
$input.val(v);
hideResultsNow();
$input.trigger("result", [selected.data, selected.value]);
return true;
}
function onChange(crap, skipPrevCheck) {
if( lastKeyPressCode == KEY.DEL ) {
select.hide();
return;
}
var currentValue = $input.val();
if ( !skipPrevCheck && currentValue == previousValue )
return;
previousValue = currentValue;
currentValue = lastWord(currentValue);
if ( currentValue.length >= options.minChars) {
$input.addClass(options.loadingClass);
if (!options.matchCase)
currentValue = currentValue.toLowerCase();
request(currentValue, receiveData, hideResultsNow);
} else {
stopLoading();
select.hide();
}
};
function trimWords(value) {
if (!value)
return [""];
if (!options.multiple)
return [$.trim(value)];
return $.map(value.split(options.multipleSeparator), function(word) {
return $.trim(value).length ? $.trim(word) : null;
});
}
function lastWord(value) {
if ( !options.multiple )
return value;
var words = trimWords(value);
if (words.length == 1)
return words[0];
var cursorAt = $(input).selection().start;
if (cursorAt == value.length) {
words = trimWords(value)
} else {
words = trimWords(value.replace(value.substring(cursorAt), ""));
}
return words[words.length - 1];
}
// fills in the input box w/the first match (assumed to be the best match)
// q: the term entered
// sValue: the first matching result
function autoFill(q, sValue){
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
// if the last user key pressed was backspace, don't autofill
if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
// select the portion of the value not typed by the user (so the next character will erase)
$(input).selection(previousValue.length, previousValue.length + sValue.length);
}
};
function hideResults() {
clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
var wasVisible = select.visible();
select.hide();
clearTimeout(timeout);
stopLoading();
if (options.mustMatch) {
// call search and run callback
$input.search(
function (result){
// if no value found, clear the input box
if( !result ) {
if (options.multiple) {
var words = trimWords($input.val()).slice(0, -1);
$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
}
else {
$input.val( "" );
$input.trigger("result", null);
}
}
}
);
}
};
function receiveData(q, data) {
if ( data && data.length && hasFocus ) {
stopLoading();
select.display(data, q);
autoFill(q, data[0].value);
select.show();
} else {
hideResultsNow();
}
};
function request(term, success, failure) {
if (!options.matchCase)
term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
var extraParams = {
timestamp: +new Date()
};
$.each(options.extraParams, function(key, param) {
extraParams[key] = typeof param == "function" ? param() : param;
});
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
url: options.url,
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
success: function(data) {
var parsed = options.parse && options.parse(data) || parse(data);
cache.add(term, parsed);
success(term, parsed);
}
});
} else {
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
select.emptyList();
failure(term);
}
};
function parse(data) {
var parsed = [];
var rows = data.split("\n");
for (var i=0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
row = row.split("|");
parsed[parsed.length] = {
data: row,
value: row[0],
result: options.formatResult && options.formatResult(row, row[0]) || row[0]
};
}
}
return parsed;
};
function stopLoading() {
$input.removeClass(options.loadingClass);
};
};
$.Autocompleter.defaults = {
inputClass: "ac_input",
resultsClass: "ac_results",
loadingClass: "ac_loading",
minChars: 1,
delay: 400,
matchCase: false,
matchSubset: true,
matchContains: false,
cacheLength: 10,
max: 100,
mustMatch: false,
extraParams: {},
selectFirst: true,
formatItem: function(row) { return row[0]; },
formatMatch: null,
autoFill: false,
width: 0,
multiple: false,
multipleSeparator: ", ",
highlight: function(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
},
scroll: true,
scrollHeight: 180
};
$.Autocompleter.Cache = function(options) {
var data = {};
var length = 0;
function matchSubset(s, sub) {
if (!options.matchCase)
s = s.toLowerCase();
var i = s.indexOf(sub);
if (options.matchContains == "word"){
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
}
if (i == -1) return false;
return i == 0 || options.matchContains;
};
function add(q, value) {
if (length > options.cacheLength){
flush();
}
if (!data[q]){
length++;
}
data[q] = value;
}
function populate(){
if( !options.data ) return false;
// track the matches
var stMatchSets = {},
nullData = 0;
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if( !options.url ) options.cacheLength = 1;
// track all options for minChars = 0
stMatchSets[""] = [];
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
var rawValue = options.data[i];
// if rawValue is a string, make an array otherwise just reference the array
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
var value = options.formatMatch(rawValue, i+1, options.data.length);
if ( value === false )
continue;
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
stMatchSets[firstChar] = [];
// if the match is a string
var row = {
value: value,
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
// push the current match into the set list
stMatchSets[firstChar].push(row);
// keep track of minChars zero items
if ( nullData++ < options.max ) {
stMatchSets[""].push(row);
}
};
// add the data items to the cache
$.each(stMatchSets, function(i, value) {
// increase the cache size
options.cacheLength++;
// add to the cache
add(i, value);
});
}
// populate any existing data
setTimeout(populate, 25);
function flush(){
data = {};
length = 0;
}
return {
flush: flush,
add: add,
populate: populate,
load: function(q) {
if (!options.cacheLength || !length)
return null;
/*
* if dealing w/local data and matchContains than we must make sure
* to loop through all the data collections looking for matches
*/
if( !options.url && options.matchContains ){
// track all matches
var csub = [];
// loop through all the data grids for matches
for( var k in data ){
// don't search through the stMatchSets[""] (minChars: 0) cache
// this prevents duplicates
if( k.length > 0 ){
var c = data[k];
$.each(c, function(i, x) {
// if we've got a match, add it to the array
if (matchSubset(x.value, q)) {
csub.push(x);
}
});
}
}
return csub;
} else
// if the exact item exists, use it
if (data[q]){
return data[q];
} else
if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var c = data[q.substr(0, i)];
if (c) {
var csub = [];
$.each(c, function(i, x) {
if (matchSubset(x.value, q)) {
csub[csub.length] = x;
}
});
return csub;
}
}
}
return null;
}
};
};
$.Autocompleter.Select = function (options, input, select, config) {
var CLASSES = {
ACTIVE: "ac_over"
};
var listItems,
active = -1,
data,
term = "",
needsInit = true,
element,
list;
// Create results
function init() {
if (!needsInit)
return;
element = $("<div/>")
.hide()
.addClass(options.resultsClass)
.css("position", "absolute")
.appendTo(document.body);
list = $("<ul/>").appendTo(element).mouseover( function(event) {
if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
$(target(event)).addClass(CLASSES.ACTIVE);
}
}).click(function(event) {
$(target(event)).addClass(CLASSES.ACTIVE);
select();
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
input.focus();
return false;
}).mousedown(function() {
config.mouseDownOnSelect = true;
}).mouseup(function() {
config.mouseDownOnSelect = false;
});
if( options.width > 0 )
element.css("width", options.width);
needsInit = false;
}
function target(event) {
var element = event.target;
while(element && element.tagName != "LI")
element = element.parentNode;
// more fun with IE, sometimes event.target is empty, just ignore it then
if(!element)
return [];
return element;
}
function moveSelect(step) {
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if(options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function() {
offset += this.offsetHeight;
});
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if(offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
};
function movePosition(step) {
active += step;
if (active < 0) {
active = listItems.size() - 1;
} else if (active >= listItems.size()) {
active = 0;
}
}
function limitNumberOfItems(available) {
return options.max && options.max < available
? options.max
: available;
}
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
for (var i=0; i < max; i++) {
if (!data[i])
continue;
var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
if ( formatted === false )
continue;
var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
$.data(li, "ac_data", data[i]);
}
listItems = list.find("li");
if ( options.selectFirst ) {
listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
active = 0;
}
// apply bgiframe if available
if ( $.fn.bgiframe )
list.bgiframe();
}
return {
display: function(d, q) {
init();
data = d;
term = q;
fillList();
},
next: function() {
moveSelect(1);
},
prev: function() {
moveSelect(-1);
},
pageUp: function() {
if (active != 0 && active - 8 < 0) {
moveSelect( -active );
} else {
moveSelect(-8);
}
},
pageDown: function() {
if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
moveSelect( listItems.size() - 1 - active );
} else {
moveSelect(8);
}
},
hide: function() {
element && element.hide();
listItems && listItems.removeClass(CLASSES.ACTIVE);
active = -1;
},
visible : function() {
return element && element.is(":visible");
},
current: function() {
return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
},
show: function() {
var offset = $(input).offset();
element.css({
width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
top: offset.top + input.offsetHeight,
left: offset.left
}).show();
if(options.scroll) {
list.scrollTop(0);
list.css({
maxHeight: options.scrollHeight,
overflow: 'auto'
});
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
var listHeight = 0;
listItems.each(function() {
listHeight += this.offsetHeight;
});
var scrollbarsVisible = listHeight > options.scrollHeight;
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
if (!scrollbarsVisible) {
// IE doesn't recalculate width when scrollbar disappears
listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
}
}
}
},
selected: function() {
var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
return selected && selected.length && $.data(selected[0], "ac_data");
},
emptyList: function (){
list && list.empty();
},
unbind: function() {
element && element.remove();
}
};
};
$.fn.selection = function(start, end) {
if (start !== undefined) {
return this.each(function() {
if( this.createTextRange ){
var selRange = this.createTextRange();
if (end === undefined || start == end) {
selRange.move("character", start);
selRange.select();
} else {
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end);
selRange.select();
}
} else if( this.setSelectionRange ){
this.setSelectionRange(start, end);
} else if( this.selectionStart ){
this.selectionStart = start;
this.selectionEnd = end;
}
});
}
var field = this[0];
if ( field.createTextRange ) {
var range = document.selection.createRange(),
orig = field.value,
teststring = "<->",
textLength = range.text.length;
range.text = teststring;
var caretAt = field.value.indexOf(teststring);
field.value = orig;
this.selection(caretAt, caretAt + textLength);
return {
start: caretAt,
end: caretAt + textLength
}
} else if( field.selectionStart !== undefined ){
return {
start: field.selectionStart,
end: field.selectionEnd
}
}
};
})(jQuery);
Looks like you are looking for destroy or disable method of autocomplete..
Check Documentation...
destroy
disable
$("#mytextfield").autocomplete( "destroy" )
$("#mytextfield").autocomplete( "disable" )
The difference is after destroy you cannot enable it back...but after disable by using enable you can enable it back..
You can use .removeAttr()
$(target).removeAttr('propertyName');
This will totally remove that property.
But if you want to change any property then use .prop() or .attr()

Any Good Number Picker for JQuery (or Javascript)?

Are there any good number picker for jquery (or standalone js)?
I would like a number picker where there is a max and min number that the user can choose from. Also, it have other options such as displaying odd number or even number or prime number or a range of number whereby some numbers in between are skipped.
Using a select to do this you can create an array with the numbers to skip and do a for loop to write the options:
int minNumber = 0;
int maxNumber = 10;
int[] skipThese = { 5, 7 };
for (int i = minNumber; i <= maxNumber; i++)
{
if(!skipThese.Contains(i)) Response.Write(String.Concat("<option value=\"", i, "\">", i, "</option>"));
}
You can do this with razor or any other way to output the HTML.
You can also do this with jQuery, dynamicaly, following the same idea:
$(document).ready(function() {
var minNumber = 0;
var maxNumber = 10;
var skipThese = [5, 7];
for (var i = minNumber; i <= maxNumber; i++) {
if ($.inArray(i, skipThese) == -1) $('#selectListID').append("<option value=\"" + i + "\">" + i + "</option>");
}
});
Edit:
Or you can use the C# code above in an aspx page and load it with AJAX from the page:
Create a select box in the page:
<select name="numPicker" id="numPicker">
<option>Loading...</option>
</select>
In a script in this page you could use jQuery's ajax() to fetch the data and populate the <select>:
$(document).ready(function() {
var numPickerSelect = $("#numPicker");
$.ajax({
url: 'url/to/page.aspx',
type: 'post'
success: function(data) {
numPickerSelect.find('option').remove(); // Remove the options in the select field
numPickerSelect.append(data); // Load the content generated by the server into the select field
},
error: function() {
alert('An error has ocurred!');
}
});
//Or use this (not sure if will work)
numPickerSelect.load("url/to/page.aspx");
});
I have used this. You should be able to modify to add extra options such as min and max fairly easily.
// Make a control only accept numeric input
// eg, $("#myedit").numeric()
// $("#myedit").numeric({alow: ' ,.'})
// $("#myedit").numeric({decimals: 2})
(function($) {
$.fn.alphanumeric = function(p) {
if (p == 'destroy') {
$(this).unbind('keypress');
$(this).unbind('blur');
return;
}
p = $.extend({
ichars: "!##$%^&*()+=[]\\\';,/{}|\":<>?~`.- ",
nchars: "",
allow: "",
decimals: null
}, p);
return this.each
(
function() {
if (p.nocaps) p.nchars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (p.allcaps) p.nchars += "abcdefghijklmnopqrstuvwxyz";
s = p.allow.split('');
for (i = 0; i < s.length; i++) if (p.ichars.indexOf(s[i]) != -1) s[i] = "\\" + s[i];
p.allow = s.join('|');
var reg = new RegExp(p.allow, 'gi');
var ch = p.ichars + p.nchars;
ch = ch.replace(reg, '');
var dp = p.decimals;
var isInteger = function(val) {
var objRegExp = /(^-?\d\d*$)/;
return objRegExp.test(val);
};
var isNumeric = function(val) {
// If the last digit is a . then add a 0 before testing so if they type 25. it will be accepted
var lastChar = val.substring(val.length - 1);
if (lastChar == ".") val = val + "0";
var objRegExp = new RegExp("^\\s*-?(\\d+(\\.\\d{1," + dp + "})?|\\.\\d{1," + dp + "})\\s*$", "g");
if (dp == -1)
objRegExp = new RegExp("^\\s*-?(\\d+(\\.\\d{1,25})?|\\.\\d{1,25})\\s*$", "g");
var result = objRegExp.test(val);
return result;
};
$(this).blur(function(e) {
var text = $(this).val();
if (dp != null) {
if (dp == 0) {
if (!isInteger(text)) {
$(this).val('');
e.preventDefault();
}
}
else {
if (!isNumeric(text)) {
$(this).val('');
e.preventDefault();
}
}
} else {
var c = text.split('')
for (i = 0; i < text.length; i++) {
if (ch.indexOf(c[i]) != -1) {
$(this).val('');
e.preventDefault();
};
}
}
});
$(this).keypress
(
function(e) {
switch (e.which) {
//Firefox fix, for ignoring specific presses
case 8: // backspace key
return true;
case 46: // delete key
return true;
};
if (dp != null) {
if (e.which == 32) { e.preventDefault(); return false; }
var range = getRange(this);
var typed = String.fromCharCode(e.which);
var text = $(this).val().substr(0, range.start) + typed + $(this).val().substr(range.start);
if (dp == 0) {
if (!isInteger(text)) e.preventDefault();
}
else {
if (!isNumeric(text)) e.preventDefault();
}
return;
}
if (!e.charCode) k = String.fromCharCode(e.which);
else k = String.fromCharCode(e.charCode);
if (ch.indexOf(k) != -1) e.preventDefault();
if (e.ctrlKey && k == 'v') e.preventDefault();
}
);
$(this).bind('contextmenu', function() { return false });
}
);
};
$.fn.numeric = function(p) {
if (p == 'destroy') {
$(this).unbind('keypress');
$(this).unbind('blur');
return;
}
var az = "abcdefghijklmnopqrstuvwxyz";
az += az.toUpperCase();
var opts = {};
if (!isNaN(p)) {
opts = $.extend({
nchars: az
}, { decimals: p });
} else {
opts = $.extend({
nchars: az
}, p);
}
return this.each(function() {
$(this).alphanumeric(opts);
}
);
};
$.fn.integer = function(p) {
if (p == 'destroy') {
$(this).unbind('keypress');
$(this).unbind('blur');
return;
}
var az = "abcdefghijklmnopqrstuvwxyz";
az += az.toUpperCase();
p = {
nchars: az,
allow: '-',
decimals: 0
};
return this.each(function() {
$(this).alphanumeric(p);
}
);
};
$.fn.alpha = function(p) {
if (p == 'destroy') {
$(this).unbind('keypress');
$(this).unbind('blur');
return;
}
var nm = "1234567890";
p = $.extend({
nchars: nm
}, p);
return this.each(function() {
$(this).alphanumeric(p);
}
);
};
})(jQuery);

Categories

Resources