Google Closure introducing errors - javascript
EDIT
The lesson, learned with the help of #Alex, is that you should never put function declarations in block scope. Not that I intended to do this, but if you slip up, it can cause big problems.
I have a script file that seems to be getting compressed via Google Closure incorrectly. When I run my app with the original code, all works fine. But when I try to compress it with Google Closure, some errors get introduced.
I am NOT using the advanced option; I'm using the basic, default mode
Obviously I can't expect anyone to debug the compressed file, but I'm hoping someone can look at the uncompressed code and let me know if I'm somehow doing something insanely stupid that would trick Closure.
Some notes on the minified code:
Closure is inlining BEFramework.prototype.hstreamLoad and BEFramework.prototype.hstreamEvalJson, and seems to be utterly removing the helper functions getDeleteValue, getValueToDisplay, getDisplayForLabel and likely others.
Uncompressed file is below.
This code can manually be compiled by closure here, which should reproduce the symptoms described above.
(function() {
var $ = jQuery;
// Load and display the messages ("healthstream") for a given module.
// This requires that the module's HTML have specific features, see
// dashboard.htm and contactsManager/details/default.htm for examples.
// This also requires that the `request` support `pageIndex` and `pageSize`,
// so we can handle paging.
//
// Args: `options` An options object with these keys:
// `channelId` The channel ID of the module (for transmitRequest)
// `translationId` Optional alternate ID for translation (if not given,
// `channelId` is used).
// `action` The action (for transmitRequest)
// - Must support `pageIndex` and `pageSize`
// `request` The request (for transmitRequest)
// - Must include `pageIndex` and `pageSize`
// `complete` Optional callback triggered when the load is complete.
// `showOptions` Optional callback if an options menu is supported
// by the calling module. Receives a raw event instance
// and the item on which the options were triggered:
// function showOptions(event, item)
// `context` Optional context (`this` value) for the call to
// `complete` and/or `showOptions`
BEFramework.prototype.hstreamLoad = hstreamLoad;
function hstreamLoad(options) {
var inst = this;
var channelId, translationId, action, request, complete, showOptions, context,
pageIndex, pageCount, pageSize, pageCount,
btnPrevious, btnNext,
dataShownFlags;
// Get our arguments (with defaults)
channelId = options.channelId;
translationId = options.translationId || options.channelId;
action = options.action;
request = $.extend({}, options.request); // Create a *copy*, because we modify it when doing paging
complete = options.complete;
if (typeof complete !== "function") {
complete = undefined;
}
showOptions = options.showOptions;
if (typeof showOptions !== "function") {
showOptions = undefined;
}
context = options.context; // (undefined will automatically become the global object)
// Grab the initial pageIndex and pageSize
pageIndex = request.pageIndex || 1;
pageSize = request.pageSize || 100;
// Disable the button and show "searching" label
$('#healthStreamSearchButton')
.button("disable")
.button("option", "label", BETranslate(translationId, 'HealthStreamSearching'));
// Hook up the buttons; be a bit paranoid that they've been hooked before and clear previous handlers
btnPrevious = $('#healthStreamPagePrevious');
btnNext = $('#healthStreamPageNext');
btnPrevious.hide().unbind("click.paging").bind("click.paging", goToPreviousPage);
btnNext.hide().unbind("click.paging").bind("click.paging", goToNextPage);
// Do it
doLoad();
// === Support functions
// Trigger a load request
function doLoad() {
request.pageIndex = pageIndex;
request.pageSize = pageSize;
inst._transport.transmitRequest(channelId, action, request, hstreamLoaded);
}
// Hndle the load response
function hstreamLoaded(objResponse) {
var healthStream = objResponse.items;
var total = objResponse.total;
var tbody = $('#healthStreamList');
// Need to make this update optional
$('#pageHeaderName').html(BETranslate(translationId, 'HeaderActivity') + ' (' + String(total) + ')');
$('#healthStreamSearchButton')
.button("enable")
.button("option", "label", BETranslate(translationId, 'HealthStreamSearch'));
tbody.empty();
btnPrevious.hide();
btnNext.hide();
if (healthStream.length > 0) {
pageCount = Math.ceil(total / pageSize);
if (pageCount > 1) {
if (pageIndex > 1) {
btnPrevious.show();
}
if (pageIndex < pageCount) {
btnNext.show();
}
}
var item;
var tr;
var tdMain;
var daysHash = {};
var creationDate;
var key;
var today = new Date();
var yesterday = new Date();
var msg;
yesterday.setDate(yesterday.getDate() - 1);
dataShownFlags = {};
for (var x = 0; x < healthStream.length; x++) {
item = healthStream[x];
msg = inst.hstreamEvalJson(item);
if (msg.length > 0) {
creationDate = new Date(item.CreationDate);
key = [creationDate.getYear(), creationDate.getMonth(), creationDate.getDate()].join('-');
if (!daysHash[key]) {
if (isDateEqual(creationDate, today)) {
addRowHeader(tbody, BETranslate(inst._channelId, 'HSToday'));
}
else if (isDateEqual(creationDate, yesterday)) {
addRowHeader(tbody, BETranslate(inst._channelId, 'HSYesterday'));
}
else {
addRowHeader(tbody, creationDate.toString('MM/dd/yyyy'));
}
daysHash[key] = true;
}
tr = $(
"<tr>" +
"<td class='date' style='white-space:nowrap;'>" + new Date(item.CreationDate).toString('h:mm tt') + "</td>" +
"<td class='main'><span class='name'>" + msg + "</span>" +
"</tr>"
);
tbody.append(tr);
if (showOptions) {
tr.find("td.main").prepend($("<em rel='opt'> </em>").click(makeShowOptionsHandler(item)));
}
}
}
// If any of the templates created links with a `data` attribute, hook them up
$('#healthStreamList a[data]').click(showTitle).each(function (index) {
this.id = 'data' + index;
});
}
else {
tbody.html('<tr><td colspan="2">' + BETranslate(inst._channelId, 'HSNoActivity') + '</td></tr>');
}
// Trigger completion callback
if (complete) {
complete.call(context, objResponse);
}
}
function makeShowOptionsHandler(item) {
// Our event comes to us from jQuery, but we pass on the raw
// event to the callback
return function (event) {
showOptions.call(context, event.originalEvent || event, item);
};
}
function addRowHeader(listRef, name) {
listRef.append(
"<tr>" +
"<td colspan='2' class='divider'>" + name + "</td>" +
"</tr>"
);
}
function showTitle(event) {
$.stopEvent(event);
var link = this;
var $link = $(this);
var href = $link.attr("href"); // We want the attribute, not the property (the property is usually expanded)
var hrefTitle = $link.attr('hreftitle') || BETranslate(inst._channelId, 'HSMoreInfo');
var data = $link.attr('data') || "";
var linkId = link.id;
if (!dataShownFlags[linkId]) {
dataShownFlags[linkId] = true;
if (data) {
var div = $(
"<div class='data'>" +
"<span data-linkId='" + linkId + "' class='close'>x</span>" +
"<table><thead></thead></table>" +
"</div>"
);
$link.parent().append(div);
var thead = div.find("thead");
var arr = data.split('~');
var splitEntry;
for (var x = 0; x < arr.length; x++) {
splitEntry = arr[x].split('|');
if (splitEntry[0] === 'Changed length') {
splitEntry[1] = splitEntry[1].replace(/\d+/g, BEFramework.prettyTime);
}
if (splitEntry.length > 1 && splitEntry[1].length > 0) {
thead.append(
"<tr>" +
"<td class='hslabel'>" + splitEntry[0] + ":</td>" +
"<td>" + splitEntry[1] + "</td>" +
"</tr>"
);
}
}
div.find("span:first").click(hideTitle);
if (href && href !== "#") {
$("<a target='_blank'>" + hrefTitle + "</a>").attr("href", href).appendTo(div);
}
}
}
}
function hideTitle(event) {
var $this = $(this),
linkId = $this.attr("data-linkId");
delete dataShownFlags[linkId];
$this.parent().remove();
return false;
}
function goToPreviousPage(event) {
--pageIndex;
doLoad();
return false;
}
function goToNextPage(event) {
++pageIndex;
doLoad();
return false;
}
}
var ___x = false;
var __i = 0;
BEFramework.prototype.hstreamEvalJson = hstreamEvalJson;
function hstreamEvalJson(item) {
var inst = this;
if (item.Action === 'saveinsurance' && !___x && __i != 0){
var start = +new Date();
__i = 1;
}
var userId = inst._BEUser ? inst._BEUser.getId() : -1;
var json = eval('(' + item.JSON + ')');
var key = 'HS' + item.Module + '_' + item.Action;
var msg = BETranslate(inst._channelId, key);
var fromIsMe = item.CreatedByContactId == userId;
var toIsMe = item.ContactId == userId;
var fromString = (fromIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYou') + '</strong>' : '<a class="vcard" contactId="' + item.CreatedByContactId + '">' + item.CreatedByName + '</a>';
var toString = (toIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYour') + '</strong>' : '<a class="vcard" contactId="' + item.ContactId + '">' + item.ContactName + '</a>';
var fromString2 = (fromIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYour').toLowerCase() + '</strong>' : '<a class="vcard" contactId="' + item.CreatedByContactId + '">' + item.CreatedByName + '</a>';
var toString2 = (toIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYou').toLowerCase() + '</strong>' : '<a class="vcard" contactId="' + item.ContactId + '">' + item.ContactName + '</a>';
var subFormat, subProps;
var configObject = (BEFramework.healthStreamConfig[item.Module] && BEFramework.healthStreamConfig[item.Module][item.Action]) || {};
var standardCase = configObject.standardCase;
var suppress = configObject.suppress || [];
var propertiesInOrder = configObject.displayOrder || [];
if (msg.indexOf('not found in module') != -1) {
try {
switch (item.Module) {
case 'contacts':
if (item.Action == 'setpermission' || item.Action == 'deleterelationship' || item.Action == 'addinvite') {
msg = BETranslate(inst._channelId, key + json.type.toString());
}
break;
case 'tasks':
if (item.Action == 'savetask') {
msg = BETranslate(inst._channelId, key + json.type.toString());
}
break;
default:
msg = '';
}
} catch (ex) {
msg = '';
}
}
for (var prop in json) {
if (typeof (json[prop]) == 'object') {
if (prop === 'changes' || prop === 'deleted'){
subProps = json[prop];
for (var propName in subProps) {
if (indexInArrayCI(propName, propertiesInOrder) === -1 && indexInArrayCI(propName, suppress) === -1){
propertiesInOrder.push(propName);
}
}
}
if (prop == 'changes') {
var changes = '';
var changeFrom = BETranslate(inst._channelId, 'HSChangedFrom');
var changeTo = BETranslate(inst._channelId, 'HSChangedTo');
for (var i = 0; i < propertiesInOrder.length; i++) {
var subprop = propertiesInOrder[i];
if (getObjectValCI(subProps, subprop) == null) continue;
var subSplit = stripHtml(getObjectValCI(subProps, subprop)).split('|');
if (subSplit.length === 1) {
subFormat = BETranslate(inst._channelId, 'HS' + item.Module + '_changes_' + subprop);
if (subFormat.indexOf('not found in module') < 0) {
changes += $.sandr(subFormat, '#{value}', subSplit[0]);
}
else {
changes += "*|" + subprop + " " + subSplit[0] + "~";
}
}
else {
var fromValue = stripHtml(subSplit[0]);
var toValue = stripHtml(subSplit[1]);
var packetInfo = processChangedValues(subprop, fromValue, toValue);
if (packetInfo.skip) continue;
changes = changes + changeFrom + packetInfo.display + '|' + packetInfo.fromValue + '<b>' + changeTo + '</b>' + packetInfo.toValue + '~';
}
}
msg = $.sandr(msg, '#{' + prop + '}', changes);
} else if (prop == 'deleted') {
var deleted = '';
for (var i = 0; i < propertiesInOrder.length; i++) {
var subprop = propertiesInOrder[i];
var currentValue = getObjectValCI(subProps, subprop);
if (currentValue == null || currentValue.toString().length === 0) continue;
deleted = deleted + getDisplayForLabel(subprop) + '|' + getDeleteValue(subprop, currentValue) + '~';
}
msg = $.sandr(msg, '#{' + prop + '}', deleted);
}
} else {
msg = $.sandr(msg, '#{' + prop + '}', $.sandr(json[prop], '"', ' '));
}
function processChangedValues(label, fromValue, toValue){
var typeFormat = (getObjectValCI(configObject, label) || {}).type;
var result = {};
if (typeFormat === 'date'){
var d1 = new Date(fromValue);
var d2 = new Date(toValue);
if (isDateEqual(d1, d2)) result.skip = true;
}
result.fromValue = getValueToDisplay(fromValue, typeFormat);
result.toValue = getValueToDisplay(toValue, typeFormat);
result.display = getDisplayForLabel(label)
return result;
}
function getDeleteValue(label, value){
var typeFormat = (getObjectValCI(configObject, label) || {}).type;
return getValueToDisplay(value, typeFormat);
}
function getValueToDisplay(rawValue, typeFormat){
if (typeFormat === 'date'){
var d = new Date(rawValue);
return isNaN(d.getTime()) ? rawValue : d.toString('MM/dd/yyyy');
} else if (typeof typeFormat === 'function') {
return typeFormat(rawValue)
} else {
return rawValue;
}
}
function getDisplayForLabel(label){
var fixCaseOfProperty = standardCase === '*' || indexInArrayCI(label, standardCase) > -1;
var rawConfigForLabel = getObjectValCI(configObject, label) || {};
return (rawConfigForLabel && rawConfigForLabel.display)
|| (fixCaseOfProperty ? fixCase(label) : null)
|| label;
}
}
msg = $.sandr(msg, '#{contactId}', item.ContactId);
msg = $.sandr(msg, '#{from}', fromString);
msg = $.sandr(msg, '#{to}', toString);
msg = $.sandr(msg, '#{from2}', fromString2);
msg = $.sandr(msg, '#{to2}', toString2);
msg = $.sandr(msg, '#{recordId}', item.RecordId);
msg = msg.replace(/#{[\S]*}/g, '');
if (item.Action === 'saveinsurance' && !___x && __i == 1){
var end = +new Date();
___x = true;
//alert(end - start);
}
if (item.Action === 'saveinsurance') __i++;
if (msg.indexOf('not found in module') == -1) {
return msg;
} else {
return '';
}
}
function stripHtml(html) {
var tmp = document.createElement('DIV');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText;
}
function isDateEqual(date1, date2) {
if (date1.getDate() === date2.getDate() &&
date1.getMonth() === date2.getMonth() &&
date1.getYear() === date2.getYear()) {
return true;
}
else {
return false;
}
}
function getObjectValCI(obj, key){
for (var k in obj){
if (k.toLowerCase() === key.toLowerCase()){
return obj[k];
}
}
}
function indexInArrayCI(item, arr){
if (!$.isArray(arr)) arr = [];
var target = item.toString().toLowerCase();
for (var i = 0; i < arr.length; i++){
if (target === arr[i].toLowerCase()) return i;
}
return -1;
}
function fixCase(str){
return str.replace(/[a-z][A-Z]/g, function(match) { return match.charAt(0) + ' ' + match.charAt(1).toLowerCase(); }).toLowerCase()
.replace(/\sid\s/g, ' ID ')
.replace(/\sid$/g, ' ID')
.replace(/^id$/g, 'ID');
}
})();
When you use closure compiler you're giving up some control over your code. It will do all sorts of tricks, and potentially remove unused code.
It appears as though your functions are not removed, but are renamed.
For example, your call to getDeleteValue...
getDeleteValue(subprop, currentValue)
is now...
l(g,r)
Because getDeleteValue was not exported, Closure renamed it.
Working with Closure Compiler takes a bit of finesse and quite a bit of documentation scouring until you're familiar with how it works.
Well, there are too many errors to think of. First of all, I don't understand if you want static reference or instantiated values. You are not using jsDoc tags or anything like that. The Compiler does it's best work only with the corresponding jsDoc tag. You're logic is very weird and ill formulated. Prototype alternations, etc, all happening in an IIFE(immediately invoked function expression). Are your functions static? Are they constructors? Are we human or are we dancer?
an IIFE executes before the DOMContentLoaded event is fired by the browser. The most you can do is a jQuery IIFE equivalent $(function() {})(); which binds that to the DOMReady or DOMContentLoaded callback. You are defining inline functions inside blocks, which is not even in the ECMA Language.
While most script engines support Function Declarations within blocks it is not part of ECMAScript (see ECMA-262, clause 13 and 14). Worse implementations are inconsistent with each other and with future EcmaScript proposals. ECMAScript only allows for Function Declarations in the root statement list of a script or function. Instead use a variable initialized with a Function Expression to define a function within a block.
var myFunctionName = function (params) {};
You are also missing loads of semi-colons. Automatic semi-colon insertion on interpretation of your JS is not exactly flawless, so make a habit out of it.
Relying on implicit insertion can cause subtle, hard to debug problems. Don't do it. You're better than that.
There are a couple places where missing semicolons are particularly dangerous:
// 1.
MyClass.prototype.myMethod = function() {
return 42;
} // No semicolon here.
(function() {
// Some initialization code wrapped in a function to create a scope for locals.
})();
var x = {
'i': 1,
'j': 2
} // No semicolon here.
// 2. Trying to do one thing on Internet Explorer and another on Firefox.
// I know you'd never write code like this, but throw me a bone.
[normalVersion, ffVersion][isFF]();
var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] // No semicolon here.
// 3. conditional execution a la bash
-1 == resultOfOperation() || die();
So what happens?
JavaScript error - first the function returning 42 is called with the second function as a parameter, then the number 42 is "called" resulting in an error.
You will most likely get a 'no such property in undefined' error at runtime as it tries to call x[ffVersion][isIE]().
die is called unless resultOfOperation() is NaN and THINGS_TO_EAT gets assigned the result of die().
Why?
JavaScript requires statements to end with a semicolon, except when it thinks it can safely infer their existence. In each of these examples, a function declaration or object or array literal is used inside a statement. The closing brackets are not enough to signal the end of the statement. Javascript never ends a statement if the next token is an infix or bracket operator.
This has really surprised people, so make sure your assignments end with semicolons.
Related
How to optimise RadComboBox clearItems function
I have a MultiSelectDropDown, that is, several RadComboBox controls are used in a combined way. For example, I can have a dropdown for regions, another for depots and another for user. The idea is to change the content of lower levels dynamically whenever items are selected or unselected on a higher level. The problem is that in the case when many items are selected, this becomes brutally slow due to some Telerik functions, but I do not understand why. This is a chunk from the client-side of the MultiSelectDropDown prototype: changeLowerLevels: function (valueIndex, values, value) { if (!this.canChange) return; //Get selected values from combobox var combo = $find(this.ddlIDs[valueIndex - 1]); var cbItems = combo.get_checkedItems(); var selectedItems = []; var change = null; var counter = 0; if (cbItems.length) this.filterString = ""; for (var i = 0; i < cbItems.length; i++) { counter++; if (this.filterString == "") this.filterString = cbItems[i].get_text(); selectedItems.push(cbItems[i].get_value()); } if (counter > 1) this.filterString += " with " + (counter - 1) + " other" + ((counter > 2) ? "s" : ""); if (JSON.stringify(selectedItems) === JSON.stringify(this.selectedItems[valueIndex - 1]) || selectedItems == []) return; this.selectedItems[valueIndex - 1] = selectedItems; var controlObject = this; var combo = $find(this.ddlIDs[valueIndex]); var comboItems = combo.get_items(); if(!this.disabled) combo.enable(); combo.clearItems(); if (valueIndex == 1) this.twoLevelCache = values; var val = values; //break if all items are found var nrOfSelectedItems = this.selectedItems[valueIndex - 1].length; var nrOfFoundItems = 0; var index = 0; var indexes = []; var found = false; while (nrOfFoundItems < nrOfSelectedItems && val[index] !== undefined) { found = (this.selectedItems[valueIndex - 1].indexOf(val[index].Value) != -1); if (!(found)) index++; else { indexes.push(index) nrOfFoundItems++; index++; } } //separators from valuesIndex - 1 level var controlObject = this; for (var i = 0; i < indexes.length; i++) { var separator = new Telerik.Web.UI.RadComboBoxItem(); separator.set_text("<span><a class=\"checkAll tt-multi-uncheck-icon\" index=\"" + index + "\">U</a>" + $find(this.ddlIDs[valueIndex - 1]).findItemByValue(val[indexes[i]].Value).get_text() + "</span>"); separator.set_value(""); separator.set_isSeparator(true); comboItems.add(separator); this.twoLevelCache.push(val[indexes[i]].Levels); //valuesIndex level var valuesArray = val; var comboItem = new Telerik.Web.UI.RadComboBoxItem(); for (var depot in valuesArray[indexes[i]].Levels) { comboItem = new Telerik.Web.UI.RadComboBoxItem(); comboItem.set_text(valuesArray[indexes[i]].Levels[depot].Name); comboItem.set_value(valuesArray[indexes[i]].Levels[depot].Value); comboItems.add(comboItem); comboItem = null; } $('#' + this.ddlIDs[valueIndex] + '_DropDown a.checkAll').unbind().on("click", function () { checkAllLowerItems(this, controlObject.ddlIDs[valueIndex]); }); } combo.set_emptyMessage(this.allText); //$("#" + this.ddlIDs[valueIndex]).html(returnValue); if (this.ddlIDs.length > valueIndex + 1) { var paramToPass = (((val == undefined) || (val[index] === undefined)) ? ("") : (val[index])); if (this.allText.length > 0) this.changeLowerLevels(valueIndex + 1, paramToPass, ""); else { if (paramToPass !== "") paramToPass = paramToPass.Levels; if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) { this.changeLowerLevels(valueIndex + 1, paramToPass, ""); } else { this.changeLowerLevels(valueIndex + 1, paramToPass, val[index].Levels[0].Value); } } } else { if (this.allText.length > 0) this.selectedItems[valueIndex] = ""; else if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) { this.selectedItems[valueIndex] = ""; } else { this.selectedItems[valueIndex] = val[index].Levels[0].Value; } } this.setText(); } combo.clearItems() is extremeley slow. I have take a look on how it is implemented: function (){var f=this._parent._getControl();?if(f._checkBoxes){f._checkedIndicesJson="[]";?f._checkedIndices=[];?var g=f.get_items();?for(var d=0,e=g.get_count();?d<e;?d++){var c=f.get_items().getItem(d);?c.set_checked(false);?}f.updateClientState();?}a.RadComboBoxItemCollection.callBaseMethod(this,"clear");?} How can I make sure that this Javascript function speeds up?
I have finally solved the problem by rewriting Telerik client-side functionalities. It was a long and difficult debugging, but it yielded a large performance boost in the most difficult circumstances. From ~30 000 milliseconds, to ~300. Let's see the parts of the optimization: The actual rewrite /* Overriding Telerik functions Start */ var overridenTelerikControls = false; function overrideTelerikFunctionalities() { if (!overridenTelerikControls) { overridenTelerikControls = true; Telerik.Web.UI.RadComboBox.prototype.clearItems = function (isMultiSelectDropDown) { this.get_items().clear(isMultiSelectDropDown); this._itemData = null; }; Telerik.Web.UI.RadComboBoxItemCollection.prototype.clear = function (isMultiSelectDropDown){ var f=this._parent._getControl(); if(f._checkBoxes){ f._checkedIndicesJson="[]"; f._checkedIndices=[]; var g = f.get_items(); for(var d=0,e=g.get_count();d<e;d++){ var c=f.get_items().getItem(d); c.set_checked(false, isMultiSelectDropDown); } if (isMultiSelectDropDown) { f._updateComboBoxText(); if (f._checkAllCheckBoxElement != null) { f._updateCheckAllState(); } } f.updateClientState(); } Telerik.Web.UI.RadComboBoxItemCollection.callBaseMethod(this, "clear"); }; Telerik.Web.UI.RadComboBoxItem.prototype.set_checked = function (d, isMultiSelectDropDown){ if(!this.get_enabled()){ return; } this._setChecked(d); var c=this.get_comboBox(); if(c){ if(d){ c._registerCheckedIndex(this.get_index()); }else{ c._unregisterCheckedIndex(this.get_index()); } if (!isMultiSelectDropDown) { c._updateComboBoxText(); } if((!isMultiSelectDropDown) && (c._checkAllCheckBoxElement!=null)){ c._updateCheckAllState(); } } }; } } /* Overriding Telerik functions End*/ My approach was to keep the old way of their working by default, but if an isMultiSelectDropDown parameter is passed, then work in the optimized manners. So we have a switch materialized as a parameter and we can turn it on/off. The main difference was that the old way was to change the label text showing the selected elements each time a checkbox is checked/unchecked. The main improvement was to do this change after all the checkboxes were checked/unchecked. This extremely simple idea was the driving force behind the boost of performance. Actual usage overrideTelerikFunctionalities(); combo.clearItems(true); This was the functionalities were overriden if they were not already and the parameter was true, therefore the new approach was chosen. Test, test, test
How can I delete Items from json string without using $.grep
I have a cart variable and I am storing the cart inside it like this. [{"course_id":"24","doc_id":"211","doc_title":"PDF Notes","doc_price":"500"},{"course_id":"25","doc_id":"217","doc_title":"PDF Notes","doc_price":"500"},{"course_id":"25","doc_id":"218","doc_title":"PDF Solved Past Papers","doc_price":"500"},{"course_id":"26","doc_id":"224","doc_title":"PDF Solved Past Papers","doc_price":"595"}] I created a RemoveFromCart function. It works in simple JQUERY But it is not working in Framework 7 because of $.grep. Is there any other way I can do it without using $.grep? This is my Function function removeFromCart(course_id, doc_id) { var x = confirm("Are you sure you want to remove this item from your cart?"); if (x) { $$('#cart_body').html(''); existing_cart = localStorage.getItem("cart"); if (existing_cart == '') { existing_cart = []; } else { existing_cart = JSON.parse(existing_cart); } existing_cart = $.grep(existing_cart, function (data, index) { return data.doc_id != doc_id }); ex_cart = JSON.stringify(existing_cart); localStorage.setItem('cart', ex_cart); existing_cart = localStorage.getItem("cart"); if (existing_cart == '') { existing_cart = []; } else { existing_cart = JSON.parse(existing_cart); } if (existing_cart !== null && existing_cart.length > 0) { var total = ''; $$('#cart_div').show(); existing_cart.forEach(function (arrayItem) { var text = ''; text = '<li class="item-content"><div class="item-inner"><div class="item-title">' + arrayItem.doc_title + '</div><div class="item-after">' + arrayItem.course_id + '</div><div class="item-after">' + arrayItem.doc_price + '</div><div class="item-after"><i class="icon icon-cross" onclick="removeFromCart(' + arrayItem.course_id + ',' + arrayItem.doc_id + ')"></i></div></div></li>'; total = Number(total) + Number(arrayItem.doc_price); $$('#cart_body').append(text); }); text = '<tr><td></td><td class="text-center"><b>Total: </b></td><td class="text-center">' + total + '</td><td></td></tr>'; $$('#cart_body').append(text); } else { $$('#cart_div').hide(); text = '<p>Your cart is empty.</p>'; $$('#cart_body').append(text); } } else { return false; } }
Instead of: $.grep(existing_cart, function ... You can use: existing_cart.filter(function ...
var new_courses = existing_cart.map( v=> { if(v.doc_id != doc_id) return v }).filter( v=> {return v}) // new_courses does not contain the course with doc_id map loops through each member of an array. filter removes members not returned in map.
Add Campaign Selector in Adwords Script
I have a bid-to-position script that targets keywords that have a label associated with them. The label contains the desired position and the script adjusts the keyword's bid in order to reach that position. Right now the script targets any keyword with the label. I'm trying to edit the script so it will look for labeled keywords in campaigns that I choose. I tried adding .withCondition(CampaignName = ' My Campaign Name'") to the labelIterator variable but had no luck. Can anyone point me in the right direction? /** * * Average Position Bidding Tool * * This script changes keyword bids so that they target specified positions, * based on recent performance. * * Version: 1.2 * Updated 2015-09-28 to correct for report column name changes * Updated 2016-02-05 to correct label reading, add extra checks and * be able to adjust maximum bid increases and decreases separately * Google AdWords Script maintained on brainlabsdigital.com * **/ // Options var maxBid = 5.00; // Bids will not be increased past this maximum. var minBid = 0.10; // Bids will not be decreased below this minimum. var firstPageMaxBid = 1.00; // The script avoids reducing a keyword's bid below its first page bid estimate. If you think // Google's first page bid estimates are too high then use this to overrule them. var dataFile = "AveragePositionData.txt"; // This name is used to create a file in your Google Drive to store today's performance so far, // for reference the next time the script is run. var useFirstPageBidsOnKeywordsWithNoImpressions = false; // If this is true, then if a keyword has had no impressions since the last time the script was run // its bid will be increased to the first page bid estimate (or the firsPageMaxBid if that is smaller). // If this is false, keywords with no recent impressions will be left alone. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Advanced Options var bidIncreaseProportion = 0.2; var bidDecreaseProportion = 0.4; var targetPositionTolerance = 0.2; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function main() { var fieldJoin = ","; var lineJoin = "$"; var idJoin = "#"; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var files = DriveApp.getFilesByName(dataFile); if (!files.hasNext()) { var file = DriveApp.createFile(dataFile,""); Logger.log("File '" + dataFile + "' has been created."); } else { var file = files.next(); if (files.hasNext()) { Logger.log("Error - more than one file named '" + dataFile + "'"); return; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var labelIds = []; var labelIterator = AdWordsApp.labels() .withCondition("CampaignName CONTAINS_IGNORE_CASE 'MY CAMPAIGN NAME' ") .withCondition("KeywordsCount > 0") .withCondition("LabelName CONTAINS_IGNORE_CASE 'Position '") .get(); while (labelIterator.hasNext()) { var label = labelIterator.next(); if (label.getName().substr(0,"position ".length).toLowerCase() == "position ") { labelIds.push(label.getId()); } } if (labelIds.length == 0) { Logger.log("No position labels found."); return; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var keywordData = { //UniqueId1: {LastHour: {Impressions: , AveragePosition: }, ThisHour: {Impressions: , AveragePosition: }, //CpcBid: , FirstPageCpc: , MaxBid, MinBid, FirstPageMaxBid, PositionTarget: , CurrentAveragePosition:, //Criteria: } } var ids = []; var uniqueIds = []; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var report = AdWordsApp.report( 'SELECT Id, Criteria, AdGroupId, AdGroupName, CampaignName, Impressions, AveragePosition, CpcBid, FirstPageCpc, Labels, BiddingStrategyType ' + 'FROM KEYWORDS_PERFORMANCE_REPORT ' + 'WHERE Status = ENABLED AND AdGroupStatus = ENABLED AND CampaignStatus = ENABLED ' + 'AND LabelIds CONTAINS_ANY [' + labelIds.join(",") + '] ' + 'AND AdNetworkType2 = SEARCH ' + 'AND Device NOT_IN ["HIGH_END_MOBILE"] ' + 'DURING TODAY' ); var rows = report.rows(); while(rows.hasNext()){ var row = rows.next(); if (row["BiddingStrategyType"] != "cpc") { if (row["BiddingStrategyType"] == "Enhanced CPC" || row["BiddingStrategyType"] == "Target search page location" || row["BiddingStrategyType"] == "Target Outranking Share" || row["BiddingStrategyType"] == "None" || row["BiddingStrategyType"] == "unknown") { Logger.log("Warning: keyword " + row["Criteria"] + "' in campaign '" + row["CampaignName"] + "' uses '" + row["BiddingStrategyType"] + "' rather than manual CPC. This may overrule keyword bids and interfere with the script working."); } else { Logger.log("Warning: keyword " + row["Criteria"] + "' in campaign '" + row["CampaignName"] + "' uses the bidding strategy '" + row["BiddingStrategyType"] + "' rather than manual CPC. This keyword will be skipped."); continue; } } var positionTarget = ""; var labels = row["Labels"].toLowerCase().split("; ") for (var i=0; i<labels.length; i++) { if (labels[i].substr(0,"position ".length) == "position ") { var positionTarget = parseFloat(labels[i].substr("position ".length-1).replace(/,/g,"."),10); break; } } if (positionTarget == "") { continue; } if (integrityCheck(positionTarget) == -1) { Logger.log("Invalid position target '" + positionTarget + "' for keyword '" + row["Criteria"] + "' in campaign '" + row["CampaignName"] + "'"); continue; } ids.push(parseFloat(row['Id'],10)); var uniqueId = row['AdGroupId'] + idJoin + row['Id']; uniqueIds.push(uniqueId); keywordData[uniqueId] = {}; keywordData[uniqueId]['Criteria'] = row['Criteria']; keywordData[uniqueId]['ThisHour'] = {}; keywordData[uniqueId]['ThisHour']['Impressions'] = parseFloat(row['Impressions'].replace(/,/g,""),10); keywordData[uniqueId]['ThisHour']['AveragePosition'] = parseFloat(row['AveragePosition'].replace(/,/g,""),10); keywordData[uniqueId]['CpcBid'] = parseFloat(row['CpcBid'].replace(/,/g,""),10); keywordData[uniqueId]['FirstPageCpc'] = parseFloat(row['FirstPageCpc'].replace(/,/g,""),10); setPositionTargets(uniqueId, positionTarget); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// setBidChange(); setMinMaxBids(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var currentHour = parseInt(Utilities.formatDate(new Date(), AdWordsApp.currentAccount().getTimeZone(), "HH"), 10); if (currentHour != 0) { var data = file.getBlob().getDataAsString(); var data = data.split(lineJoin); for(var i = 0; i < data.length; i++){ data[i] = data[i].split(fieldJoin); var uniqueId = data[i][0]; if(keywordData.hasOwnProperty(uniqueId)){ keywordData[uniqueId]['LastHour'] = {}; keywordData[uniqueId]['LastHour']['Impressions'] = parseFloat(data[i][1],10); keywordData[uniqueId]['LastHour']['AveragePosition'] = parseFloat(data[i][2],10); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// findCurrentAveragePosition(); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// try { updateKeywords(); } catch (e) { Logger.log("Error updating keywords: " + e); Logger.log("Retrying after one minute."); Utilities.sleep(60000); updateKeywords(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// var content = resultsString(); file.setContent(content); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // Functions //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function integrityCheck(target){ var n = parseFloat(target, 10); if(!isNaN(n) && n >= 1){ return n; } else{ return -1; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function setPositionTargets(uniqueId, target){ if(target !== -1){ keywordData[uniqueId]['HigherPositionTarget'] = Math.max(target-targetPositionTolerance, 1); keywordData[uniqueId]['LowerPositionTarget'] = target+targetPositionTolerance; } else{ keywordData[uniqueId]['HigherPositionTarget'] = -1; keywordData[uniqueId]['LowerPositionTarget'] = -1; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function bidChange(uniqueId){ var newBid = -1; if(keywordData[uniqueId]['HigherPositionTarget'] === -1){ return newBid; } var cpcBid = keywordData[uniqueId]['CpcBid']; var minBid = keywordData[uniqueId]['MinBid']; var maxBid = keywordData[uniqueId]['MaxBid']; if (isNaN(keywordData[uniqueId]['FirstPageCpc'])) { Logger.log("Warning: first page CPC estimate is not a number for keyword '" + keywordData[uniqueId]['Criteria'] + "'. This keyword will be skipped"); return -1; } var firstPageBid = Math.min(keywordData[uniqueId]['FirstPageCpc'], keywordData[uniqueId]['FirstPageMaxBid'], maxBid); var currentPosition = keywordData[uniqueId]['CurrentAveragePosition']; var higherPositionTarget = keywordData[uniqueId]['HigherPositionTarget']; var lowerPositionTarget = keywordData[uniqueId]['LowerPositionTarget']; var bidIncrease = keywordData[uniqueId]['BidIncrease']; var bidDecrease = keywordData[uniqueId]['BidDecrease']; if((currentPosition > lowerPositionTarget) && (currentPosition !== 0)){ var linearBidModel = Math.min(2*bidIncrease,(2*bidIncrease/lowerPositionTarget)*(currentPosition-lowerPositionTarget)); var newBid = Math.min((cpcBid + linearBidModel), maxBid); } if((currentPosition < higherPositionTarget) && (currentPosition !== 0)) { var linearBidModel = Math.min(2*bidDecrease,((-4)*bidDecrease/higherPositionTarget)*(currentPosition-higherPositionTarget)); var newBid = Math.max((cpcBid-linearBidModel),minBid); if (cpcBid > firstPageBid) { var newBid = Math.max(firstPageBid,newBid); } } if((currentPosition === 0) && useFirstPageBidsOnKeywordsWithNoImpressions && (cpcBid < firstPageBid)){ var newBid = firstPageBid; } if (isNaN(newBid)) { Logger.log("Warning: new bid is not a number for keyword '" + keywordData[uniqueId]['Criteria'] + "'. This keyword will be skipped"); return -1; } return newBid; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function findCurrentAveragePosition(){ for(var x in keywordData){ if(keywordData[x].hasOwnProperty('LastHour')){ keywordData[x]['CurrentAveragePosition'] = calculateAveragePosition(keywordData[x]); } else { keywordData[x]['CurrentAveragePosition'] = keywordData[x]['ThisHour']['AveragePosition']; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function calculateAveragePosition(keywordDataElement){ var lastHourImpressions = keywordDataElement['LastHour']['Impressions']; var lastHourAveragePosition = keywordDataElement['LastHour']['AveragePosition']; var thisHourImpressions = keywordDataElement['ThisHour']['Impressions']; var thisHourAveragePosition = keywordDataElement['ThisHour']['AveragePosition']; if(thisHourImpressions == lastHourImpressions){ return 0; } else{ var currentPosition = (thisHourImpressions*thisHourAveragePosition-lastHourImpressions*lastHourAveragePosition)/(thisHourImpressions-lastHourImpressions); if (currentPosition < 1) { return 0; } else { return currentPosition; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function keywordUniqueId(keyword){ var id = keyword.getId(); var idsIndex = ids.indexOf(id); if(idsIndex === ids.lastIndexOf(id)){ return uniqueIds[idsIndex]; } else{ var adGroupId = keyword.getAdGroup().getId(); return adGroupId + idJoin + id; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function setMinMaxBids(){ for(var x in keywordData){ keywordData[x]['MinBid'] = minBid; keywordData[x]['MaxBid'] = maxBid; keywordData[x]['FirstPageMaxBid'] = firstPageMaxBid; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function setBidChange(){ for(var x in keywordData){ keywordData[x]['BidIncrease'] = keywordData[x]['CpcBid'] * bidIncreaseProportion/2; keywordData[x]['BidDecrease'] = keywordData[x]['CpcBid'] * bidDecreaseProportion/2; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function updateKeywords() { var keywordIterator = AdWordsApp.keywords() .withIds(uniqueIds.map(function(str){return str.split(idJoin);})) .get(); while(keywordIterator.hasNext()){ var keyword = keywordIterator.next(); var uniqueId = keywordUniqueId(keyword); var newBid = bidChange(uniqueId); if(newBid !== -1){ keyword.setMaxCpc(newBid); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// function resultsString(){ var results = []; for(var uniqueId in keywordData){ var resultsRow = [uniqueId, keywordData[uniqueId]['ThisHour']['Impressions'], keywordData[uniqueId]['ThisHour']['AveragePosition']]; results.push(resultsRow.join(fieldJoin)); } return results.join(lineJoin); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// }
so CampaignName isn't a valid with condition for the label selector. What you need to do instead is have a Campaign Selector in a while look before you come to your Label selector, which then feeds from the campaign iteration. I've done a quick and dirty example below, but of course, you'd have to take a look to see if doing this will require other changes later on (or earlier on) in your code. var labelIds = []; var campaignIterator = AdWordsApp.campaigns() .withCondition("CampaignName CONTAINS_IGNORE_CASE 'MY CAMPAIGN NAME' ") .get() while (campaignIterator.hasNext()) { var campaign = campaignIterator.next() var labelIterator = campaign.labels() .withCondition("CampaignName CONTAINS_IGNORE_CASE 'MY CAMPAIGN NAME' ") .withCondition("KeywordsCount > 0") .withCondition("LabelName CONTAINS_IGNORE_CASE 'Position '") .get(); while (labelIterator.hasNext()) { var label = labelIterator.next(); if (label.getName().substr(0,"position ".length).toLowerCase() == "position ") { labelIds.push(label.getId()); } } if (labelIds.length == 0) { Logger.log("No position labels found."); return; } } var labelIterator = AdWordsApp.labels()
SyntaxError: let is a reserved identifier on firefox
I am using those code below 'use strict'; jQuery(document).ready(function($) { function CMB2ConditionalsInit(context) { if(typeof context === 'undefined') { context = 'body'; } $('[data-conditional-id]', context).each(function(i, e) { var $e = $(e), id = $e.data('conditional-id'), value = $e.data('conditional-value'); var $element = $('[name="' + id + '"]'), $parent = $e.parents('.cmb-row:first').hide(); $e.data('conditional-required', $e.prop('required')).prop('required', false); $element .on('change', function(evt){ let conditionValue = CMB2ConditionalsStringToUnicode(evt.currentTarget.value); if(typeof value === 'undefined') { CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"]', ($element.val() ? true : false)); } else { CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"]:not([data-conditional-value="' + conditionValue + '"])', false); CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"][data-conditional-value="' + conditionValue + '"]', true); CMB2ConditionalToggleRows('[data-conditional-id="' + id + '"][data-conditional-value*=\'"' + conditionValue + '"\']', true); } }); if($element.length === 1) { $element.trigger('change'); } else { $element.filter(':checked').trigger('change'); } }); } function CMB2ConditionalsStringToUnicode(string){ let result = '', map = ["Á", "Ă","Ắ","Ặ","Ằ","Ẳ","Ẵ","Ǎ","Â","Ấ","Ậ","Ầ","Ẩ","Ẫ","Ä","Ǟ","Ȧ","Ǡ","Ạ","Ȁ","À","Ả","Ȃ","Ā","Ą","Å","Ǻ","Ḁ","Ⱥ","Ã","Ꜳ","Æ","Ǽ","Ǣ","Ꜵ","Ꜷ","Ꜹ","Ꜻ","Ꜽ","Ḃ","Ḅ","Ɓ","Ḇ","Ƀ","Ƃ","Ć","Č","Ç","Ḉ","Ĉ","Ċ","Ƈ","Ȼ","Ď","Ḑ","Ḓ","Ḋ","Ḍ","Ɗ","Ḏ","Dz","Dž","Đ","Ƌ","DZ","DŽ","É","Ĕ","Ě","Ȩ","Ḝ","Ê","Ế","Ệ","Ề","Ể","Ễ","Ḙ","Ë","Ė","Ẹ","Ȅ","È","Ẻ","Ȇ","Ē","Ḗ","Ḕ","Ę","Ɇ","Ẽ","Ḛ","Ꝫ","Ḟ","Ƒ","Ǵ","Ğ","Ǧ","Ģ","Ĝ","Ġ","Ɠ","Ḡ","Ǥ","Ḫ","Ȟ","Ḩ","Ĥ","Ⱨ","Ḧ","Ḣ","Ḥ","Ħ","Í","Ĭ","Ǐ","Î","Ï","Ḯ","İ","Ị","Ȉ","Ì","Ỉ","Ȋ","Ī","Į","Ɨ","Ĩ","Ḭ","Ꝺ","Ꝼ","Ᵹ","Ꞃ","Ꞅ","Ꞇ","Ꝭ","Ĵ","Ɉ","Ḱ","Ǩ","Ķ","Ⱪ","Ꝃ","Ḳ","Ƙ","Ḵ","Ꝁ","Ꝅ","Ĺ","Ƚ","Ľ","Ļ","Ḽ","Ḷ","Ḹ","Ⱡ","Ꝉ","Ḻ","Ŀ","Ɫ","Lj","Ł","LJ","Ḿ","Ṁ","Ṃ","Ɱ","Ń","Ň","Ņ","Ṋ","Ṅ","Ṇ","Ǹ","Ɲ","Ṉ","Ƞ","Nj","Ñ","NJ","Ó","Ŏ","Ǒ","Ô","Ố","Ộ","Ồ","Ổ","Ỗ","Ö","Ȫ","Ȯ","Ȱ","Ọ","Ő","Ȍ","Ò","Ỏ","Ơ","Ớ","Ợ","Ờ","Ở","Ỡ","Ȏ","Ꝋ","Ꝍ","Ō","Ṓ","Ṑ","Ɵ","Ǫ","Ǭ","Ø","Ǿ","Õ","Ṍ","Ṏ","Ȭ","Ƣ","Ꝏ","Ɛ","Ɔ","Ȣ","Ṕ","Ṗ","Ꝓ","Ƥ","Ꝕ","Ᵽ","Ꝑ","Ꝙ","Ꝗ","Ŕ","Ř","Ŗ","Ṙ","Ṛ","Ṝ","Ȑ","Ȓ","Ṟ","Ɍ","Ɽ","Ꜿ","Ǝ","Ś","Ṥ","Š","Ṧ","Ş","Ŝ","Ș","Ṡ","Ṣ","Ṩ","Ť","Ţ","Ṱ","Ț","Ⱦ","Ṫ","Ṭ","Ƭ","Ṯ","Ʈ","Ŧ","Ɐ","Ꞁ","Ɯ","Ʌ","Ꜩ","Ú","Ŭ","Ǔ","Û","Ṷ","Ü","Ǘ","Ǚ","Ǜ","Ǖ","Ṳ","Ụ","Ű","Ȕ","Ù","Ủ","Ư","Ứ","Ự","Ừ","Ử","Ữ","Ȗ","Ū","Ṻ","Ų","Ů","Ũ","Ṹ","Ṵ","Ꝟ","Ṿ","Ʋ","Ṽ","Ꝡ","Ẃ","Ŵ","Ẅ","Ẇ","Ẉ","Ẁ","Ⱳ","Ẍ","Ẋ","Ý","Ŷ","Ÿ","Ẏ","Ỵ","Ỳ","Ƴ","Ỷ","Ỿ","Ȳ","Ɏ","Ỹ","Ź","Ž","Ẑ","Ⱬ","Ż","Ẓ","Ȥ","Ẕ","Ƶ","IJ","Œ","ᴀ","ᴁ","ʙ","ᴃ","ᴄ","ᴅ","ᴇ","ꜰ","ɢ","ʛ","ʜ","ɪ","ʁ","ᴊ","ᴋ","ʟ","ᴌ","ᴍ","ɴ","ᴏ","ɶ","ᴐ","ᴕ","ᴘ","ʀ","ᴎ","ᴙ","ꜱ","ᴛ","ⱻ","ᴚ","ᴜ","ᴠ","ᴡ","ʏ","ᴢ","á","ă","ắ","ặ","ằ","ẳ","ẵ","ǎ","â","ấ","ậ","ầ","ẩ","ẫ","ä","ǟ","ȧ","ǡ","ạ","ȁ","à","ả","ȃ","ā","ą","ᶏ","ẚ","å","ǻ","ḁ","ⱥ","ã","ꜳ","æ","ǽ","ǣ","ꜵ","ꜷ","ꜹ","ꜻ","ꜽ","ḃ","ḅ","ɓ","ḇ","ᵬ","ᶀ","ƀ","ƃ","ɵ","ć","č","ç","ḉ","ĉ","ɕ","ċ","ƈ","ȼ","ď","ḑ","ḓ","ȡ","ḋ","ḍ","ɗ","ᶑ","ḏ","ᵭ","ᶁ","đ","ɖ","ƌ","ı","ȷ","ɟ","ʄ","dz","dž","é","ĕ","ě","ȩ","ḝ","ê","ế","ệ","ề","ể","ễ","ḙ","ë","ė","ẹ","ȅ","è","ẻ","ȇ","ē","ḗ","ḕ","ⱸ","ę","ᶒ","ɇ","ẽ","ḛ","ꝫ","ḟ","ƒ","ᵮ","ᶂ","ǵ","ğ","ǧ","ģ","ĝ","ġ","ɠ","ḡ","ᶃ","ǥ","ḫ","ȟ","ḩ","ĥ","ⱨ","ḧ","ḣ","ḥ","ɦ","ẖ","ħ","ƕ","í","ĭ","ǐ","î","ï","ḯ","ị","ȉ","ì","ỉ","ȋ","ī","į","ᶖ","ɨ","ĩ","ḭ","ꝺ","ꝼ","ᵹ","ꞃ","ꞅ","ꞇ","ꝭ","ǰ","ĵ","ʝ","ɉ","ḱ","ǩ","ķ","ⱪ","ꝃ","ḳ","ƙ","ḵ","ᶄ","ꝁ","ꝅ","ĺ","ƚ","ɬ","ľ","ļ","ḽ","ȴ","ḷ","ḹ","ⱡ","ꝉ","ḻ","ŀ","ɫ","ᶅ","ɭ","ł","lj","ſ","ẜ","ẛ","ẝ","ḿ","ṁ","ṃ","ɱ","ᵯ","ᶆ","ń","ň","ņ","ṋ","ȵ","ṅ","ṇ","ǹ","ɲ","ṉ","ƞ","ᵰ","ᶇ","ɳ","ñ","nj","ó","ŏ","ǒ","ô","ố","ộ","ồ","ổ","ỗ","ö","ȫ","ȯ","ȱ","ọ","ő","ȍ","ò","ỏ","ơ","ớ","ợ","ờ","ở","ỡ","ȏ","ꝋ","ꝍ","ⱺ","ō","ṓ","ṑ","ǫ","ǭ","ø","ǿ","õ","ṍ","ṏ","ȭ","ƣ","ꝏ","ɛ","ᶓ","ɔ","ᶗ","ȣ","ṕ","ṗ","ꝓ","ƥ","ᵱ","ᶈ","ꝕ","ᵽ","ꝑ","ꝙ","ʠ","ɋ","ꝗ","ŕ","ř","ŗ","ṙ","ṛ","ṝ","ȑ","ɾ","ᵳ","ȓ","ṟ","ɼ","ᵲ","ᶉ","ɍ","ɽ","ↄ","ꜿ","ɘ","ɿ","ś","ṥ","š","ṧ","ş","ŝ","ș","ṡ","ṣ","ṩ","ʂ","ᵴ","ᶊ","ȿ","ɡ","ᴑ","ᴓ","ᴝ","ť","ţ","ṱ","ț","ȶ","ẗ","ⱦ","ṫ","ṭ","ƭ","ṯ","ᵵ","ƫ","ʈ","ŧ","ᵺ","ɐ","ᴂ","ǝ","ᵷ","ɥ","ʮ","ʯ","ᴉ","ʞ","ꞁ","ɯ","ɰ","ᴔ","ɹ","ɻ","ɺ","ⱹ","ʇ","ʌ","ʍ","ʎ","ꜩ","ú","ŭ","ǔ","û","ṷ","ü","ǘ","ǚ","ǜ","ǖ","ṳ","ụ","ű","ȕ","ù","ủ","ư","ứ","ự","ừ","ử","ữ","ȗ","ū","ṻ","ų","ᶙ","ů","ũ","ṹ","ṵ","ᵫ","ꝸ","ⱴ","ꝟ","ṿ","ʋ","ᶌ","ⱱ","ṽ","ꝡ","ẃ","ŵ","ẅ","ẇ","ẉ","ẁ","ⱳ","ẘ","ẍ","ẋ","ᶍ","ý","ŷ","ÿ","ẏ","ỵ","ỳ","ƴ","ỷ","ỿ","ȳ","ẙ","ɏ","ỹ","ź","ž","ẑ","ʑ","ⱬ","ż","ẓ","ȥ","ẕ","ᵶ","ᶎ","ʐ","ƶ","ɀ","ff","ffi","ffl","fi","fl","ij","œ","st","ₐ","ₑ","ᵢ","ⱼ","ₒ","ᵣ","ᵤ","ᵥ","ₓ"]; for(let i = 0; i < string.length; i++){ if(jQuery.inArray(string[i], map) === -1) { result += string[i] } else { result += "\\\\u" + ("000" + string[i].charCodeAt(0).toString(16)).substr(-4); } } return result; }; function CMB2ConditionalToggleRows(obj, showOrHide){ var $elements = (obj instanceof jQuery) ? obj : $(obj); return $elements.each(function(i, e) { let $e = $(e); $e.prop('required', showOrHide && $e.data('conditional-required')); $e.parents('.cmb-row:first').toggle(showOrHide); }); } CMB2ConditionalsInit('#post'); }); But it is giving me error below After searched stackoverflow, I got that it will not work on firefox. Is there any way to fix it? BTW, other browser working fine. Thanks in advance.
As you can see the let keyword isn't supported on FF yet: https://kangax.github.io/compat-table/es6/ You will need to change it to var, or transpile your code with babel
What is the JavaScript equivalent of var_dump or print_r in PHP? [duplicate]
This question already has answers here: Is there an equivalent for var_dump (PHP) in Javascript? (20 answers) Closed 5 years ago. I would like to see the structure of object in JavaScript (for debugging). Is there anything similar to var_dump in PHP?
Most modern browsers have a console in their developer tools, useful for this sort of debugging. console.log(myvar); Then you will get a nicely mapped out interface of the object/whatever in the console. Check out the console documentation for more details.
Most common way: console.log(object); However I must mention JSON.stringify which is useful to dump variables in non-browser scripts: console.log( JSON.stringify(object) ); The JSON.stringify function also supports built-in prettification as pointed out by Simon Zyx. Example: var obj = {x: 1, y: 2, z: 3}; console.log( JSON.stringify(obj, null, 2) ); // spacing level = 2 The above snippet will print: { "x": 1, "y": 2, "z": 3 } On caniuse.com you can view the browsers that support natively the JSON.stringify function: http://caniuse.com/json You can also use the Douglas Crockford library to add JSON.stringify support on old browsers: https://github.com/douglascrockford/JSON-js Docs for JSON.stringify: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify I hope this helps :-)
I wrote this JS function dump() to work like PHP's var_dump(). To show the contents of the variable in an alert window: dump(variable) To show the contents of the variable in the web page: dump(variable, 'body') To just get a string of the variable: dump(variable, 'none') /* repeatString() returns a string which has been repeated a set number of times */ function repeatString(str, num) { out = ''; for (var i = 0; i < num; i++) { out += str; } return out; } /* dump() displays the contents of a variable like var_dump() does in PHP. dump() is better than typeof, because it can distinguish between array, null and object. Parameters: v: The variable howDisplay: "none", "body", "alert" (default) recursionLevel: Number of times the function has recursed when entering nested objects or arrays. Each level of recursion adds extra space to the output to indicate level. Set to 0 by default. Return Value: A string of the variable's contents Limitations: Can't pass an undefined variable to dump(). dump() can't distinguish between int and float. dump() can't tell the original variable type of a member variable of an object. These limitations can't be fixed because these are *features* of JS. However, dump() */ function dump(v, howDisplay, recursionLevel) { howDisplay = (typeof howDisplay === 'undefined') ? "alert" : howDisplay; recursionLevel = (typeof recursionLevel !== 'number') ? 0 : recursionLevel; var vType = typeof v; var out = vType; switch (vType) { case "number": /* there is absolutely no way in JS to distinguish 2 from 2.0 so 'number' is the best that you can do. The following doesn't work: var er = /^[0-9]+$/; if (!isNaN(v) && v % 1 === 0 && er.test(3.0)) { out = 'int'; } */ break; case "boolean": out += ": " + v; break; case "string": out += "(" + v.length + '): "' + v + '"'; break; case "object": //check if null if (v === null) { out = "null"; } //If using jQuery: if ($.isArray(v)) //If using IE: if (isArray(v)) //this should work for all browsers according to the ECMAScript standard: else if (Object.prototype.toString.call(v) === '[object Array]') { out = 'array(' + v.length + '): {\n'; for (var i = 0; i < v.length; i++) { out += repeatString(' ', recursionLevel) + " [" + i + "]: " + dump(v[i], "none", recursionLevel + 1) + "\n"; } out += repeatString(' ', recursionLevel) + "}"; } else { //if object let sContents = "{\n"; let cnt = 0; for (var member in v) { //No way to know the original data type of member, since JS //always converts it to a string and no other way to parse objects. sContents += repeatString(' ', recursionLevel) + " " + member + ": " + dump(v[member], "none", recursionLevel + 1) + "\n"; cnt++; } sContents += repeatString(' ', recursionLevel) + "}"; out += "(" + cnt + "): " + sContents; } break; default: out = v; break; } if (howDisplay == 'body') { var pre = document.createElement('pre'); pre.innerHTML = out; document.body.appendChild(pre); } else if (howDisplay == 'alert') { alert(out); } return out; }
The var_dump equivalent in JavaScript? Simply, there isn't one. But, that doesn't mean you're left helpless. Like some have suggested, use Firebug (or equivalent in other browsers), but unlike what others suggested, don't use console.log when you have a (slightly) better tool console.dir: console.dir(object) Prints an interactive listing of all properties of the object. This looks identical to the view that you would see in the DOM tab.
As others have already mentioned, the best way to debug your variables is to use a modern browser's developer console (e.g. Chrome Developer Tools, Firefox+Firebug, Opera Dragonfly (which now disappeared in the new Chromium-based (Blink) Opera, but as developers say, "Dragonfly is not dead though we cannot give you more information yet"). But in case you need another approach, there's a really useful site called php.js: http://phpjs.org/ which provides "JavaScript alternatives to PHP functions" - so you can use them the similar way as you would in PHP. I will copy-paste the appropriate functions to you here, BUT be aware that these codes can get updated on the original site in case some bugs are detected, so I suggest you visiting the phpjs.org site! (Btw. I'm NOT affiliated with the site, but I find it extremely useful.) var_dump() in JavaScript Here is the code of the JS-alternative of var_dump(): http://phpjs.org/functions/var_dump/ it depends on the echo() function: http://phpjs.org/functions/echo/ function var_dump() { // discuss at: http://phpjs.org/functions/var_dump/ // original by: Brett Zamir (http://brett-zamir.me) // improved by: Zahlii // improved by: Brett Zamir (http://brett-zamir.me) // depends on: echo // note: For returning a string, use var_export() with the second argument set to true // test: skip // example 1: var_dump(1); // returns 1: 'int(1)' var output = '', pad_char = ' ', pad_val = 4, lgth = 0, i = 0; var _getFuncName = function(fn) { var name = (/\W*function\s+([\w\$]+)\s*\(/) .exec(fn); if (!name) { return '(Anonymous)'; } return name[1]; }; var _repeat_char = function(len, pad_char) { var str = ''; for (var i = 0; i < len; i++) { str += pad_char; } return str; }; var _getInnerVal = function(val, thick_pad) { var ret = ''; if (val === null) { ret = 'NULL'; } else if (typeof val === 'boolean') { ret = 'bool(' + val + ')'; } else if (typeof val === 'string') { ret = 'string(' + val.length + ') "' + val + '"'; } else if (typeof val === 'number') { if (parseFloat(val) == parseInt(val, 10)) { ret = 'int(' + val + ')'; } else { ret = 'float(' + val + ')'; } } // The remaining are not PHP behavior because these values only exist in this exact form in JavaScript else if (typeof val === 'undefined') { ret = 'undefined'; } else if (typeof val === 'function') { var funcLines = val.toString() .split('\n'); ret = ''; for (var i = 0, fll = funcLines.length; i < fll; i++) { ret += (i !== 0 ? '\n' + thick_pad : '') + funcLines[i]; } } else if (val instanceof Date) { ret = 'Date(' + val + ')'; } else if (val instanceof RegExp) { ret = 'RegExp(' + val + ')'; } else if (val.nodeName) { // Different than PHP's DOMElement switch (val.nodeType) { case 1: if (typeof val.namespaceURI === 'undefined' || val.namespaceURI === 'http://www.w3.org/1999/xhtml') { // Undefined namespace could be plain XML, but namespaceURI not widely supported ret = 'HTMLElement("' + val.nodeName + '")'; } else { ret = 'XML Element("' + val.nodeName + '")'; } break; case 2: ret = 'ATTRIBUTE_NODE(' + val.nodeName + ')'; break; case 3: ret = 'TEXT_NODE(' + val.nodeValue + ')'; break; case 4: ret = 'CDATA_SECTION_NODE(' + val.nodeValue + ')'; break; case 5: ret = 'ENTITY_REFERENCE_NODE'; break; case 6: ret = 'ENTITY_NODE'; break; case 7: ret = 'PROCESSING_INSTRUCTION_NODE(' + val.nodeName + ':' + val.nodeValue + ')'; break; case 8: ret = 'COMMENT_NODE(' + val.nodeValue + ')'; break; case 9: ret = 'DOCUMENT_NODE'; break; case 10: ret = 'DOCUMENT_TYPE_NODE'; break; case 11: ret = 'DOCUMENT_FRAGMENT_NODE'; break; case 12: ret = 'NOTATION_NODE'; break; } } return ret; }; var _formatArray = function(obj, cur_depth, pad_val, pad_char) { var someProp = ''; if (cur_depth > 0) { cur_depth++; } var base_pad = _repeat_char(pad_val * (cur_depth - 1), pad_char); var thick_pad = _repeat_char(pad_val * (cur_depth + 1), pad_char); var str = ''; var val = ''; if (typeof obj === 'object' && obj !== null) { if (obj.constructor && _getFuncName(obj.constructor) === 'PHPJS_Resource') { return obj.var_dump(); } lgth = 0; for (someProp in obj) { lgth++; } str += 'array(' + lgth + ') {\n'; for (var key in obj) { var objVal = obj[key]; if (typeof objVal === 'object' && objVal !== null && !(objVal instanceof Date) && !(objVal instanceof RegExp) && ! objVal.nodeName) { str += thick_pad + '[' + key + '] =>\n' + thick_pad + _formatArray(objVal, cur_depth + 1, pad_val, pad_char); } else { val = _getInnerVal(objVal, thick_pad); str += thick_pad + '[' + key + '] =>\n' + thick_pad + val + '\n'; } } str += base_pad + '}\n'; } else { str = _getInnerVal(obj, thick_pad); } return str; }; output = _formatArray(arguments[0], 0, pad_val, pad_char); for (i = 1; i < arguments.length; i++) { output += '\n' + _formatArray(arguments[i], 0, pad_val, pad_char); } this.echo(output); } print_r() in JavaScript Here is the print_r() function: http://phpjs.org/functions/print_r/ It depends on echo() too. function print_r(array, return_val) { // discuss at: http://phpjs.org/functions/print_r/ // original by: Michael White (http://getsprink.com) // improved by: Ben Bryan // improved by: Brett Zamir (http://brett-zamir.me) // improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) // input by: Brett Zamir (http://brett-zamir.me) // depends on: echo // example 1: print_r(1, true); // returns 1: 1 var output = '', pad_char = ' ', pad_val = 4, d = this.window.document, getFuncName = function(fn) { var name = (/\W*function\s+([\w\$]+)\s*\(/) .exec(fn); if (!name) { return '(Anonymous)'; } return name[1]; }; repeat_char = function(len, pad_char) { var str = ''; for (var i = 0; i < len; i++) { str += pad_char; } return str; }; formatArray = function(obj, cur_depth, pad_val, pad_char) { if (cur_depth > 0) { cur_depth++; } var base_pad = repeat_char(pad_val * cur_depth, pad_char); var thick_pad = repeat_char(pad_val * (cur_depth + 1), pad_char); var str = ''; if (typeof obj === 'object' && obj !== null && obj.constructor && getFuncName(obj.constructor) !== 'PHPJS_Resource') { str += 'Array\n' + base_pad + '(\n'; for (var key in obj) { if (Object.prototype.toString.call(obj[key]) === '[object Array]') { str += thick_pad + '[' + key + '] => ' + formatArray(obj[key], cur_depth + 1, pad_val, pad_char); } else { str += thick_pad + '[' + key + '] => ' + obj[key] + '\n'; } } str += base_pad + ')\n'; } else if (obj === null || obj === undefined) { str = ''; } else { // for our "resource" class str = obj.toString(); } return str; }; output = formatArray(array, 0, pad_val, pad_char); if (return_val !== true) { if (d.body) { this.echo(output); } else { try { // We're in XUL, so appending as plain text won't work; trigger an error out of XUL d = XULDocument; this.echo('<pre xmlns="http://www.w3.org/1999/xhtml" style="white-space:pre;">' + output + '</pre>'); } catch (e) { // Outputting as plain text may work in some plain XML this.echo(output); } } return true; } return output; } var_export() in JavaScript You may also find the var_export() alternative useful, which also depends on echo(): http://phpjs.org/functions/var_export/ function var_export(mixed_expression, bool_return) { // discuss at: http://phpjs.org/functions/var_export/ // original by: Philip Peterson // improved by: johnrembo // improved by: Brett Zamir (http://brett-zamir.me) // input by: Brian Tafoya (http://www.premasolutions.com/) // input by: Hans Henrik (http://hanshenrik.tk/) // bugfixed by: Brett Zamir (http://brett-zamir.me) // bugfixed by: Brett Zamir (http://brett-zamir.me) // depends on: echo // example 1: var_export(null); // returns 1: null // example 2: var_export({0: 'Kevin', 1: 'van', 2: 'Zonneveld'}, true); // returns 2: "array (\n 0 => 'Kevin',\n 1 => 'van',\n 2 => 'Zonneveld'\n)" // example 3: data = 'Kevin'; // example 3: var_export(data, true); // returns 3: "'Kevin'" var retstr = '', iret = '', value, cnt = 0, x = [], i = 0, funcParts = [], // We use the last argument (not part of PHP) to pass in // our indentation level idtLevel = arguments[2] || 2, innerIndent = '', outerIndent = '', getFuncName = function(fn) { var name = (/\W*function\s+([\w\$]+)\s*\(/) .exec(fn); if (!name) { return '(Anonymous)'; } return name[1]; }; _makeIndent = function(idtLevel) { return (new Array(idtLevel + 1)) .join(' '); }; __getType = function(inp) { var i = 0, match, types, cons, type = typeof inp; if (type === 'object' && (inp && inp.constructor) && getFuncName(inp.constructor) === 'PHPJS_Resource') { return 'resource'; } if (type === 'function') { return 'function'; } if (type === 'object' && !inp) { // Should this be just null? return 'null'; } if (type === 'object') { if (!inp.constructor) { return 'object'; } cons = inp.constructor.toString(); match = cons.match(/(\w+)\(/); if (match) { cons = match[1].toLowerCase(); } types = ['boolean', 'number', 'string', 'array']; for (i = 0; i < types.length; i++) { if (cons === types[i]) { type = types[i]; break; } } } return type; }; type = __getType(mixed_expression); if (type === null) { retstr = 'NULL'; } else if (type === 'array' || type === 'object') { outerIndent = _makeIndent(idtLevel - 2); innerIndent = _makeIndent(idtLevel); for (i in mixed_expression) { value = this.var_export(mixed_expression[i], 1, idtLevel + 2); value = typeof value === 'string' ? value.replace(/</g, '<') . replace(/>/g, '>'): value; x[cnt++] = innerIndent + i + ' => ' + (__getType(mixed_expression[i]) === 'array' ? '\n' : '') + value; } iret = x.join(',\n'); retstr = outerIndent + 'array (\n' + iret + '\n' + outerIndent + ')'; } else if (type === 'function') { funcParts = mixed_expression.toString() . match(/function .*?\((.*?)\) \{([\s\S]*)\}/); // For lambda functions, var_export() outputs such as the following: // '\000lambda_1'. Since it will probably not be a common use to // expect this (unhelpful) form, we'll use another PHP-exportable // construct, create_function() (though dollar signs must be on the // variables in JavaScript); if using instead in JavaScript and you // are using the namespaced version, note that create_function() will // not be available as a global retstr = "create_function ('" + funcParts[1] + "', '" + funcParts[2].replace(new RegExp("'", 'g'), "\\'") + "')"; } else if (type === 'resource') { // Resources treated as null for var_export retstr = 'NULL'; } else { retstr = typeof mixed_expression !== 'string' ? mixed_expression : "'" + mixed_expression.replace(/(["'])/g, '\\$1') . replace(/\0/g, '\\0') + "'"; } if (!bool_return) { this.echo(retstr); return null; } return retstr; } echo() in JavaScript http://phpjs.org/functions/echo/ function echo() { // discuss at: http://phpjs.org/functions/echo/ // original by: Philip Peterson // improved by: echo is bad // improved by: Nate // improved by: Brett Zamir (http://brett-zamir.me) // improved by: Brett Zamir (http://brett-zamir.me) // improved by: Brett Zamir (http://brett-zamir.me) // revised by: Der Simon (http://innerdom.sourceforge.net/) // bugfixed by: Eugene Bulkin (http://doubleaw.com/) // bugfixed by: Brett Zamir (http://brett-zamir.me) // bugfixed by: Brett Zamir (http://brett-zamir.me) // bugfixed by: EdorFaus // input by: JB // note: If browsers start to support DOM Level 3 Load and Save (parsing/serializing), // note: we wouldn't need any such long code (even most of the code below). See // note: link below for a cross-browser implementation in JavaScript. HTML5 might // note: possibly support DOMParser, but that is not presently a standard. // note: Although innerHTML is widely used and may become standard as of HTML5, it is also not ideal for // note: use with a temporary holder before appending to the DOM (as is our last resort below), // note: since it may not work in an XML context // note: Using innerHTML to directly add to the BODY is very dangerous because it will // note: break all pre-existing references to HTMLElements. // example 1: echo('<div><p>abc</p><p>abc</p></div>'); // returns 1: undefined var isNode = typeof module !== 'undefined' && module.exports && typeof global !== "undefined" && {}.toString.call( global) == '[object global]'; if (isNode) { var args = Array.prototype.slice.call(arguments); return console.log(args.join(' ')); } var arg = ''; var argc = arguments.length; var argv = arguments; var i = 0; var holder, win = this.window; var d = win.document; var ns_xhtml = 'http://www.w3.org/1999/xhtml'; // If we're in a XUL context var ns_xul = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; var stringToDOM = function(str, parent, ns, container) { var extraNSs = ''; if (ns === ns_xul) { extraNSs = ' xmlns:html="' + ns_xhtml + '"'; } var stringContainer = '<' + container + ' xmlns="' + ns + '"' + extraNSs + '>' + str + '</' + container + '>'; var dils = win.DOMImplementationLS; var dp = win.DOMParser; var ax = win.ActiveXObject; if (dils && dils.createLSInput && dils.createLSParser) { // Follows the DOM 3 Load and Save standard, but not // implemented in browsers at present; HTML5 is to standardize on innerHTML, but not for XML (though // possibly will also standardize with DOMParser); in the meantime, to ensure fullest browser support, could // attach http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.js (see http://svn2.assembla.com/svn/brettz9/DOMToString/DOM3.xhtml for a simple test file) var lsInput = dils.createLSInput(); // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default lsInput.stringData = stringContainer; // synchronous, no schema type var lsParser = dils.createLSParser(1, null); return lsParser.parse(lsInput) .firstChild; } else if (dp) { // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default try { var fc = new dp() .parseFromString(stringContainer, 'text/xml'); if (fc && fc.documentElement && fc.documentElement.localName !== 'parsererror' && fc.documentElement.namespaceURI !== 'http://www.mozilla.org/newlayout/xml/parsererror.xml') { return fc.documentElement.firstChild; } // If there's a parsing error, we just continue on } catch (e) { // If there's a parsing error, we just continue on } } else if (ax) { // We don't bother with a holder in Explorer as it doesn't support namespaces var axo = new ax('MSXML2.DOMDocument'); axo.loadXML(str); return axo.documentElement; } /*else if (win.XMLHttpRequest) { // Supposed to work in older Safari var req = new win.XMLHttpRequest; req.open('GET', 'data:application/xml;charset=utf-8,'+encodeURIComponent(str), false); if (req.overrideMimeType) { req.overrideMimeType('application/xml'); } req.send(null); return req.responseXML; }*/ // Document fragment did not work with innerHTML, so we create a temporary element holder // If we're in XHTML, we'll try to allow the XHTML namespace to be available by default //if (d.createElementNS && (d.contentType && d.contentType !== 'text/html')) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways) if (d.createElementNS && // Browser supports the method (d.documentElement.namespaceURI || // We can use if the document is using a namespace d.documentElement.nodeName.toLowerCase() !== 'html' || // We know it's not HTML4 or less, if the tag is not HTML (even if the root namespace is null) (d.contentType && d.contentType !== 'text/html') // We know it's not regular HTML4 or less if this is Mozilla (only browser supporting the attribute) and the content type is something other than text/html; other HTML5 roots (like svg) still have a namespace )) { // Don't create namespaced elements if we're being served as HTML (currently only Mozilla supports this detection in true XHTML-supporting browsers, but Safari and Opera should work with the above DOMParser anyways, and IE doesn't support createElementNS anyways); last test is for the sake of being in a pure XML document holder = d.createElementNS(ns, container); } else { // Document fragment did not work with innerHTML holder = d.createElement(container); } holder.innerHTML = str; while (holder.firstChild) { parent.appendChild(holder.firstChild); } return false; // throw 'Your browser does not support DOM parsing as required by echo()'; }; var ieFix = function(node) { if (node.nodeType === 1) { var newNode = d.createElement(node.nodeName); var i, len; if (node.attributes && node.attributes.length > 0) { for (i = 0, len = node.attributes.length; i < len; i++) { newNode.setAttribute(node.attributes[i].nodeName, node.getAttribute(node.attributes[i].nodeName)); } } if (node.childNodes && node.childNodes.length > 0) { for (i = 0, len = node.childNodes.length; i < len; i++) { newNode.appendChild(ieFix(node.childNodes[i])); } } return newNode; } else { return d.createTextNode(node.nodeValue); } }; var replacer = function(s, m1, m2) { // We assume for now that embedded variables do not have dollar sign; to add a dollar sign, you currently must use {$$var} (We might change this, however.) // Doesn't cover all cases yet: see http://php.net/manual/en/language.types.string.php#language.types.string.syntax.double if (m1 !== '\\') { return m1 + eval(m2); } else { return s; } }; this.php_js = this.php_js || {}; var phpjs = this.php_js; var ini = phpjs.ini; var obs = phpjs.obs; for (i = 0; i < argc; i++) { arg = argv[i]; if (ini && ini['phpjs.echo_embedded_vars']) { arg = arg.replace(/(.?)\{?\$(\w*?\}|\w*)/g, replacer); } if (!phpjs.flushing && obs && obs.length) { // If flushing we output, but otherwise presence of a buffer means caching output obs[obs.length - 1].buffer += arg; continue; } if (d.appendChild) { if (d.body) { if (win.navigator.appName === 'Microsoft Internet Explorer') { // We unfortunately cannot use feature detection, since this is an IE bug with cloneNode nodes being appended d.body.appendChild(stringToDOM(ieFix(arg))); } else { var unappendedLeft = stringToDOM(arg, d.body, ns_xhtml, 'div') .cloneNode(true); // We will not actually append the div tag (just using for providing XHTML namespace by default) if (unappendedLeft) { d.body.appendChild(unappendedLeft); } } } else { // We will not actually append the description tag (just using for providing XUL namespace by default) d.documentElement.appendChild(stringToDOM(arg, d.documentElement, ns_xul, 'description')); } } else if (d.write) { d.write(arg); } else { console.log(arg); } } }
Firebug. Then, in your javascript: var blah = {something: 'hi', another: 'noway'}; console.debug("Here is blah: %o", blah); Now you can look at the console, click on the statement and see what is inside blah
A nice simple solution for parsing a JSON Response to HTML. var json_response = jQuery.parseJSON(data); html_response += 'JSON Response:<br />'; jQuery.each(json_response, function(k, v) { html_response += outputJSONReponse(k, v); }); function outputJSONReponse(k, v) { var html_response = k + ': '; if(jQuery.isArray(v) || jQuery.isPlainObject(v)) { jQuery.each(v, function(j, w) { html_response += outputJSONReponse(j, w); }); } else { html_response += v + '<br />'; } return html_response; }
You could also try this function. I can't remember the original author, but all credits go to them. Works like a charm - 100% the same as var_dump in PHP. Check it out. function dump(arr,level) { var dumped_text = ""; if(!level) level = 0; //The padding given at the beginning of the line. var level_padding = ""; for(var j=0;j<level+1;j++) level_padding += " "; if(typeof(arr) == 'object') { //Array/Hashes/Objects for(var item in arr) { var value = arr[item]; if(typeof(value) == 'object') { //If it is an array, dumped_text += level_padding + "'" + item + "' ...\n"; dumped_text += dump(value,level+1); } else { dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n"; } } } else { //Stings/Chars/Numbers etc. dumped_text = "===>"+arr+"<===("+typeof(arr)+")"; } return dumped_text; } // Example: var employees = [ { id: '1', sex: 'm', city: 'Paris' }, { id: '2', sex: 'f', city: 'London' }, { id: '3', sex: 'f', city: 'New York' }, { id: '4', sex: 'm', city: 'Moscow' }, { id: '5', sex: 'm', city: 'Berlin' } ] // Open dev console (F12) to see results: console.log(dump(employees));
I put this forward to help anyone needing something readily practical for giving you a nice, prettified (indented) picture of a JS Node. None of the other solutions worked for me for a Node ("cyclical error" or whatever...). This walks you through the tree under the DOM Node (without using recursion) and gives you the depth, tagName (if applicable) and textContent (if applicable). Any other details from the nodes you encounter as you walk the tree under the head node can be added as per your interest... function printRNode( node ){ // make sort of human-readable picture of the node... a bit like PHP print_r if( node === undefined || node === null ){ throwError( 'node was ' + typeof node ); } let s = ''; // NB walkDOM could be made into a utility function which you could // call with one or more callback functions as parameters... function walkDOM( headNode ){ const stack = [ headNode ]; const depthCountDowns = [ 1 ]; while (stack.length > 0) { const node = stack.pop(); const depth = depthCountDowns.length - 1; // TODO non-text, non-BR nodes could show more details (attributes, properties, etc.) const stringRep = node.nodeType === 3? 'TEXT: |' + node.nodeValue + '|' : 'tag: ' + node.tagName; s += ' '.repeat( depth ) + stringRep + '\n'; const lastIndex = depthCountDowns.length - 1; depthCountDowns[ lastIndex ] = depthCountDowns[ lastIndex ] - 1; if( node.childNodes.length ){ depthCountDowns.push( node.childNodes.length ); stack.push( ... Array.from( node.childNodes ).reverse() ); } while( depthCountDowns[ depthCountDowns.length - 1 ] === 0 ){ depthCountDowns.splice( -1 ); } } } walkDOM( node ); return s; }