Hi Stackoverflow Community,
I've got the following problem and I can't handle it myself:
I've got the following search form:
<li class="list-group-item active" data-ignore-list-search="ignore"><?php echo $translate->_('Animals'); ?><span class="icon-search pull-right" rel="SearchBox" data-placement="bottom" data-container="body"></span>
<div id="popover_content_wrapper" class="hide">
<input placeholder="<?php echo $translate->_('Search'); ?> " id="FilterForm" class="filterform filterinput" type="text">
</div>
</li>
And I'm using following jQuery code to filter ul items:
(function(jQuery){
var ListSearch = function() {};
ListSearch.prototype = {
/**
* run
* start incremental search.
* #param {} input
* #param {} target
* #returns {}
*/
run: function(input, target){
var _this = this;
var tagName = target.get(0).tagName;
input.on('keyup', function(e){
text = _this.getInputText(input);
switch(tagName){
case 'TABLE':
_this.listSearchTable(target, text);
break;
case 'UL':
_this.listSearchListUl(target, text);
break;
case 'OL':
_this.listSearchListOl(target, text);
break;
case 'DL':
_this.listSearchListDl(target, text);
break;
default:
throw new Error('Illegal tag name ' + targetObject.targetTagName);
}
});
},
getInputText: function(input){
return input.val();
},
/**
* tag table
*/
listSearchTable: function(target, text){
this.listSearchCommon('tr', target, text);
},
/**
* tag ul
*/
listSearchListUl: function(target, text){
this.listSearchCommon('li', target, text);
},
/**
* tag ol
*/
listSearchListOl: function(target, text){
return this.listSearchListUl(target, text);
},
/**
* tag dl
*/
listSearchListDl: function(target, text){
this.listSearchCommon('dd dt', target, text);
},
/**
* commondSearchList
*/
listSearchCommon: function(tagName ,target, text){
var _this = this;
var searchWords = this.searchWordToWords(text);
var wordLength = searchWords.length;
target.find(tagName).each(function(){
var thisJQuery = jQuery(this);
if (thisJQuery.data('ignore-list-search') === 'ignore') return true;
var body = _this.getBody(thisJQuery);
var displayFlag = true;
var wordCount = 0;
for(wordCount = 0; wordCount < wordLength; wordCount++){
var word = searchWords[wordCount];
var pattern = new RegExp(word, 'i');
if ( !body.match(pattern) ) {
displayFlag = false;
break;
}
}
jQuery(this).css('display', _this.getDisplayProperty(tagName, displayFlag));
return true;
})
},
/**
* getDisplayProperty
* #param {} tagName
* #param {} flag
* #returns {}
*/
getDisplayProperty: function(tagName, flag){
switch(tagName){
case 'tr':
return flag?'table-row':'none';
case 'li':
return flag?'block':'none';
case 'dd dt':
return flag?'list-item':'none';
default:
throw new Error('Illegal tag name ' + targetObject.targetTagName);
}
},
/**
* getBody
* #returns {}
*/
getBody: function(target){
var body = target.text();
return jQuery.trim(body);
},
/**
* searchWordToWords
* a search text split to search words.
* #param {} body
* #param {} searchWord
* #returns {}
*/
searchWordToWords: function(text){
text = jQuery.trim(text);
var pattern = new RegExp(/[ \-\/]/);
var words = text.split(pattern);
// delete empty element
var newWords = new Array();
var wordsLength = words.length;
var wordsCount = 0;
for (wordsCount = 0; wordsCount < wordsLength; wordsCount++){
var word = words[wordsCount];
if (word != ""){
newWords.push(words[wordsCount]);
}
}
return newWords;
}
}
/**
* Main stream
*/
jQuery.fn.listSearch = function(input, options){
var options = jQuery.extend(jQuery.fn.listSearch.defaults, options);
// set using objects.
var target = jQuery(this);
switch (jQuery.type(input)){
case 'string':
input = jQuery(input);
break;
case 'object':
input = input;
break;
default:
throw 'Argiment value "' + input + '" is invalid.';
}
// Event start
listSearch = new ListSearch();
listSearch.run(input, target);
return target;
};
/**
* default settings.
*/
jQuery.fn.listSearch.defaults = {
};
$('#AnimalList').listSearch('#FilterForm')
;
})(jQuery);
This works fine .. but if I put the #FilterForm input into the popover it doesn't filter out my uls anymore.
My Popover jQuery code:
$('[rel=SearchBox]').popover({
html : true,
content: function() {
return $('#popover_content_wrapper').html();
},
trigger: 'click'
});
my UL hasn't any special tags or anything.
Thanks in advance and sorry for my weird English [Ed - fixed]: I am from Germany.
Greetings,
Daniel
#user2908086, Its because you are using the same "filterform" text twice in the same input field but in different Types! Here is what you use: id="FilterForm" + class="filterform"! Locate the "filterform" class text in your CSS and rename the "filterform" and make sure its not the same as your ID. Then add the same class name back in your input and see if that will work for you! Good luck!
Related
I have used the following code so I can add multiple parameter to a url
<script>
function setParam(name, value) {
var l = window.location;
/* build params */
var params = {};
var x = /(?:\??)([^=&?]+)=?([^&?]*)/g;
var s = l.search;
for(var r = x.exec(s); r; r = x.exec(s))
{
r[1] = decodeURIComponent(r[1]);
if (!r[2]) r[2] = '%%';
params[r[1]] = r[2];
}
/* set param */
params[name] = encodeURIComponent(value);
/* build search */
var search = [];
for(var i in params)
{
var p = encodeURIComponent(i);
var v = params[i];
if (v != '%%') p += '=' + v;
search.push(p);
}
search = search.join('&');
/* execute search */
l.search = search;
}
</script>
add priceMin=300
add priceMin=600
add MaxDistance=300
This is taken from question: How to add a parameter to the URL?
However what additional script would need to be added so if you clicked on the same hyperlink again it would remove the parameter in the url? In this case being '?priceMin=300'
Javascript already have URL objects.you can append the param if you want something
like this.
const newUrl = new URL(window.location.href);
function setParam(name, value, option = 'add') {
var paramexist = newUrl.searchParams.has(name);
if (option == 'remove') {
newUrl.searchParams.delete(name);
return newUrl;
}
(!paramexist) ? newUrl.searchParams.append(name, value) : newUrl.searchParams.set(name, value);
return newUrl;
}
console.log(setParam('priceMin', 300));
console.log(setParam('priceMin', 300,'remove'))
Hero Qu's answer will reset the entire list of parameters if you have multiple parameters.
The way to do this is to check if that specific parameter exists then delete it, otherwise add it.
I modified your code below to account for multiple parameter.
<script>
function setParam(name, value) {
var l = window.location;
/* build params */
var params = {};
var x = /(?:\??)([^=&?]+)=?([^&?]*)/g;
var s = l.search;
for (var r = x.exec(s); r; r = x.exec(s)) {
r[1] = decodeURIComponent(r[1]);
if (!r[2]) r[2] = '%%';
params[r[1]] = r[2];
}
/** Check to see if the param exist already
Delete if it exist, set it, if it doesn't
**/
if (params[name] && value == params[name]) {
delete params[name];
} else if (params[name] && value != params[name]) {
delete params[name];
params[name] = encodeURIComponent(value);
} else {
params[name] = encodeURIComponent(value);
}
/* set param */
/* build search */
var search = [];
for (var i in params) {
var p = encodeURIComponent(i);
var v = params[i];
if (v != '%%') p += '=' + v;
search.push(p);
}
search = search.join('&');
/* execute search */``
l.search = search;
}
</script>
add priceMin=200
<br />
add priceMin=300
<br />
add priceMax=400
you can try with adding an if block that would clear the search if it is already there OR allow to add param if the search is empty:
function setParam(name, value) {
var l = window.location;
if (l.search) {
l.search = ''
return
}
...
}
My Problem
I am trying to remove a specific item from my array, however, my array contains other objects which I cannot get a handle to.
I am defining a "Station" like this:
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
So I added a station to my array by using this function ...
.
.
self.addStation(new Station(76, "Receiving", 0, 10));
Now, I want to know how to remove from array by passing the name, as in:
self.removeStation("Receiving");
I can't figure it out. I have researched all the links on here, with no luck.
Entire Source Code
// CLASS Workflow
var Workflow = function(id, status){
this.status = status || false;
this.id = id;
}
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.station = ko.observableArray();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status=false) {
var id;
if(!workFlowId){
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station){
self.station.push(station);
}
/* Remove Station - */
self.removeStation = function (Name) {
for( var i = 0; i < self.station().length; i++){
console.dir("In Remove Function: " + self.station()[i]);
}
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function(){
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStation(new Station(76, "Receiving", 0, 10));
// 2. Remove
self.removeStation("Receiving");
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel();
// Away we go...
ko.applyBindings(window.view_model);
I can't seem to get a hold of the name in the array:
// DON'T WORK
self.station()[i].Station.name
Thanks for looking.
John
u could use function to find its index look like:
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
then in your /* Remove Station - */u edit code look like this:
/* Remove Station - */
self.removeStation = function (Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name === Name;
});
index > -1 && self.station.splice(index, 1);
}
Hope this help !
you can use ko.utils.arrayRemoveItem to remove an item from the array you need to find it first
/* Remove Station */
self.removeStation = function (search) {
// find the station
var foundStation = ko.utils.arrayFirst(this.station, function(item) {
return ko.utils.stringStartsWith(item.name().toLowerCase(), search);
});
// remove the station
ko.utils.arrayRemoveItem(this.station, foundStation);
}
Thank you for your responses. But I found an acceptable solution to my problem by using this following code:
/* Remove Station */
self.removeStation = function (Name) {
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = item.name();
if(value==Name){
console.log("found it! at index " + c);
self.station().splice(c,1);
}
});
// Force the UI to update
self.station.valueHasMutated();
}
I realize this is not the cleanest or most efficient solution, but it seems to work. I would love to know how to optimize this, but that's above my pay grade, lol.
sry for long days answer, i think the problem you got is about variable you declared and variable you use not the same type, and you not use any convert of them when u assigned to your object look like:
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId()); //observalbe declared
this.name = ko.observable(name);//observalbe declared
this.posX = ko.observable(posX);//observalbe declared
this.posY = ko.observable(posY);//observalbe declared
};
but then you use assign look like
var newObj = {
id: "82",
name: "Updated Name",
posX: 92,
posY: 88
}
self.modifyStation("name","Shipping",newObj);
when u assign it:
objNewItem.id = oldId; // Put old ID back in
self.station.push(objNewItem); //not convert its properties to observable will make some errors on binding html.
i make a working jsfildle with your example, please read it, and reply when u not understand which part. hope this help.
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
// CLASS Workflow
var Workflow = function(id, status) {
this.status = status || false;
this.id = id;
}
/* CLASS Field
* #param id int unique id this station
* #param fieldType str type of input
* #param fieldName obj name of the input
* #param options array options array
*/
var Field = function(fieldId, fieldType, fieldName, options) {
this.fieldId = ko.observable(fieldId);
this.fieldType = ko.observable(fieldType);
this.fieldName = ko.observable(fieldName);
this.options = ko.observableArray(options);
};
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX = null, posY = null, fields) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
this.fields = ko.observableArray(fields || []);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.fieldId = ko.observable();
self.fieldName = ko.observable();
self.fieldType = ko.observable();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
self.station = ko.observableArray();
self.fields = ko.observableArray();
/*******************************
* Computed Observables
*-----------------------------*/
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status = false) {
var id;
if (!workFlowId) {
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station) {
self.station.push(station);
}
/* Remove Station */
self.removeStation = function(Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name() === Name;
});
index > -1 && self.station.splice(index, 1);
}
/* Update A Station ** NEEDS FIXING **
*
* #param findBy string Property to find ( "id" or "name")
* #param cmpVal string Value to compare against
* #param objNewItem object The new object replacing old
* */
self.modifyStation = function(findBy, cmpVal, objNewItem) {
var sourceIndex;
var oldId;
var found = false;
/* Find Index Of Old Station */
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
switch (findBy) {
case "id":
var value = ko.unwrap(item.id);
if (value == cmpVal) {
sourceIndex = c;
oldId = value;
found = true;
}
break;
case "name":
var value = ko.unwrap(item.name);
if (value == cmpVal) {
sourceIndex = c;
oldId = ko.unwrap(item.id);
found = true;
}
break;
}
});
/* Remove Old */
if (found === true) {
self.station().splice(sourceIndex, 1);
/* Insert New Station
* [For Now] not allowing updating of ID. Only
* can update the other properties (yes, I realize that
* only leaves "name", but more will be added )
*/
objNewItem.id(oldId); // Put old ID back in
self.station.push(objNewItem);
} else {
alert(cmpVal + " was not found in array!");
}
}
self.addField = function(stationId, newField) {
var c = -1;
found = false;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = ko.unwrap(item.id);
console.log(value, c);
if (value == stationId) {
//console.log("found it! at index " + c);
self.station()[c].fields.push(newField);
}
});
self.station.valueHasMutated();
};
self.modifyField = function(stationId, oldFieldId, newObjField) {
// TO DO
};
self.removeField = function(field) {
self.fields.remove(field);
};
/* Perform Test On Button Click */
self.doTest = function() {
self.removeStation("Shipping");
}
self.doTest2 = function() {
var newObj = {
id: ko.observable("82"),
name: ko.observable("Updated Name"),
posX: ko.observable(92),
posY: ko.observable(88),
fields: ko.observableArray([])
}
self.modifyStation("name", "Shipping", newObj);
self.station.valueHasMutated();
}
// Add Fields
self.doTest3 = function() {
var objNewField = {
fieldId: 456,
fieldName: "Last Name",
fieldType: "Text"
}
self.addField(86, objNewField);
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function() {
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/*-------------------------------------------------------
* Method: debugAll
*
* Desc: Console Logs Our View Model
*--------------------------------------------------------*/
this.debugAll = function() {
console.group("Debug:");
// Workflow
console.group("Current Workflow Id")
console.log("self.workflowId = " + self.workflowId);
console.groupEnd("Current Workflow Id")
// Stations
console.group("Stations In This Workflow")
console.table(self.station());
console.groupEnd("Stations In This Workflow")
// Fields
console.groupEnd("Debug:");
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStationShipping = function() {
self.addStation(new Station(86, "Shipping", 0, 10, [{
fieldId: 45,
fieldName: "First Name",
fieldType: "Text"
}]));
}
self.addStation(new Station(76, "Receiving", 0, 10, null));
self.addStationShipping();
/* Dump ViewModel */
self.debugAll();
//----------------------------------------------------------------
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel(1213131212);
// Away we go...
ko.applyBindings(window.view_model);
// Page Utility Functions
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<ul data-bind="foreach: station">
<li data-bind="text: 'id: ' + id()"></li>
<li data-bind="text: 'name: ' +name()"></li>
<li data-bind="text: 'posX: ' +posX()"></li>
<li data-bind="text: 'posY: ' +posY()"></li>
<ul data-bind="foreach: fields">
<li data-bind="text: fieldId"></li>
<li data-bind="text: fieldName"></li>
<li data-bind="text: fieldType"></li>
</ul>
</ul>
</div>
<button data-bind="click: doTest">Remove "Shipping" From Station Array</button>
<button data-bind="click: doTest2">Update "Shipping" In Station Array</button>
<hr>
<h3>Test: Add Field To Shipping Station</h3>
<button data-bind="click: function() { addStationShipping()}">Add Shpping</button>
<button data-bind="click: doTest3()">Add Field</button>
I would like to get the publicationdate from a URL, I can get it with the following code: window.dataLayer[0]['publicationdate'], but when it's time to make it work locally, the console gives me the following error "Cannot read property '0' of undefined". I have the following code, can someone please help me? Thank you!
"use strict";
var takashyx = takashyx || {};
takashyx.toast = (function() {
/**
* The main Toast object
* #param {Object} options See Toast.prototype.DEFAULT_SETTINGS for more info
*/
function Toast(text, options) {
if(getToastStage() != null) {
// If there is already a Toast being shown, hide it
Toast.prototype.destroy();
}
var _options = options || {};
_options = Toast.prototype.mergeOptions(Toast.prototype.DEFAULT_SETTINGS, _options);
Toast.prototype.show(text, _options);
_options = null;
};
/**
* The toastStage. This is the HTML element in which the toast resides
* Getter and setter methods are available privately
* #type {Element}
*/
var _toastStage = null;
function getToastStage() {
return _toastStage;
};
function setToastStage(toastStage) {
_toastStage = toastStage;
};
/**
* The Toast animation speed; how long the Toast takes to move to and from the screen
* #type {Number}
*/
Toast.prototype.TOAST_ANIMATION_SPEED = 400;
// Toast classes
Toast.prototype.CLASS_TOAST_GONE = "takashyx_toast_gone";
Toast.prototype.CLASS_TOAST_VISIBLE = "takashyx_toast_visible";
Toast.prototype.CLASS_TOAST_ANIMATED = "takashyx_toast_animated";
/**
* The default Toast settings
* #type {Object}
*/
Toast.prototype.DEFAULT_SETTINGS = {
style: {
main: {
"background": "rgba(0, 0, 0, .85)",
"box-shadow": "0 0 10px rgba(0, 0, 0, .8)",
"border-radius": "3px",
"z-index": "99999",
"color": "#01ff00",
"padding": "10px 15px",
"max-width": "40%",
"word-break": "keep-all",
"margin": "0 auto",
"text-align": "center",
"position": "fixed",
"left": "0",
"right": "0"
}
},
settings: {
duration: 4000
}
};
/**
* Specifies whether or not the inline style in the <head> exists. It only needs to be added once to a page
* #type {Boolean}
*/
Toast.prototype.styleExists = false;
/**
* The Timeout object for animations.
* This should be shared among the Toasts, because timeouts may be cancelled e.g. on explicit call of hide()
* #type {Object}
*/
Toast.prototype.timeout = null;
/**
* Merge the DEFAULT_SETTINGS with the user defined options if specified
* #param {Object} options The user defined options
*/
Toast.prototype.mergeOptions = function(initialOptions, customOptions) {
var merged = customOptions;
for(var prop in initialOptions) {
if(merged.hasOwnProperty(prop)) {
if(initialOptions[prop] != null && initialOptions[prop].constructor == Object) {
merged[prop] = Toast.prototype.mergeOptions(initialOptions[prop], merged[prop]);
}
} else {
merged[prop] = initialOptions[prop];
}
}
return merged;
};
/**
* Add the inline stylesheet to the <head>
* These inline styles are needed for animation purposes.
*/
Toast.prototype.initializeStyles = function() {
if(Toast.prototype.styleExists) return;
var style = document.createElement("style");
style.insertAdjacentHTML("beforeend",
Toast.prototype.generateInlineStylesheetRules(this.CLASS_TOAST_GONE, {
"opacity": "0",
"top": "10%"
}) +
Toast.prototype.generateInlineStylesheetRules(this.CLASS_TOAST_VISIBLE, {
"opacity": "1",
"top": "10%"
}) +
Toast.prototype.generateInlineStylesheetRules(this.CLASS_TOAST_ANIMATED, {
"transition": "opacity " + this.TOAST_ANIMATION_SPEED + "ms, bottom " + this.TOAST_ANIMATION_SPEED + "ms"
})
);
document.head.appendChild(style);
style = null;
// Notify that the stylesheet exists to avoid creating more
Toast.prototype.styleExists = true;
};
/**
* Generate the Toast with the specified text.
* #param {String|Object} text The text to show inside the Toast, can be an HTML element or plain text
* #param {Object} style The style to set for the Toast
*/
Toast.prototype.generate = function(text, style) {
var toastStage = document.createElement("div");
/**
* If the text is a String, create a textNode for appending
*/
if(typeof text === "string") {
var lines = text.split('[[[br]]]');
for (let i=0; i<lines.length; ++i) {
var l = document.createTextNode(lines[i]);
toastStage.appendChild(l);
var r = document.createElement('br');
toastStage.appendChild(r);
}
setToastStage(toastStage);
toastStage = null;
Toast.prototype.stylize(getToastStage(), style);
};
/**
* Stylize the Toast.
* #param {Element} element The HTML element to stylize
* #param {Object} styles An object containing the style to apply
* #return Returns nothing
*/
Toast.prototype.stylize = function(element, styles) {
Object.keys(styles).forEach(function(style) {
element.style[style] = styles[style];
console.log(style + ": "+ styles[style]);
});
};
/**
* Generates styles to be used in an inline stylesheet. The output will be something like:
* .class {background: blue;}
* #param {String} elementClass The class of the element to style
* #param {Object} styles The style to insert into the inline stylsheet
* #return {String} The inline style as a string
*/
Toast.prototype.generateInlineStylesheetRules = function(elementClass, styles) {
var out = "." + elementClass + "{";
Object.keys(styles).forEach(function(style) {
out += style + ":" + styles[style] + ";";
});
out += "}";
return out;
};
/**
* Show the Toast
* #param {String} text The text to show inside the Toast
* #param {Object} options The object containing the options for the Toast
*/
Toast.prototype.show = function(text, options) {
this.initializeStyles();
this.generate(text, options.style.main);
var toastStage = getToastStage();
toastStage.classList.add(this.CLASS_TOAST_ANIMATED);
toastStage.classList.add(this.CLASS_TOAST_GONE);
document.body.insertBefore(toastStage, document.body.firstChild);
// This is a hack to get animations started. Apparently without explicitly redrawing, it'll just attach the class and no animations would be done
toastStage.offsetHeight;
toastStage.classList.remove(this.CLASS_TOAST_GONE);
toastStage.classList.add(this.CLASS_TOAST_VISIBLE);
var toastStage = null;
clearTimeout(Toast.prototype.timeout);
Toast.prototype.timeout = setTimeout(Toast.prototype.hide, options.settings.duration);
};
/**
* Hide the Toast that's currently shown
*/
Toast.prototype.hide = function() {
var toastStage = getToastStage();
toastStage.classList.remove(Toast.prototype.CLASS_TOAST_VISIBLE);
toastStage.classList.add(Toast.prototype.CLASS_TOAST_GONE);
toastStage = null;
// Destroy the Toast element after animations end
clearTimeout(Toast.prototype.timeout);
Toast.prototype.timeout = setTimeout(Toast.prototype.destroy, Toast.prototype.TOAST_ANIMATION_SPEED);
};
/**
* Clean up after the Toast slides away. Namely, removing the Toast from the DOM.
*/
Toast.prototype.destroy = function() {
var toastStage = getToastStage();
document.body.removeChild(toastStage);
toastStage = null;
setToastStage(null);
};
return {
Toast: Toast
};
})();
Using OpenPGP.js v1.3.0 I can successfully create a public/private key pair and encrypt/decrypt ok.
I am struggling to obtain the key id using this function (found in openpgp.js and public_key.js):
/**
* Calculates the key id of the key
* #return {String} A 8 byte key id
*/
PublicKey.prototype.getKeyId = function () {
if (this.keyid) {
return this.keyid;
}
this.keyid = new type_keyid();
if (this.version == 4) {
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
} else if (this.version == 3) {
this.keyid.read(this.mpi[0].write().substr(-8));
}
return this.keyid;
};
PublicKey() also in openpgp.js:
/**
* #constructor
*/
function PublicKey() {
this.tag = enums.packet.publicKey;
this.version = 4;
/** Key creation date.
* #type {Date} */
this.created = new Date();
/** A list of multiprecision integers
* #type {module:type/mpi} */
this.mpi = [];
/** Public key algorithm
* #type {module:enums.publicKey} */
this.algorithm = 'rsa_sign';
// time in days (V3 only)
this.expirationTimeV3 = 0;
/**
* Fingerprint in lowercase hex
* #type {String}
*/
this.fingerprint = null;
/**
* Keyid
* #type {module:type/keyid}
*/
this.keyid = null;
}
and trying to get the key id like this:
var publickey = openpgp.key.readArmored(myPublicKey);
//var keyID = openpgp.packet.PublicKey(publickey).getKeyId()[0].toHex();
var keyID = openpgp.PublicKey(publickey).getKeyId()[0].toHex();
console.log(keyID);
which gives me the error:
TypeError: openpgp.PublicKey is not a function.
Thanks.
I asked the question here:
http://www.mail-archive.com/list#openpgpjs.org/msg00932.html
and received a very helpful reply.
var openpgp = window.openpgp;
var testPublicKey = sessionStorage.getItem('testPublicKey');
var foundKeys = openpgp.key.readArmored(testPublicKey).keys;
if (!foundKeys || foundKeys.length !== 1) {
throw new Error("Key not read, or more than one key found");
}
var pubKey = foundKeys[0]; foundKeys = null;
var getFingerprint = function (key) {
// openpgp.key <- Class
// key <- Instance received by params
return key.primaryKey.fingerprint;
};
var getKeyId = function (key) {
return key.primaryKey.getKeyId().toHex();
}
console.log(getFingerprint(pubKey));
console.log(getKeyId(pubKey));
I have a problem with settings on multiple instances on a jquery plugin.
If I cast my plugin multiple times and alert them on an onclick binding, it always alerts the same parameter.
This is my plugin.
/**
* Provide user search for input elements.
*
* Call this jQuery plugin on an input element to add a button
* for searching the active directory on userdata.
* Returned data are saved in declared target input elements.
*
* Depends:
* jQuery UI Dialog
*
* #license http://www.opensource.org/licenses/mit-license.php
* #version 1.0
*/
;(function ( $, window, document, undefined ){
var PROP_NAME = 'userdata';
var IS_IFRAME = ( self !== top ) ? true : false;
var rfuuid = new Date().getTime();
var $jParent = ( self !== top ) ? window.parent.jQuery.noConflict() : $;
/**
* Userdata manager.
* Use the singleton instance of this class, $.userdata, to interact with the date picker.
* Settings for (groups of) date pickers are maintained in an instance object,
* allowing multiple different settings on the same page.
*/
function Userdata() {
this.regional = [];
this.regional[''] = {
abortText: 'Abbrechen',
acceptText: 'Hinzufügen',
errorTitle: 'Fehler beim Suchen',
errorFilterText: 'Suchkriterien einschränken.',
errorVoidText: 'Der gesuchte Kontakt konnte nicht gefunden werden.',
errorScriptText: 'Bei der Suche ist ein Fehler aufgetreten. Falls der Fehler wieder auftritt, wenden Sie sich bitte an Ihren Webadministrator.',
searchText: 'Durchsuchen',
selectionTitle: 'Namen überprüfen',
selectionInfoText: '"%s" kommt mehrmals vor.',
selectionDescText: 'Wählen Sie die Adresse aus, die Sie verwenden möchten:'
};
this._defaults = {
ajaxURL: 'userdata.php',
buttonClass: 'rf-button secondary',
buttonContainer: '<div>',
buttonContainerClass: 'grid_2',
targets: {}
}
$.extend(this._defaults, this.regional['']);
}
$.extend(Userdata.prototype, {
/**
* Override the default settings for all instances of the userdata plugin.
*
* #param object settings The new settings to use as defaults (anonymous object).
* #return the manager object.
*/
setDefaults: function( settings ){
$.extend(this._defaults, settings);
return this;
},
/**
*
*
* #param object input DOM input element.
* #param object settings Settings for attaching new userdata functionality.
*/
_attachDialog: function( input, settings ){
var settings = $.extend(this._defaults, settings);
if ( !document.getElementById('rf-userdata-dialog') ){
var inst = $jParent('<div id="rf-userdata-dialog"></div>').appendTo('body');
inst.dialog({ autoOpen: false, close: function(){ $jParent('body').css('overflow', 'auto'); input.focus(); }, modal: true, resizable: false }).after('<span class="ui-dialog-footer" /><span class="ui-dialog-footer-edge" />');
}
else {
var inst = $('#rf-userdata-dialog');
}
input.settings = settings;
$(input).data('settings', settings);
this._attachButton(input, inst);
},
/**
*
*
* #param object input DOM input element.
* #param object inst jQuery dialog instance.
*/
_attachButton: function( input, inst ){
var manager = this,
$input = $(input);
// Create search button.
var $button = $('<button>').attr('type', 'button').html(input.settings.searchText).addClass(input.settings.buttonClass);
alert(dump($(input).data('settings'))); // WORKS FINE, everything is unique.
/**
* Bind manager._searchUserdata() function on button click.
*/
$button.bind('click', { settings : $(input).data('settings') }, function(e){
alert(dump(e.data.settings)); // DOES NOT WORK, always settings from last plugin call...
//manager._searchUserdata(input, inst);
});
/**
* Insert container with search button after input field.
*/
$input.closest('[class*="grid_"]').after(function(){
return $(input.settings.buttonContainer).addClass(input.settings.buttonContainerClass).append($button);
});
/**
* Change default enterkey behaviour on triggering the userdata button.
*/
$input.bind('focusin', function(){
$input.bind('keydown.enterOpen', function(e){
if ( e.keyCode === 13 ){
$button.trigger('click');
return false;
}
});
});
/**
* Unbind keydown event with enterOpen namespace.
*/
$input.bind('focusout', function(){
$input.unbind('keydown.enterOpen');
});
},
/**
*
*
* #param object input DOM input element.
* #param object inst jQuery dialog instance.
*/
_searchUserdata: function( input, inst ){
var manager = this,
$input = $(input),
value = $input.val();
/**
* Perform an Ajax request to get the userdata of specified user.
*/
$.ajax({
url: input.settings.ajaxURL,
dataType: 'json',
data: 'value=' + encodeURIComponent(value),
/**
* A pre-request callback function.
* Returning false in the beforeSend function will cancel the request.
*/
beforeSend: function(){
// If value is smaller than two characters is equal space character
// call showError and cancel ajax call.
if ( value.length <= 2 || value === ' ' || value === '' ){
manager._showError(input.settings.errorFilterText, inst, input);
return false;
}
},
/**
* A function to be called if the request succeeds.
*
* #see manager._showError() for error display.
* #see manager._checkName() for selecting dialog.
*
* #param object data LDAP userdata returned from server.
*/
success: function( data ){
if ( $.isEmptyObject(data) ){
manager._showError(input.settings.errorVoidText, inst, input);
}
else if ( data.error === 4 ){
manager._showError(input.settings.errorFilterText, inst, input);
}
else {
// If request returned more than one user, call checkName() function.
if ( data.length > 1 ){
manager._checkName(input, inst, data);
}
else {
manager._setUserdata(inst, data[0], input);
}
}
},
/**
* A function to be called if the request fails.
*
* #see manager._showError() for more information.
*
* #param object jqXHR XMLHttpRequest object.
* #param string textStatus Description of occurred error.
* #param object errorThrown Exception object.
*/
error: function( jqXHR, textStatus, errorThrown ){
manager._showError(input.settings.errorScriptText, inst, input);
}
});
},
/**
*
*
* #param string error Error to display.
* #param object inst jQuery dialog instance.
* #param object input DOM input element.
*/
_showError: function( error, inst, input ){
inst.html('<div class="ui-dialog-container">' + error + '</div>')
inst.dialog({ title: input.settings.errorTitle, width: 400 });
inst.dialog('open');
},
/**
*
*
* #param object input DOM input element.
* #param object inst jQuery dialog instance.
* #param array data LDAP userdata.
*/
_checkName: function( input, inst, data ){
var manager = this,
$container = $('<div>').addClass('ui-dialog-container').html('<p>' + sprintf(input.settings.selectionInfoText, $(input).val()) + '</p><p>' + input.settings.selectionDescText + '</p>'),
$tableWrapperOuter = $('<div>').addClass('rf-select-list-wrapper-outer'),
$tableWrapperInner = $('<div>').addClass('rf-select-list-wrapper-inner').css('height', 240),
$table = $('<table>').addClass('rf-select-list'),
$thead = $('<thead>').html('<tr><th>Name</th><th>Position</th><th>Telefon</th><th>Ort</th><th>E-Mail</th><th>Alias</th></tr>'),
$tbody = $('<tbody>');
// Loop trough userdata and create a table row for each entry.
for ( var i = 0, length = data.length; i < length; i++ ){
var $row = $('<tr>').html(function(){
this.onselectstart = function(){ return false; }
//return '<td class="user-icon">' + data[i].sn + ' ' + data[i].givenname + '</td><td>' + data[i].title + '</td><td>' + data[i].telephonenumber + '</td><td>' + data[i].location + '</td><td>' + data[i].mail + '</td><td>' + data[i].cn + '</td>';
return '<td class="user-icon">' + data[i].sn + ' ' + data[i].givenname + '</td><td>' + data[i].title + '</td><td>' + data[i].location + '</td><td>' + data[i].cn + '</td>';
});
$row.bind('click', { obj: data[i] }, function(e){
var $this = $(this);
// Temp-save data from selection for ok-button.
inst.selection = e.data.obj;
$this.siblings().removeClass('ui-state-active');
$this.addClass('ui-state-active');
});
$row.bind('dblclick', { obj: data[i] }, function(e){
inst.dialog('close');
manager._setUserdata(inst, e.data.obj, input);
});
$row.appendTo($tbody);
}
// Trigger first row selection.
$tbody.find('tr').first().trigger('click');
// Hide scrollbar on form body to prevent scrolling problem.
$jParent('body').css('overflow', 'hidden');
// Create buttons and append them to a container.
var $buttonAccept = $('<button>').addClass("rf-button primary").html(input.settings.acceptText).bind('click', function(){
inst.dialog('close');
manager._setUserdata(inst, inst.selection, input);
});
var $buttonAbort = $('<button>').addClass("rf-button secondary").html(input.settings.abortText).bind('click', function(){
inst.dialog('close');
});
// Toggle 'rf-button-hover' class on buttons hover state.
$buttonAccept.hover(function(){ $buttonAccept.toggleClass('rf-button-hover'); });
$buttonAbort.hover(function(){ $buttonAbort.toggleClass('rf-button-hover'); });
var $buttonContainer = $('<div>').addClass('float-right').append($buttonAccept, $buttonAbort);
// Append dialog html to container.
$container.append($tableWrapperOuter.append($tableWrapperInner.append($table.append(/*$thead,*/ $tbody))), $buttonContainer);
inst.html($container);
inst.dialog({ title: input.settings.selectionTitle, width: 800 });
inst.dialog('open');
},
/**
*
*
* #param object inst jQuery dialog instance.
* #param array data LDAP userdata.
*/
_setUserdata: function( inst, data, input ){
for ( var target in input.settings.targets ){
var values = [];
if ( typeof(input.settings.targets[target]) === 'object' ){
for ( var i = 0, length = input.settings.targets[target].length; i < length; i++ ){
values.push(data[input.settings.targets[target][i]]);
}
}
else {
values.push(data[input.settings.targets[target]]);
}
$(target).val(values.join(' '));
}
}
});
/**
* Invoke the userdata functionality.
*
* #param object options Settings for attaching new userdata functionality.
* #return jQuery object.
*/
$.fn.userdata = function( options ){
// Verify an empty collection wasn't passed.
if ( !this.length ){
return this;
}
/**
* Loop through each plugin object.
*/
return this.each(function(){
$.userdata._attachDialog(this, options);
});
};
$.userdata = new Userdata();
$.userdata.uuid = new Date().getTime();
})( jQuery, window, document );
I call it in my html multiple times:
$('#inputid_1').userdata({ targets: {'#targetid_1': 'cn'} });
$('#inputid_2').userdata({ targets: {'#targetid_2': 'phone'} });
Now if you look at the _attachButton method, there are two alerts. One outside the click bind and one inside the click bind. Outside the click bind, the settings are unique foreach plugin call. Inside the click bind, it always alerts the settings from the last call, even if I pass them with event.data.
Extend the settings like this:
var settings = $.extend(settings, this._defaults); // invert the params
or
var settings = $.extend({}, this._defaults, settings);
Why ?
The $.extend() takes as first param a target. So you were merging the properties of this._default with the settings and not the contrary.
The second form (with {}) says: ignore target, let both this._default and settings untouched, simply return a merged object (hope i'm clear ^^).
See jQuery documentation about .extend().