i made a module for odoo 12 to lock the pos screen the code works fine in odoo 10, but gives error this.pos is null or this.pos.currency is undefined. in the models.js, i want to switch the orders of different cashiers but this.pos is not initialized, what should i do so that it becomes initialized?
here is my code:
odoo.define('pos_user_lock.models',
//['point_of_sale.models','point_of_sale.screens','point_of_sale.chrome'
//,'web.ajax','web.core'],
function (require) {
"use strict";
var ajax = require('web.ajax');
var screens = require('point_of_sale.screens');
var models = require('point_of_sale.models');
var chrome = require('point_of_sale.chrome');
var core = require('web.core');
var QWeb = core.qweb;
var _t = core._t;
models.Order = models.Order.extend({
initialize: function(attributes,options){
var order = models.Order.__super__.initialize.call(this,attributes,options);
if (!this.cashier)
{
this.cashier = this.pos.cashier || this.pos.user;
}
this.is_last_selected = (this.is_last_selected === undefined) ? false:this.is_last_selected;
return order;
},
finalize: function() {
var res = models.Order.__super__.finalize.call(this);
this.pos.lock_user_screen();
},
init_from_JSON: function(json) {
var user = this.get_user_by_id(json.user_id);
if (user)
this.cashier = user;
this.is_last_selected = json.is_last_selected;
models.Order.__super__.init_from_JSON.call(this, json);
},
export_as_JSON: function() {
var res = models.Order.__super__.export_as_JSON.call(this);
if (this.cashier)
res.user_id = this.cashier.id ;
if(this.pos && this.pos.get_cashier())
res.user_id = this.pos.get_cashier().id ;
res.is_last_selected = this.is_last_selected;
return res;
},
get_user_by_id: function(id) {
var users = this.pos.users;
for (var i = 0; i < users.length; i++) {
var user = users[i];
if (user.id == id)
return user;
}
},
});
models.PosModel = models.PosModel.extend({
initialize: function(session, attributes) {
models.PosModel.__super__.initialize.call(this, session, attributes);
var self = this;
alert(JSON.stringify(attributes.pos));
this.ready.then(function(){
if (self.config.auto_push_order)
self.config.iface_precompute_cash = true;
});
},
is_locked: function(){
if (
this.gui.current_popup
&& this.gui.current_popup.template == 'PasswordPinPopupWidget'
){
return true;
}
return false;
},
unlock: function(){
if (this.is_locked()){
this.gui.close_popup();
}
},
lock_user_screen: function(){
var self = this;
if (!this.config.lock_users) return;
this.gui.show_popup('passwordPin',{
'title': _t('Insert password or scan your barcode'),
});
},
get_last_order: function(){
var orders = this.get_order_list();
for (var i = 0; i < orders.length; i++) {
if (orders[i].is_last_selected) {
return orders[i];
}
}
return null;
},
set_last_order: function(order){
var orders = this.get_order_list();
for (var i = 0; i < orders.length; i++) {
if (orders[i].uid == order.uid) {
orders[i].is_last_selected = true;
}else{
orders[i].is_last_selected = false;
}
}
return null;
},
set_order: function(order){
models.PosModel.__super__.set_order.call(this, order);
this.set_last_order(order);
},
get_order_list: function(){
var orders = models.PosModel.__super__.get_order_list.call(this);
var c_orders = [];
var cashier = this.cashier || this.user;
for (var i = 0; i < orders.length; i++) {
if (cashier && orders[i].cashier.id === cashier.id) {
c_orders.push(orders[i]);
}
}
return c_orders;
},
switch_order: function(){
var self = this;
if(self.pos)
{
}
else
{
alert("self.pos is null");
}
if(self !== undefined && self != null && self.pos !== undefined && self.pos.currency != null)
{
alert("in switch order");
//console.log(this.pos.currency);
if (!self.config.lock_users) return;
var user = self.pos.get_cashier() || self.user;
var orders = self.get_order_list();
var last_order = self.get_last_order() || orders[0];
if (orders.length) {
self.set_order(last_order);
}
else {
self.add_new_order();
}
}
},
set_cashier: function(user){
models.PosModel.__super__.set_cashier.call(this,user);
if(this.pos)
{
alert("this.pos is not null in set_cashier");
}
else
{
alert("this.pos is null in set_cashier");
}
this.switch_order(this);
if (!this.table && this.config.iface_floorplan)
this.set_table(null);
},
});
return models;
});
the web page shows this.pos is null. and this.currency is null.
i found the solution by adding:
this.pos = this;
inside initialize function of PosModel.
Related
I am trying to fix this script to automatically connect people you may know on Linkedin based on User roles (CEO e.t.c), Can someone help me fix this, Below is my code; I have tried the script on almost all browsers, Somebody help fix this.
var userRole = [
"CEO",
"CIO"
];
var inviter = {} || inviter;
inviter.userList = [];
inviter.className = 'button-secondary-small';
inviter.refresh = function () {
window.scrollTo(0, document.body.scrollHeight);
window.scrollTo(document.body.scrollHeight, 0);
window.scrollTo(0, document.body.scrollHeight);
};
inviter.initiate = function()
{
inviter.refresh();
var connectBtns = $(".button-secondary-small:visible");
//
if (connectBtns == null) {var connectBtns = inviter.initiate();}
return connectBtns;
};
inviter.invite = function () {
var connectBtns = inviter.initiate();
var buttonLength = connectBtns.length;
for (var i = 0; i < buttonLength; i++) {
if (connectBtns != null && connectBtns[i] != null) {inviter.handleRepeat(connectBtns[i]);}
//if there is a connect button and there is at least one that has not been pushed, repeat
if (i == buttonLength - 1) {
console.log("done: " + i);
inviter.refresh();
}
}
};
inviter.handleRepeat = function(button)
{
var nameValue = button.children[1].textContent
var name = nameValue.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
function hasRole(role){
for(var i = 0; i < role.length; i++) {
// cannot read children of undefined
var position = button.parentNode.parentNode.children[1].children[1].children[0].children[3].textContent;
var formatedPosition = position.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
var hasRole = formatedPosition.indexOf(role[i]) == -1 ? false : true;
console.log('Has role: ' + role[i] + ' -> ' + hasRole);
if (hasRole) {
return hasRole;
}
}
return false;
}
if(inviter.arrayContains(name))
{
console.log("canceled");
var cancel = button.parentNode.parentNode.children[0];
cancel.click();
}
else if (hasRole(userRole) == false) {
console.log("cancel");
var cancel = button.parentNode.parentNode.children[0];
cancel.click();
}
else if (button.textContent.indexOf("Connect")<0){
console.log("skipped");
inviter.userList.push(name); // it's likely that this person didn't join linkedin in the meantime, so we'll ignore them
var cancel = button.parentNode.parentNode.children[0];
cancel.click();
}
else {
console.log("added");
inviter.userList.push(name);
button.click();
}
};
inviter.arrayContains = function(item)
{
return (inviter.userList.indexOf(item) > -1);
};
inviter.usersJson = {};
inviter.loadResult = function()
{
var retrievedObject = localStorage.getItem('inviterList');
var temp = JSON.stringify(retrievedObject);
inviter.userList = JSON.parse(temp);
};
inviter.saveResult = function()
{
inviter.usersJson = JSON.stringify(inviter.userList);
localStorage.setItem('inviterList', inviter.usersJson);
};
setInterval(function () { inviter.invite(); }, 5000);
`
When I try executing this, I get the following error:
VM288:49 Uncaught TypeError: Cannot read property 'children' of undefined
at hasRole (<anonymous>:49:71)
at Object.inviter.handleRepeat (<anonymous>:66:11)
at Object.inviter.invite (<anonymous>:30:69)
at <anonymous>:108:35
Any ideas as to how to fix it?
I look into a few answers but I'm not getting any results, I'm trying to fix this issue "Uncaught TypeError: this.getElements is not a function". This part of the code, full code in the link.
var SIDEBAR = new function() {
this.on = function(nameElement){
this.menu = nameElement;
return this;
};
/*more code*/
this.getElements = function() {
/*more code*/
return [];
};
/*more code*/
this.addElements = function() {
var elementsData = this.getElements();
/*more code*/
};
}();
var sid = SIDEBAR.on('test');
sid.load();
Full code: https://jsfiddle.net/e6shbnsu/
The value of this is determined by how a function is called.
this will point to window in setTimeout. Use .bind to have specified values as this context.
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
function inElectron() {
return navigator.userAgent.indexOf("Electron") != -1;
}
var dataManager = {
getItem: function(key, local) {
if (inElectron() || local == 1)
return localStorage.getItem(key);
else
return sessionStorage.getItem(key);
},
setItem: function(key, item, local) {
if (inElectron() || local == 1)
localStorage.setItem(key, item);
else
sessionStorage.setItem(key, item);
}
};
var SIDEBAR = new function() {
this.on = function(nameElement) {
this.menu = nameElement;
return this;
};
this.load = function() {
this.currentElement = 0;
this.refreshElements();
};
this.setAddElementName = function(name) {
this.addElementName = name;
};
this.setNewElementName = function(name) {
this.newElementName = name;
};
this.getElements = function() {
var elementsData = dataManager.getItem(this.getDataKey);
if (typeof elementsData !== 'undefined' && elementsData !== null) {
return JSON.parse(elementsData);
}
return this.getPreloadData();
};
this.setDataKey = function(key) {
this.dataKey = key;
};
this.getDataKey = function() {
if (this.dataKey) {
return this.dataKey;
}
return "SideBar" + this.menu;
};
this.setPreloadData = function(dataArray) {
this.preloadData = dataArray;
};
this.getPreloadData = function() {
if (typeof this.preloadData !== 'undefined' && this.preloadData !== null) {
return this.preloadData;
}
return [];
};
this.getCurrentElement = function() {
var elementsData = getElements;
return elementsData[currentElement];
};
this.refreshElements = function() {
window.setTimeout(function() {
this.clearElements();
}.bind(this), 1);
//outer `this` context is bound to the handler
window.setTimeout(function() {
this.addElements();
}.bind(this), 2);
};
this.deleteElement = function() {
var newArr = [];
var elementsData = this.getElements();
for (var i = 0, l = elementsData.length; i < l; i++) {
if (i != index) {
newArr.push(elementsData[i]);
}
}
dataManager.setItem(this.getDataKey, JSON.stringify(newArr));
};
this.addElements = function() {
var elementsData = this.getElements();
var menuNode = document.getElementById(this.menu);
console.log(elementsData);
for (var i = 0; i < elementsData.length; i++) {
var li = document.createElement("li");
var div = document.createElement("div");
li.value = i;
div.classList.add("list");
var p = document.createElement("p");
p.id = "textBlock";
p.style.display = "inline";
p.setAttribute("contentEditable", false);
p.appendChild(document.createTextNode(elementsData[i].name));
div.appendChild(p);
var obj = getObject();
console.log(obj);
div.onclick = function(e) {
e.stopImmediatePropagation();
if (this.querySelector("#textBlock").contentEditable == "false") {
this.currentElement = this.parentNode.value;
elementsData = this.getElements();
document.getElementById("prompt").innerHTML = elementsData[this.parentNode.value]["data"];
document.querySelector("#wrapper").classList.toggle("toggled");
}
};
var span2 = document.createElement("span");
span2.id = "deleteMode";
span2.classList.add("glyphicon");
span2.classList.add("glyphicon-minus");
span2.onclick = function(e) {
e.stopImmediatePropagation();
deleteItem(this.parentNode.parentNode.value);
window.setTimeout(this.refreshElements, 1);
};
span2.style.display = "none";
div.appendChild(span2);
var span = document.createElement("span");
span.id = "editMode";
span.classList.add("glyphicon");
span.classList.add("glyphicon-pencil");
span.onclick = function(e) {
e.stopImmediatePropagation();
// get href of first anchor in element and change location
for (var j = 0; j < menuNode.length; j++) {
menuNode[j].classList.add("disabled");
}
this.style.display = "none";
this.parentNode.querySelector("#deleteMode").style.display = "";
this.parentNode.classList.add("editableMode");
this.parentNode.classList.remove("disabled");
var textBlock = this.parentNode.querySelector("#textBlock");
textBlock.setAttribute("contentEditable", true);
this.placeCaretAtEnd(textBlock);
textBlock.onkeydown = function(e) {
if (e.keyCode == 13) {
e.stopImmediatePropagation();
var text = this.innerHTML.replace(" ", '');
text = text.replace("<br>", '');
if (text.length > 0) {
this.innerHTML = text;
elementsData[this.parentNode.parentNode.value]['name'] = text;
dataManager.setItem("IFTeleprompterScripts", JSON.stringify(elementsData));
for (var j = 0; j < menuNode.length; j++) {
menuNode[j].classList.remove("disabled");
}
this.parentNode.classList.remove("editableMode");
this.setAttribute("contentEditable", false);
this.parentNode.querySelector("#editMode").style.display = "";
this.parentNode.querySelector("#deleteMode").style.display = "none";
} else {
return false;
}
} else if (e.keyCode == 8) {
if (textBlock.innerHTML.length - 1 === 0) {
textBlock.innerHTML = " ";
}
}
return true;
};
return false;
};
div.appendChild(span);
li.appendChild(div);
scriptsNode.appendChild(li);
}
var li = document.createElement("li");
var div = document.createElement("div");
var span2 = document.createElement("span");
span2.id = "addMode";
span2.classList.add("glyphicon");
span2.classList.add("glyphicon-plus");
div.appendChild(span2);
var p = document.createElement("p");
p.id = "textBlock";
p.style.display = "inline";
p.setAttribute("contentEditable", false);
if (typeof this.addElementName !== 'undefined' && this.addElementName !== null)
p.appendChild(document.createTextNode(" " + this.addElementName));
else
p.appendChild(document.createTextNode(" Add " + this.menu));
div.appendChild(p);
li.onclick = function(e) {
e.stopImmediatePropagation();
var newPushElementName = "New " + this.menu;
if (typeof this.addElementName !== 'undefined' && this.addElementName !== null) {
newPushElementName = this.addElementName;
}
elementsData.push({
"name": newPushElementName,
"data": ""
});
dataManager.setItem(this.getDataKey, JSON.stringify(elementsData));
this.refreshElements();
};
li.appendChild(div);
menuNode.appendChild(li);
};
this.placeCaretAtEnd = function(el) {
el.focus();
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
};
}();
var sid = SIDEBAR.on('test');
sid.load();
<ul class="sidebar-nav" id="test">
</ul>
Below Infinite-scroll factory i am using to get data from backend, is there any where to apply $promise to this method as in controller i need to know when data has finished loading .
Factory :
app.factory('Gallery', function($http) {
var Gallery = function(media, album_type) {
this.items = [];
this.busy = false;
this.next = '/upload/list/'+ media +'/?album='+album_type+'&page=1';
this.end=false;
};
Gallery.prototype.nextPage = function() {
if (this.busy) return;
this.busy = true;
if (this.next){
var url = this.next;
}
else{
this.end=true;
this.busy = false;
return
}
$http.get(url).success(function(data) {
var items = data.results;
for (var i = 0; i < items.length; i++) {
this.items.push(items[i]);
}
this.next=data.next;
this.busy = false;
if (!data.next)
this.end=true;
this.count=data.count;
}.bind(this));
};
return Gallery;
});
In Controller;
$scope.images = $scope.images || new ScrollGallery('image', 'CRP');
I solved issue by adding $q in ininite-service , $q returns promise method .which i can use to to check if http was success to failed .
Infinite-service ::
app.factory('ScrollGallery', function($http,$q) {
var Gallery = function(url) {
this.items = [];
this.busy = false;
this.next = url;
this.end = false;
this.extra = {};
};
Gallery.prototype.nextPage = function() {
var def = $q.defer();
if (this.busy)
return;
this.busy = true;
if (this.next) {
var url = this.next;
} else {
this.end = true;
this.busy = false;
return;
}
$http.get(url).success( function(data) {
var items = data.results;
var extra = [];
if ( typeof data.extra != 'undefined') {
extra = data.extra;
}
for (var i = 0; i < items.length; i++) {
this.items.push(items[i]);
}
this.extra = extra;
this.next = data.next;
this.busy = false;
if (!data.next)
this.end = true;
this.count = data.count;
def.resolve(data);
}.bind(this));
return def.promise;
};
return Gallery;
});
In controller ::
$scope.mediacontent[$scope.slider_url] =new ScrollGallery($scope.slider_url_pass);
$scope.mediacontent[$scope.slider_url].nextPage().then(function(albums) {
$scope.mediacontent[$scope.slider_url].items[$scope.img_no].active=true;
console.log('albums returned to controller.');
},
function(data) {
console.log('albums retrieval failed.');
});
I created this fiddle http://jsfiddle.net/mj9f9/4/
The view does not update the enabled status of buttons "Search" and "Create". Only once it happen when the user select a product class, but it must happen also when the user changes a value of a component.
The view is a search view enabling the user to search products identified by components that depend on the class of product selected.
The view model:
/// ProductClass
function ProductClassModel() {
this.Id = 0;
this.FullCode = "";
this.Name = "";
this.EntitiClass = "";
this.CompClasses = [];
}
/// Component class that is part of a product class
function ComponentClassModel() {
this.Id = 0;
this.FullCode = "";
this.Name = "";
this.ProductClassId = 0;
this.ComponentClass = "";
}
/// The product, it is the composition of a product class with values for each of its components
function ProductModel() {
this.Id = ko.observable(0);
this.Uid = ko.observable("");
this.FullCode = ko.observable("");
this.Name = ko.observable("");
this.EntitiClass = ko.observable("");
this.Components = ko.observable([]);
}
/// ComponentValue is a class of component with a value (actually an instance of that class of component)
function ComponentValueModel() {
this.Id = ko.observable(0);
this.FullCode = ko.observable("");
this.Name = ko.observable("");
this.ProductClassId = ko.observable(0);
this.ProductId = ko.observable(0);
this.ComponentClass = ko.observable("");
this.Value = ko.observable();
}
/// Wrapper of ComponentValueModel with convenient observable properties (We cannot modify the original ComponentValueModel because it is extern)
function ComponentStateViewModel(prodcomp) {
var self = this;
console.log("CREA ComponentStateViewModel " + prodcomp);
self.value = prodcomp.Value();
self.component = prodcomp;
self.changed = ko.observable(false);
self.found = ko.observable(false);
self.searched = ko.observable(false);
self.required = true;
self.isCreatable = ko.computed(function () {
return self.searched() && !self.found();
});
self.isDefined = ko.computed(function () {
return self.component.Value() !== undefined;
});
self.component.Value.subscribe(function () {
var newval = self.component.Value();
if (newval !== self.value) {
self.changed(true);
self.found(false);
self.searched(false);
self.value = newval;
}
});
}
/// View model
function ProductViewModel() {
var self = this;
self.customerId = ko.observable(111);
self.prodClasses = ko.observable([]);
self.currentProdClass = ko.observable();
self.currentProd = ko.observable();
self.currentUid = ko.observable();
self.productsFound = ko.observableArray();
self.componentStates = ko.observableArray();
self.productFound = ko.computed(function () {
var prod = self.currentProd();
return self.prodClassSelected && (prod !== undefined && prod.Uid() !== undefined);
});
self.customerSelected = ko.computed(function () {
return self.customerId() > 0;
});
self.prodClassSelected = ko.computed(function () {
return self.currentProdClass() !== undefined;
});
self.canSearch = ko.computed(function () {
if (!self.prodClassSelected() || !self.productFound()) {
return false;
}
var inuid = self.currentUid();
var puid = self.currentProd().Uid();
if ((inuid !== undefined) && (inuid !== puid) && (inuid.length > 5)) {
return true;
}
var comps = self.componentStates();
for (var i = 0; i < comps.length; i++) {
var c = comps[i];
if (c.isDefined && !c.searched) {
return true;
}
}
return false;
});
self.canCreate = ko.computed(function () {
if (!self.prodClassSelected() || self.productFound()) {
return false;
}
var cstates = self.componentStates();
for (var i = 0; i < cstates.length; i++) {
var cm = cstates[i];
if (!cm.component.Value()) {
return false;
}
if (!cm.isCreatable()) {
return false;
}
}
return true;
});
self.clear = function () {
self.componentStates([]);
var c = self.currentProdClass();
if (c !== undefined) {
self.currentUid(undefined);
self.currentProd(undefined);
}
};
self.initialize = function (compClassesData) {
self.prodClasses(compClassesData);
};
self.currentUid.subscribe(function () {
var uid = self.currentUid();
if (uid === undefined) {
return;
}
var prod = self.currentProd();
if ((prod === undefined) || (prod.Uid() !== uid)) {
if (uid.length < 5) {
return;
}
self.currentProd(new ProductModel());
}
});
self.currentProdClass.subscribe(function () {
self.clear();
var c = self.currentProdClass();
if (c === undefined) {
return;
}
var psearch = new ProductModel();
psearch.FullCode(c.FullCode);
psearch.Name(c.Name);
psearch.EntitiClass(c.EntitiClass);
for (var i = 0; i < c.CompClasses.length; i++) {
var cca = c.CompClasses[i];
var comp = new ComponentValueModel();
comp.FullCode(cca.FullCode);
comp.Name(cca.Name);
comp.ProductClassId(cca.ProductClassId);
comp.ComponentClass(cca.ComponentClass);
psearch.Components().push(comp);
self.componentStates.push(new ComponentStateViewModel(comp));
}
self.currentProd(psearch);
self.currentUid(psearch.Uid());
});
self.searchProduct = function () {
var compfilter = [];
var comps = self.currentProd().Components();
for (var i = 0; i < comps.length; i++) {
var c = comps[i];
if ((c.Value() !== undefined) && (c.Value() !== "")) {
var filter = new ComponentValueModel();
filter.FullCode(c.FullCode());
filter.ComponentClass(c.ComponentClass());
filter.Value(c.Value());
compfilter.push(filter);
}
}
};
self.createProduct = function () {
// TODO
};
}
The problem is with the buttons Search and Create. The first one is enabled or disabled by a computed function that involves several observables but the one I am interested in is the Value of a component of the class ComponentValueModel.Value:
<button data-bind="click: searchProduct, enable: canSearch()">Search</button>
The code I expected to enable or disable this button is defined in ProductViewModel.canSearch computed function:
self.canSearch = ko.computed(function () {
if (!self.prodClassSelected() || !self.productFound()) {
return false;
}
var inuid = self.currentUid();
var puid = self.currentProd().Uid();
if ((inuid !== undefined) && (inuid !== puid) && (inuid.length > 5)) {
return true;
}
var comps = self.componentStates();
for (var i = 0; i < comps.length; i++) {
var c = comps[i];
if (c.isDefined && !c.searched) {
return true;
}
}
return false;
});
It seems as if ko does not notifies the change of value in the observable component.Value. Any help will be appreciated.
I build a prototype that handle pages, I successfully add (push), but can get the data, I failed:
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
Here the javascript page handler:
var Pages = new Array();
PageContainer = function () //constructor for the proxy
{
// this._baseURL = url;
};
PageContainer.prototype =
{
AddPage: function (data) {
if (data == null) return;
Pages.push({ PageID: data.PageID, SegmentID: data.SegmentID });
},
GetPage: function (PageID) {
alert('getPage('+PageID+')=' + JSON.stringify(Pages));
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
var dt = { PageID: Pages[foundImageIndex].PageID, SegmentID: Pages[foundImageIndex].SegmentID };
return dt;
}
};
I call from other js as following:
var gPageContainer = new PageContainer();
for (var i = 0; i < SegStruct.SegmentsCount; i++) {
var segRClass = //get from webservice
gPageContainer.AddPage({ PageID: i, SegmentID: segRClass.SegmentID });
}
I trying to call: gPageContainer.GetPage(1); but it failed in GetPage: function (PageID) it returns -1 in:
var foundImageIndex = Pages.indexFirst(function (item) { if (item.PageID == PageID) return true; });
foundImageIndex always -1
why?
Simply add the following before the constructor:
if (typeof Array.prototype.indexFirst == 'undefined') {
Array.prototype.indexFirst = function (validator) {
for (var i = 0; i <= this.length - 1; i++) {
if (validator(this[i])) {
return i;
}
}
return -1;
};
}