This is an example of how we can log in chrome dev tools with colors:
console.log('%c test1 ', 'background: black; color: green')
I was wondering whether we can log with table and colors and the same time ?
There's no styling capabilities on the console.table function, as per the Console API.
However, I was able to come up with a really hacky solution for styling a console.log statement as if it were a table. This is probably not going to be good enough, but it was pretty fun to make.
There were many limitations, like not being able to set the width and height properties. My workaround was to pad the text with spaces to match the longest property name/value.
(function() {
function getProperties(obj) {
var props = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
props.push(prop);
}
}
return props;
}
function getLongestTextLength(objArray) {
var longest = 0;
for (var obj of objArray) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
var length = Math.max(prop.length, obj[prop].length);
if (length > longest) longest = length;
}
}
}
return longest;
}
console.fancyTable = function(objArray) {
var objProto = objArray[0];
var args = [];
var header = '';
var baseStyles = 'padding: 2px; line-height: 18px;';
var baseBorders = 'border-top: 1px solid black; border-bottom: 1px solid black; border-left: 1px solid black; '
var headerStyles = baseStyles + baseBorders + 'font-weight: bold; background: lightcoral;';
var lastHeaderStyles = baseStyles + 'font-weight: bold; border: 1px solid black; background: lightcoral;';
var rowStyles = baseStyles + baseBorders + 'background: lightblue;'
var lastRowStyles = baseStyles + 'border: 1px solid black; background: lightblue;'
var props = getProperties(objProto);
var longestTextLength = getLongestTextLength(objArray);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
while (prop.length < longestTextLength) {
prop += ' ';
}
header += '%c' + prop + ' ';
if (i === props.length - 1) {
args.push(lastHeaderStyles);
} else {
args.push(headerStyles);
}
}
for (var i = 0; i < objArray.length; i++) {
var obj = objArray[i];
header += '\n';
for (var j = 0; j < props.length; j++) {
var val = obj[props[j]];
while (val.length < longestTextLength) {
val += ' ';
}
header += '%c' + val + ' ';
if (j === props.length - 1) {
args.push(lastRowStyles);
} else {
args.push(rowStyles);
}
}
}
args.unshift(header);
console.log.apply(this, args);
}
})();
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// create some test objects
var john = new Person("John", "Smith");
var jane = new Person("Jane", "Doe");
var emily = new Person("Emily", "Jones");
var peopleToLog = [john, jane, emily];
console.fancyTable(peopleToLog);
I'll make some improvements if I can, and perhaps publish it for the giggles.
Related
SVP i'm newsbe in Appsscript with API PEOPLE
This script don't work : I want to rename "CKD EPI" by "CKD EPI / CROKOFF / CREAT"
Can you tell me what's wrong ?
function renameCustomField() {
var debug = true;
var group = People.PeopleApi.getContactGroups().get("Mygroup").execute();
var oldFieldName = "CKD EPI";
var newFieldName = "CKD EPI / CROKOFF / CREAT";
for (var i = 0; i < group.members.length; i++) {
var contact = group.members[i];
var fields = contact.userDefined;
for (var j = 0; j < fields.length; j++) {
if (fields[j].key === oldFieldName) {
fields[j].key = newFieldName;
if (debug) {
console.log("Renaming field for contact: " + contact.names[0].displayName);
}
}
}
var updateContact = {
resourceName: contact.resourceName,
updatePersonFields: "userDefined",
userDefined: fields
}
People.PeopleApi.updateContact(updateContact).execute();
}
}
I have a 200 pages long google document containing some complex data like tables, paragraph and hyperlinks.
I am trying to create a custom dialog box or button with two option "next" and "Previous".
So Whenever I click on "next" it should set my cursor on next hyperlink in google doc for example if I am on page 30 and there is hyper link on page 31 too I want to jump over next hyperlink or position.
So far I am able to get all hyperlink through out the document but I don't how to set my cursor over those word or jump over to next or previous hyperlink by clicking on button in dialog box.
code.gs
function highlightLink3() {
const doc = DocumentApp.getActiveDocument()
const body = doc.getBody()
const text = body.getText();
const words = [...new Set(text.split(/[\n ]/g).map(e => e.trim()).filter(String))];
words.forEach(t => {
let word = body.findText(t);
while (word) {
const e = word.getElement();
const start = word.getStartOffset();
if (e.getLinkUrl(start)) {
doc.setCursor(e.getLinkUrl(start))
}
word = body.findText(t, word);
}
});
}
So far I am able to get all hyperlink through out the document but I don't how to set my cursor over those word or jump over to next or previous hyperlink by clicking on button in dialog box.
Here is something I created a while back. If constructs a tree view of my document based on the Headers. I create a side bar and can navigate down the tree. When I click on an item it directs me to that header.
My Document was 93 pages so I developed this to navigate back and forth. Otherwise you would have to return to the table of contents every time to go to another section.
Code.gs
function onOpen() {
var thisMenu = DocumentApp.getUi().createMenu('Test');
thisMenu.addItem('Treeview in Code.gs', 'treeViewOnOpen').addToUi();
}
function treeViewOnOpen() {
try {
var html = HtmlService.createTemplateFromFile("HTML_TreeView");
html = html.evaluate();
DocumentApp.getUi().showSidebar(html);
}
catch(err) {
Logger.log("Error in treeViewOnOpen: "+err);
}
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function TreeNode(level,name) {
this.parent = null;
this.level = level;
this.name = name;
this.children = [];
this.link = null
}
function ToCListItem(level,text) {
this.level = level;
this.text = text;
var time = new Date();
this.link = "h"+level.toString()+"_"+time.getTime().toString();
}
function getTreeView() {
try {
var doc = DocumentApp.getActiveDocument();
var bod = doc.getBody();
var pars = bod.getParagraphs();
var root = new TreeNode(0,"Root");
var parent = root;
var level = 1;
var last = null;
for( var i=0; i<pars.length; i++ ) {
var par = pars[i];
var head = getHeading(par);
if( par.getText().trim() === "" ) continue;
if( head > 0 ) {
var node = new TreeNode(head,par.getText());
var link = par.getLinkUrl();
if( !link ) {
var time = new Date();
node.link = "h"+head.toString()+"_"+time.getTime().toString();
par.setLinkUrl(node.link);
}
else {
node.link = link;
}
if( head > level ) {
parent = last;
level = head;
}
else if( head < level ) {
while( head <= level ) {
parent = parent.parent;
level = parent.level;
}
if( level === 0 ) level = 1;
}
node.parent = parent;
parent.children.push(node);
last = node;
}
}
var s = "";
for( var i=0; i<root.children.length; i++ ) {
s = s + stringify(root.children[i]);
}
return s;
}
catch(err) {
Logger.log("Error in getTreeView: "+err);
}
}
function stringify(tree) {
try {
var s = ""
if( tree.children.length > 0 ) {
s = "<li><input class='node' type='button' value='+'>"+tree.name+"\n";
s = s + "<ul>\n";
for( var i=0; i<tree.children.length; i++ ) {
s = s + stringify(tree.children[i]);
}
s = s + "</ul>\n</li>\n";
}
else {
var s = "<li class='link' id='"+tree.link+"'><input class='link' type='button' value=' '>"+tree.name+"</li>\n";
}
return s;
}
catch(err) {
Logger.log(err);
}
}
function getHeading(par) {
try {
var head = par.getHeading();
if( head == DocumentApp.ParagraphHeading.HEADING1 ) {
return 1;
}
else if( head == DocumentApp.ParagraphHeading.HEADING2 ) {
return 2;
}
else if( head == DocumentApp.ParagraphHeading.HEADING3 ) {
return 3;
}
else if( head == DocumentApp.ParagraphHeading.HEADING4 ) {
return 4;
}
else if( head == DocumentApp.ParagraphHeading.HEADING5 ) {
return 5;
}
else if( head == DocumentApp.ParagraphHeading.HEADING6 ) {
return 6;
}
return -1;
}
catch(err) {
Logger.log("Error in getHeading: "+err);
}
}
function linkClick(link) {
try {
var doc = DocumentApp.getActiveDocument();
var bod = doc.getBody();
var pars = bod.getParagraphs();
for( var i=0; i<pars.length; i++ ) {
var par = pars[i];
var head = getHeading(par);
if( head > 0 ) {
if( par.getLinkUrl() == link ) {
var pos = doc.newPosition(par.getChild(0),0);
doc.setCursor(pos);
return;
}
}
}
}
catch(err) {
Logger.log("Error in setCursor: "+err);
}
}
HTML_TreeView
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('CSS_TreeView'); ?>
</head>
<body>
<ul class="tree">
<?!= getTreeView(); ?>
</ul>
<?!= include('JS_TreeView'); ?>
</body>
</html>
CSS_TreeView
<style>
body {
font-family: Arial;
font-size: small;
}
ul.tree li {
list-style-type: none;
position: relative;
}
ul.tree li ul {
display: none;
}
ul.tree li.open > ul {
display: block;
}
ul.tree li input {
color: black;
text-decoration: none;
margin-right: 10px;
width: 25px;
}
ul.tree li input:before {
height: 1em;
padding: 0 .1em;
font-size: .8em;
display: block;
position: absolute;
left: -1.3em;
top: .2em;
}
</style>
JS_TreeView
<script>
function expandClick(e) {
try {
var parent = e.target.parentElement;
var classList = parent.classList;
if(classList.contains("open")) {
classList.remove('open');
var opensubs = parent.querySelectorAll(':scope .open');
for(var i = 0; i < opensubs.length; i++){
opensubs[i].classList.remove('open');
}
e.target.value = "+";
}
else {
classList.add('open');
e.target.value = "-";
}
}
catch(err) {
alert(err);
}
}
function linkClick(e) {
try {
var id = e.target.id;
if( id === "" ) id = e.target.parentNode.id;
google.script.run.linkClick(id);
}
catch(err) {
alert(err);
}
}
(function () {
try {
var tree = document.querySelectorAll('ul.tree input.node');
for(var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', expandClick);
}
var tree = document.querySelectorAll('ul.tree li.link');
for(var i = 0; i < tree.length; i++) {
tree[i].addEventListener('click', linkClick);
}
}
catch(err) {
alert(err);
}
})();
</script>
in chrome, space-around doesn't center items if its single column.
but in Firefox, it works.
how to make it behave like firefox?
also, keep in mind that text is aligned to the right
.flex-cont {
display: flex;
justify-content: flex-start;
flex-flow: column wrap;
align-content: space-around;
align-content: space-evenly;
align-items: flex-end;
}
.flex-item {
/* display: inline-block; */
flex: 0 1 auto;
width: fit-content;
}
http://jsfiddle.net/f6k7xoe0/1/
edit: also I can do this but this messes up text aligning to right :
.flex-cont{
align-items: center;
}
edit: honestly I wouldn't care so much if it was as a hobby, but I added cefsharp(chrome) in my application. will be in production. there is no other way. i have to get that render in the cefsharp.
edit:
this is not a duplicate.
I dont ask WHY it doesn't work. I want a solution
my output is different. output in the other questions is not even multi-column.
edit2: I solved it via js getboundrect compare get max-width of each item them apply margin if wrap happens. but its messy don't wanna use it. but I have to.
I cleaned up the code to make it apply the all flex-container, flex item if you give appropriate CssSelector in the doit() function. it will work. but this is for columns.
http://jsfiddle.net/yeaqrh48/1203/
var debug = true;
class ertTimer {
constructor(funcName ,intervalms=3500, maxRunDuration=20000 , StopIfReturnsTrue=true ) {
this.intervalObj = setInterval(function(){
console.log("interval - funcName:" + funcName.name);
try{
var res = funcName();
if(StopIfReturnsTrue)
if(res == true)
clearInterval(intervalObj);
} catch(exx){console.warn(exx.message, exx.stack);}
}, intervalms);
// after 15 sec delete interval
setTimeout( function(){ clearInterval( intervalObj ); },maxRunDuration);
this.intervalms = intervalms;
this.maxRunDuration = maxRunDuration;
}
get getter_intervalms() { return this.intervalms; }
calcRepeatTimes() {
return this.maxRunDuration / this.intervalms;
}
}
var center_ONsingleCol_nonFF = function(contNode, itemSelector) {
var items = contNode.querySelectorAll(itemSelector);
//arr.count shoud be 1 element // items[0].style.alignItems = "center";
var parItem = items[0].parentNode;
var parItemR = parItem.getBoundingClientRect();
var parWidth = parItemR.width;
var maxItemWidth = 0;
for (var k = 0; k < items.length; k++) {
var currItem = items[k].getBoundingClientRect();
if (currItem.width > maxItemWidth)
maxItemWidth = currItem.width;
//console.log(parWidth, itemWidth);
}
var alignItemsVal = getComputedStyle_propValue(parItem , "align-items");
var flexDirVal = getComputedStyle_propValue(parItem , "flex-direction");
var iswrapped = isWrapped(contNode ,itemSelector );
for (var k = 0; k < items.length; k++) {
if(iswrapped && flexDirVal == "column" ){
if(alignItemsVal == "flex-end"){
items[k].style.marginRight = "" + ((parWidth - maxItemWidth) * 0.5) + "px";
items[k].style.marginLeft = "";
}
else if(alignItemsVal == "flex-start"){
items[k].style.marginRight = "";
items[k].style.marginLeft = "" + ((parWidth - maxItemWidth) * 0.5) + "px";
}else
{
items[k].style.marginRight = "";
items[k].style.marginLeft = "";
}
}
else{
items[k].style.marginRight = "";
items[k].style.marginLeft = "";
}
}
};
var getComputedStyle_propValue = function(element , CssPropName){
//var element = document.querySelector( selector );
var compStyles = window.getComputedStyle(element);
var comStyle_xxx = compStyles.getPropertyValue(CssPropName);
return comStyle_xxx;
};
var colorizeItem = function(items) {
for (var k = 0; k < items.length; k++) {
items[k].style += ";background:Red;";
}
};
var detectWrap = function(contNode, item_selector) {
var wrappedItems = [];
var prevItem = {};
var currItem = {};
var items = contNode.querySelectorAll(item_selector);
//console.log("wrapped item arrrat::",items);
for (var i = 0; i < items.length; i++) {
currItem = items[i].getBoundingClientRect();
if (prevItem && prevItem.top > currItem.top) {
wrappedItems.push(items[i]);
}
prevItem = currItem;
}
return wrappedItems;
};
var isFirefox = function() {
var _isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
return _isFF;
};
var isWrapped = function(contNode, item_selector){
var wrappedItems = detectWrap(contNode, item_selector);
//colorizeItem(wrappedItems);
if (wrappedItems == null || wrappedItems.length == 0)
return true;
else
return false;
};
var isWired_listenContResize = false;
var doit = function() {
if (isFirefox()) {
console.log("ff already works Right. ");
return;
} else {
console.log("not ff. script will run. ");
}
/* flexItem_selector must be relative to flexCont*/
var flexContainer_selector = ".flex-cont.cont-resize"; /*specific flex-cont */
var flexItem_selector = ".flex-item";
var contList = document.querySelectorAll(flexContainer_selector);
for (var i = 0; i < contList.length; i++) {
//no such event //there is external lib..
// call doit after you change size in the code;
if (!isWired_listenContResize) {
contList[i].onresize = function() { doit(); };
}
center_ONsingleCol_nonFF(contList[i], flexItem_selector);
}
isWired_listenContResize = true;
};
window.onresize = function(event) { doit(); };
window.onload = function(event) {
doit();
const et1_ = new ertTimer(doit , 500, 320000,true );
};
I have three classes of div containers. One is an outer container
with width:1200px, with inner div rows that each have inner div's with a
desired size of 280 px. My problem is that when I create the elements
using the DOM that the CountriesInnerDiv elements are varying in size depending what is inside them.
Here's what I have so far:
.CountriesOuterDiv1{
width: 1200px;
}
.CountriesInnerDiv{
min-width:280px;
display:inline;
border:1px solid black;
}
.RowDiv {
width:100%;
}
Here is the relevant Javascript:
function AddTable() {
var TableDiv = document.getElementById("CountriesOuterDiv");
var i,j;
var NumCountries = 5;
var NumSectors = 5;
for (i = 0; i < NumCountries; i++) {
var RowDiv = document.createElement("div")
RowDiv.className = "RowDiv";
RowDiv.id = "Row"+i;
for (j = 0; j < NumSectors + 1; j++) {
var CountryData = document.createElement("div");
var SectorString = String(i+1)+"_"+String(j);
CountryData.className = "CountriesInnerDiv";
CountryData.id = "CountryData"+SectorString;
if (!((i ==0 && j == 0) || (i==0 && j ==1))) {
CountryData.style.display = "none";
}
if (j == 0 || (i == 0 && j == 1)) {
var CountryDataSelect = document.createElement("select");
var AddButton = document.createElement("button");
AddButton.id = "AddButton" + SectorString;
AddButton.innerHTML = "+";
if (j != 0) {
AddButton.setAttribute('onclick','AddOrDeleteDiv("add", "' + SectorString + '");');
}
else {
if (i != NumCountries - 1) {
AddButton.setAttribute('onclick','AddCountry("' + SectorString + '")');
}
if (i != 0) {
var DeleteCountry = document.createElement("button");
DeleteCountry.id = "DeleteButton" + SectorString;
DeleteCountry.innerHTML = "-";
DeleteCountry.setAttribute('onclick', 'DeleteCountry("' + SectorString + '")');
}
}
CountryData.appendChild(CountryDataSelect);
if (i != NumCountries - 1) {
CountryData.appendChild(AddButton);
}
if (i!=0) {
CountryData.appendChild(DeleteCountry);
}
RowDiv.appendChild(CountryData);
}
else if (j == NumSectors) {
var CountryDataSelect = document.createElement("select");
var DeleteButton = document.createElement("button");
DeleteButton.id = "DeleteButton" + SectorString;
DeleteButton.innerHTML = "-";
DeleteButton.setAttribute('onclick','AddOrDeleteDiv("delete", "' + SectorString + '");');
CountryData.appendChild(CountryDataSelect);
CountryData.appendChild(DeleteButton);
}
else {
var CountryDataSelect = document.createElement("select");
var AddButton = document.createElement("button");
var ADString = "add";
AddButton.setAttribute('onclick','AddOrDeleteDiv("add", "' + SectorString + '");');
AddButton.id = "AddButton" + SectorString;
AddButton.innerHTML = "+";
var DeleteButton = document.createElement("button");
DeleteButton.id = "DeleteButton" + SectorString;
DeleteButton.innerHTML = "-";
DeleteButton.setAttribute('onclick','AddOrDeleteDiv("delete", "' + SectorString + '");');
CountryData.appendChild(CountryDataSelect);
CountryData.appendChild(AddButton);
CountryData.appendChild(DeleteButton);
}
RowDiv.appendChild(CountryData);
TableDiv.appendChild(RowDiv);
}//End of inner for
}//End of outer for
}//end of function
Its cose you make this div display:inline; do like this .CountriesInnerDiv{
min-width:280px;
display:inline-block;
border:1px solid black;
}
Basically, what you're experiencing, is most likely that the inner element is just too big in it's minimal size to fit the parent one when the window is small... well.. we can't have an inside bigger than an outside... unless you use
overflow: scroll;
This will make the parent element scrollable so you can visualize the child by scrolling...
or...
.parent{min-width: 280px}
wich will make the parent has the same minimum width...
There is a select with disabled options. My question is that is there a CSS solution to sort the options? I want to arrange all the disabled options to the bottom, and all the enabled options to the top. I've attached a screenshot. The enabled options' color is black, and the disabled options' color is light gray.
#playground-content select option {
color: black;
}
#playground-content select option:disabled {
color: rgba(221, 221, 221, 1);
}
try this:-
$("#addselect option").each(function (i, val) {
if ($(this)[i].disabled) {
moveDown("selectId");
}
else {
moveUp("selectId");
}
}
function moveUp(selectId) {
var selectList = document.getElementById(selectId);
var selectOptions = selectList.getElementsByTagName('option');
for (var i = 1; i < selectOptions.length; i++) {
var opt = selectOptions[i];
if (!opt.disabled) {
selectList.removeChild(opt);
selectList.insertBefore(opt, selectOptions[i - 1]);
}
}
}
function moveDown(selectId) {
var selectList = document.getElementById(selectId);
var selectOptions = selectList.getElementsByTagName('option');
for (var i = selectOptions.length - 2; i >= 0; i--) {
var opt = selectOptions[i];
if (opt.disabled) {
var nextOpt = selectOptions[i + 1];
opt = selectList.removeChild(opt);
nextOpt = selectList.replaceChild(opt, nextOpt);
selectList.insertBefore(nextOpt, opt);
}
}
}