I am developing an extension which is about fetching the list of topics from the server and find if those topics match with the currently opened gmail messages or not, if found then highlight that topic otherwise don't. But if already 6 topics are matched then it should not check or highlight other topics.
I have used the treewalker for crawling the gmail contents so the matched content will get highlighted as follow
function searchPage(topics) {
highlightAllWords(topics);
}
var highlightAllWords = function(topics) {
Object.keys(topics.topics).forEach(function(topic) {
highlightTopic(topic);
})
}
function highlightTopic(topic) {
let found = 0;
if (topic == null || topic.length === 0) return;
var topicRegex = new RegExp(topic, 'gi');
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
{
acceptNode: function(node) {
var result = NodeFilter.FILTER_SKIP;
if (topicRegex.test(node.nodeValue)) {
found += 1
console.log('found', found);
if (found <= 6) {
console.log('foound less than 6', found)
result = NodeFilter.FILTER_ACCEPT
return result;
}
};
}
}, false
)
var skipTagName = {
"NOSCRIPT": true,
"SCRIPT": true,
"STYLE": true
}
var nodeList = [];
while (treeWalker.nextNode()) {
if (!skipTagName[treeWalker.currentNode.parentNode.tagName]) {
nodeList.push(treeWalker.currentNode);
}
}
nodeList.forEach(function (n) {
var rangeList = [];
// find sub-string ranges
var startingIndex = 0;
do {
// console.log(word, startingIndex, n.parentNode, n.textContent);
startingIndex = n.textContent.indexOf(topic, startingIndex + 1);
if (startingIndex !== -1) {
var topicRange = document.createRange();
topicRange.setStart(n, startingIndex);
topicRange.setEnd(n, startingIndex + topic.length);
rangeList.push(topicRange);
}
} while (startingIndex !== -1);
// highlight all ranges
rangeList.forEach(function (r) {
highlightRange(r);
});
});
}
// highlight the keyword by surround it by `i`
var highlightRange = function (range) {
const bgColorCode = '#000000';
var anchor = document.createElement("A");
var selectorName = anchor.className = "highlighted_text";
anchor.classList.add("highlighted_text");
// range.surroundContents(iNode) will throw exception if word across multi tag
if (!ruleExistenceDict[bgColorCode]) {
sheet.insertRule([".", selectorName, " { background: #", bgColorCode, " !important; }"].join(""), 0);
ruleExistenceDict[bgColorCode] = true;
console.log(sheet);
}
anchor.appendChild(range.extractContents());
anchor.href = `https://google.com/?search=${
range.extractContents()
}`;
range.insertNode(anchor);
};
It highlights the matched content in gmail messages but does highlights more than 6 contents. I have taken the screenshot and it is something like this
update after counter increased and checked in treewalker.nextnode()
There are two syntax errors in your code. You are missing semicolon at end of this statement
found += 1;
Secondly, there is also one extra ";" at the end of function(node).
And You can add the counter check in the following code snippet as
var count=1;
while (treeWalker.nextNode() && count<=6) {
if (!skipTagName[treeWalker.currentNode.parentNode.tagName]) {
nodeList.push(treeWalker.currentNode);
count=count+1;
}
}
So, the final script for the function highlightTopic(topic) will look like
function highlightTopic(topic) {
let found = 0;
if (topic == null || topic.length === 0) return;
var topicRegex = new RegExp(topic, 'gi');
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
{
acceptNode: function(node) {
var result = NodeFilter.FILTER_SKIP;
if (topicRegex.test(node.nodeValue)) {
found += 1;
console.log('found', found);
if (found <= 6) {
console.log('foound less than 6', found)
result = NodeFilter.FILTER_ACCEPT
return result;
}
}
}
}, false
)
var skipTagName = {
"NOSCRIPT": true,
"SCRIPT": true,
"STYLE": true
}
var nodeList = [];
var count=1;
while (treeWalker.nextNode() && count<=6) {
if (!skipTagName[treeWalker.currentNode.parentNode.tagName]) {
nodeList.push(treeWalker.currentNode);
count=count+1;
console.log('count:'+count);
}
}
nodeList.forEach(function (n) {
var rangeList = [];
// find sub-string ranges
var startingIndex = 0;
do {
// console.log(word, startingIndex, n.parentNode, n.textContent);
startingIndex = n.textContent.indexOf(topic, startingIndex + 1);
if (startingIndex !== -1) {
var topicRange = document.createRange();
topicRange.setStart(n, startingIndex);
topicRange.setEnd(n, startingIndex + topic.length);
rangeList.push(topicRange);
}
} while (startingIndex !== -1);
// highlight all ranges
rangeList.forEach(function (r) {
highlightRange(r);
});
});
}
Please update me if it worked.
Thanks
Edit:
And also update the function highlightAllWords as follow:
var highlightAllWords = function(topics) {
var count=1;
Object.keys(topics.topics).forEach(function(topic) {
if(count<=6){
highlightTopic(topic);
console.log('counter:'+count);
if (topic != null && topic.length != 0)
count=count+1;
}
})
}
Related
Using a script to sync google sheets with google calendar. It was originally posted on GitHub by DavePar - the original can be seen here - GitHub project
Changed a couple of titles and it works fine.
However, added a column title Stand and getting the following error message. When tried to run the script now
TypeError: Cannot find function getStand in object CalendarEvent.
Have looked through several times and can't see where the problem lies. Can anyone have a look though and point me in the right direction, please?
Here is the slightly modified version:
// Script to synchronize a calendar to a spreadsheet and vice versa.
//
// See https://github.com/Davepar/gcalendarsync for instructions on setting this up.
//
// Set this value to match your calendar!!!
// Calendar ID can be found in the "Calendar Address" section of the Calendar Settings.
var calendarId = 'dtsmike#googlemail.com';
// Set the beginning and end dates that should be synced. beginDate can be set to Date() to use
// today. The numbers are year, month, date, where month is 0 for Jan through 11 for Dec.
var beginDate = new Date(1970, 0, 1); // Default to Jan 1, 1970
var endDate = new Date(2500, 0, 1); // Default to Jan 1, 2500
// Date format to use in the spreadsheet.
var dateFormat = 'd/m/yyyy H:mm';
var titleRowMap = {
'title': 'Title',
'stand': 'Stand',
'description': 'Kit Prep',
'location': 'Location',
'starttime': 'Install',
'endtime': 'Show Finish',
'guests': 'Guests',
'color': 'Color',
'id': 'Id'
};
var titleRowKeys = ['title', 'stand', 'description', 'location', 'starttime', 'endtime', 'guests', 'color', 'id'];
var requiredFields = ['id', 'title', 'starttime', 'endtime'];
// This controls whether email invites are sent to guests when the event is created in the
// calendar. Note that any changes to the event will cause email invites to be resent.
var SEND_EMAIL_INVITES = false;
// Setting this to true will silently skip rows that have a blank start and end time
// instead of popping up an error dialog.
var SKIP_BLANK_ROWS = true;
// Updating too many events in a short time period triggers an error. These values
// were tested for updating 40 events. Modify these values if you're still seeing errors.
var THROTTLE_THRESHOLD = 10;
var THROTTLE_SLEEP_TIME = 75;
// Adds the custom menu to the active spreadsheet.
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{
name: "Update from Calendar",
functionName: "syncFromCalendar"
}, {
name: "Update to Calendar",
functionName: "syncToCalendar"
}
];
spreadsheet.addMenu('Calendar Sync', menuEntries);
}
// Creates a mapping array between spreadsheet column and event field name
function createIdxMap(row) {
var idxMap = [];
for (var idx = 0; idx < row.length; idx++) {
var fieldFromHdr = row[idx];
for (var titleKey in titleRowMap) {
if (titleRowMap[titleKey] == fieldFromHdr) {
idxMap.push(titleKey);
break;
}
}
if (idxMap.length <= idx) {
// Header field not in map, so add null
idxMap.push(null);
}
}
return idxMap;
}
// Converts a spreadsheet row into an object containing event-related fields
function reformatEvent(row, idxMap, keysToAdd) {
var reformatted = row.reduce(function(event, value, idx) {
if (idxMap[idx] != null) {
event[idxMap[idx]] = value;
}
return event;
}, {});
for (var k in keysToAdd) {
reformatted[keysToAdd[k]] = '';
}
return reformatted;
}
// Converts a calendar event to a psuedo-sheet event.
function convertCalEvent(calEvent) {
convertedEvent = {
'id': calEvent.getId(),
'title': calEvent.getTitle(),
'stand': calEvent.getStand(),
'description': calEvent.getDescription(),
'location': calEvent.getLocation(),
'guests': calEvent.getGuestList().map(function(x) {return x.getEmail();}).join(','),
'color': calEvent.getColor()
};
if (calEvent.isAllDayEvent()) {
convertedEvent.starttime = calEvent.getAllDayStartDate();
var endtime = calEvent.getAllDayEndDate();
if (endtime - convertedEvent.starttime === 24 * 3600 * 1000) {
convertedEvent.endtime = '';
} else {
convertedEvent.endtime = endtime;
if (endtime.getHours() === 0 && endtime.getMinutes() == 0) {
convertedEvent.endtime.setSeconds(endtime.getSeconds() - 1);
}
}
} else {
convertedEvent.starttime = calEvent.getStartTime();
convertedEvent.endtime = calEvent.getEndTime();
}
return convertedEvent;
}
// Converts calendar event into spreadsheet data row
function calEventToSheet(calEvent, idxMap, dataRow) {
convertedEvent = convertCalEvent(calEvent);
for (var idx = 0; idx < idxMap.length; idx++) {
if (idxMap[idx] !== null) {
dataRow[idx] = convertedEvent[idxMap[idx]];
}
}
}
// Returns empty string or time in milliseconds for Date object
function getEndTime(ev) {
return ev.endtime === '' ? '' : ev.endtime.getTime();
}
// Tests whether calendar event matches spreadsheet event
function eventMatches(cev, sev) {
var convertedCalEvent = convertCalEvent(cev);
return convertedCalEvent.title == sev.title &&
convertedCalEvent.description == sev.description &&
convertedCalEvent.stand == sev.stand &&
convertedCalEvent.location == sev.location &&
convertedCalEvent.starttime.toString() == sev.starttime.toString() &&
getEndTime(convertedCalEvent) === getEndTime(sev) &&
convertedCalEvent.guests == sev.guests &&
convertedCalEvent.color == ('' + sev.color);
}
// Determine whether required fields are missing
function areRequiredFieldsMissing(idxMap) {
return requiredFields.some(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Returns list of fields that aren't in spreadsheet
function missingFields(idxMap) {
return titleRowKeys.filter(function(val) {
return idxMap.indexOf(val) < 0;
});
}
// Set up formats and hide ID column for empty spreadsheet
function setUpSheet(sheet, fieldKeys) {
sheet.getRange(1, fieldKeys.indexOf('starttime') + 1, 999).setNumberFormat(dateFormat);
sheet.getRange(1, fieldKeys.indexOf('endtime') + 1, 999).setNumberFormat(dateFormat);
sheet.hideColumns(fieldKeys.indexOf('id') + 1);
}
// Display error alert
function errorAlert(msg, evt, ridx) {
var ui = SpreadsheetApp.getUi();
if (evt) {
ui.alert('Skipping row: ' + msg + ' in event "' + evt.title + '", row ' + (ridx + 1));
} else {
ui.alert(msg);
}
}
// Updates a calendar event from a sheet event.
function updateEvent(calEvent, sheetEvent){
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (sheetEvent.endtime === '') {
calEvent.setAllDayDate(sheetEvent.starttime);
} else {
calEvent.setTime(sheetEvent.starttime, sheetEvent.endtime);
}
calEvent.setTitle(sheetEvent.title);
calEvent.setDescription(sheetEvent.description);
calEvent.setStand(sheetEvent.stand);
calEvent.setLocation(sheetEvent.location);
// Set event color
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
calEvent.setColor('' + sheetEvent.color);
}
var guestCal = calEvent.getGuestList().map(function (x) {
return {
email: x.getEmail(),
added: false
};
});
var sheetGuests = sheetEvent.guests || '';
var guests = sheetGuests.split(',').map(function (x) {
return x ? x.trim() : '';
});
// Check guests that are already invited.
for (var gIx = 0; gIx < guestCal.length; gIx++) {
var index = guests.indexOf(guestCal[gIx].email);
if (index >= 0) {
guestCal[gIx].added = true;
guests.splice(index, 1);
}
}
guests.forEach(function (x) {
if (x) calEvent.addGuest(x);
});
guestCal.forEach(function (x) {
if (!x.added) {
calEvent.removeGuest(x.email);
}
});
}
// Synchronize from calendar to spreadsheet.
function syncFromCalendar() {
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
var calEvents = calendar.getEvents(beginDate, endDate);
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var eventFound = new Array(data.length);
// Check if spreadsheet is empty and add a title row
var titleRow = [];
for (var idx = 0; idx < titleRowKeys.length; idx++) {
titleRow.push(titleRowMap[titleRowKeys[idx]]);
}
if (data.length < 1) {
data.push(titleRow);
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
if (data.length == 1 && data[0].length == 1 && data[0][0] === '') {
data[0] = titleRow;
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
setUpSheet(sheet, titleRowKeys);
}
// Map spreadsheet headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
// Array of IDs in the spreadsheet
var sheetEventIds = data.slice(1).map(function(row) {return row[idIdx];});
// Loop through calendar events
for (var cidx = 0; cidx < calEvents.length; cidx++) {
var calEvent = calEvents[cidx];
var calEventId = calEvent.getId();
var ridx = sheetEventIds.indexOf(calEventId) + 1;
if (ridx < 1) {
// Event not found, create it
ridx = data.length;
var newRow = [];
var rowSize = idxMap.length;
while (rowSize--) newRow.push('');
data.push(newRow);
} else {
eventFound[ridx] = true;
}
// Update event in spreadsheet data
calEventToSheet(calEvent, idxMap, data[ridx]);
}
// Remove any data rows not found in the calendar
var rowsDeleted = 0;
for (var idx = eventFound.length - 1; idx > 0; idx--) {
//event doesn't exists and has an event id
if (!eventFound[idx] && sheetEventIds[idx - 1]) {
data.splice(idx, 1);
rowsDeleted++;
}
}
// Save spreadsheet changes
range = sheet.getRange(1, 1, data.length, data[0].length);
range.setValues(data);
if (rowsDeleted > 0) {
sheet.deleteRows(data.length + 1, rowsDeleted);
}
}
// Synchronize from spreadsheet to calendar.
function syncToCalendar() {
// Get calendar and events
var calendar = CalendarApp.getCalendarById(calendarId);
if (!calendar) {
errorAlert('Cannot find calendar. Check instructions for set up.');
}
var calEvents = calendar.getEvents(beginDate, endDate);
var calEventIds = calEvents.map(function(val) {return val.getId();});
// Get spreadsheet and data
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
if (data.length < 2) {
errorAlert('Spreadsheet must have a title row and at least one data row');
return;
}
// Map headers to indices
var idxMap = createIdxMap(data[0]);
var idIdx = idxMap.indexOf('id');
var idRange = range.offset(0, idIdx, data.length, 1);
var idData = idRange.getValues()
// Verify header has all required fields
if (areRequiredFieldsMissing(idxMap)) {
var reqFieldNames = requiredFields.map(function(x) {return titleRowMap[x];}).join(', ');
errorAlert('Spreadsheet must have ' + reqFieldNames + ' columns');
return;
}
var keysToAdd = missingFields(idxMap);
// Loop through spreadsheet rows
var numChanges = 0;
var numUpdated = 0;
var changesMade = false;
for (var ridx = 1; ridx < data.length; ridx++) {
var sheetEvent = reformatEvent(data[ridx], idxMap, keysToAdd);
// If enabled, skip rows with blank/invalid start and end times
if (SKIP_BLANK_ROWS && !(sheetEvent.starttime instanceof Date) &&
!(sheetEvent.endtime instanceof Date)) {
continue;
}
// Do some error checking first
if (!sheetEvent.title) {
errorAlert('must have title', sheetEvent, ridx);
continue;
}
if (!(sheetEvent.starttime instanceof Date)) {
errorAlert('start time must be a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime !== '') {
if (!(sheetEvent.endtime instanceof Date)) {
errorAlert('end time must be empty or a date/time', sheetEvent, ridx);
continue;
}
if (sheetEvent.endtime < sheetEvent.starttime) {
errorAlert('end time must be after start time for event', sheetEvent, ridx);
continue;
}
}
// Ignore events outside of the begin/end range desired.
if (sheetEvent.starttime > endDate) {
continue;
}
if (sheetEvent.endtime === '') {
if (sheetEvent.starttime < beginDate) {
continue;
}
} else {
if (sheetEvent.endtime < beginDate) {
continue;
}
}
// Determine if spreadsheet event is already in calendar and matches
var addEvent = true;
if (sheetEvent.id) {
var eventIdx = calEventIds.indexOf(sheetEvent.id);
if (eventIdx >= 0) {
calEventIds[eventIdx] = null; // Prevents removing event below
addEvent = false;
var calEvent = calEvents[eventIdx];
if (!eventMatches(calEvent, sheetEvent)) {
// Update the event
updateEvent(calEvent, sheetEvent);
// Maybe throttle updates.
numChanges++;
if (numChanges > THROTTLE_THRESHOLD) {
Utilities.sleep(THROTTLE_SLEEP_TIME);
}
}
}
}
if (addEvent) {
var newEvent;
sheetEvent.sendInvites = SEND_EMAIL_INVITES;
if (sheetEvent.endtime === '') {
newEvent = calendar.createAllDayEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent);
} else {
newEvent = calendar.createEvent(sheetEvent.title, sheetEvent.starttime, sheetEvent.endtime, sheetEvent);
}
// Put event ID back into spreadsheet
idData[ridx][0] = newEvent.getId();
changesMade = true;
// Set event color
if (sheetEvent.color > 0 && sheetEvent.color < 12) {
newEvent.setColor('' + sheetEvent.color);
}
// Maybe throttle updates.
numChanges++;
if (numChanges > THROTTLE_THRESHOLD) {
Utilities.sleep(THROTTLE_SLEEP_TIME);
}
}
}
// Save spreadsheet changes
if (changesMade) {
idRange.setValues(idData);
}
// Remove any calendar events not found in the spreadsheet
var numToRemove = calEventIds.reduce(function(prevVal, curVal) {
if (curVal !== null) {
prevVal++;
}
return prevVal;
}, 0);
if (numToRemove > 0) {
var ui = SpreadsheetApp.getUi();
var response = ui.Button.YES;
if (numToRemove > numUpdated) {
response = ui.alert('Delete ' + numToRemove + ' calendar event(s) not found in spreadsheet?',
ui.ButtonSet.YES_NO);
}
if (response == ui.Button.YES) {
calEventIds.forEach(function(id, idx) {
if (id != null) {
calEvents[idx].deleteEvent();
Utilities.sleep(20);
}
});
}
}
Logger.log('Updated %s calendar events', numChanges);
}
// Set up a trigger to automatically update the calendar when the spreadsheet is
// modified. See the instructions for how to use this.
function createSpreadsheetEditTrigger() {
var ss = SpreadsheetApp.getActive();
ScriptApp.newTrigger('syncToCalendar')
.forSpreadsheet(ss)
.onEdit()
.create();
}
// Delete the trigger. Use this to stop automatically updating the calendar.
function deleteTrigger() {
// Loop over all triggers.
var allTriggers = ScriptApp.getProjectTriggers();
for (var idx = 0; idx < allTriggers.length; idx++) {
if (allTriggers[idx].getHandlerFunction() === 'syncToCalendar') {
ScriptApp.deleteTrigger(allTriggers[idx]);
}
}
}
I am developing an extension, which is about fetching the list of topics from the server and find if those topics match with the currently opened Gmail messages or not, if found then highlight that topic otherwise don't.
But if already 6 topics are matched, then it should not check or highlight other topics. This one is working but now I have a problem like if I go back from the current message and again come to that message then highlight won't be shown. Also if I open another message, the highlight is not done.
If I remove the code of counter check from the following snippet it works but this will highlight all the topics that are matched instead of just max 6 topics.
var count = 1;
var highlightAllWords = function(topics) {
Object.keys(topics.topics).forEach(function(topic) {
if (count <= 6) {
highlightTopic(topic);
if (topic !== null || !topic.length) {
count += 1;
}
}
});
};
// init highlight CSS
var ruleExistenceDict = {};
var sheet = (function() {
var style = document.createElement('style');
style.appendChild(document.createTextNode('')); // WebKit hack ##
document.head.appendChild(style);
return style.sheet;
})();
var topicData = {
topics: {
hostname: 4,
cto: 19,
aws: 382,
its: 26,
repo: 15,
unsubscribe: 65,
bitbucket: 313,
having: 28,
devops: 414,
frontend: 25,
stepin: 105,
username: 121,
deployed: 24,
vimeo: 460,
gmail: 156,
rds: 486,
clicked: 9,
lai: 850
}
};
function fetchTopics() {
// api call will be done here but for now its done with dummy object
searchPage(topicData);
}
function searchPage(topics) {
highlightAllWords(topics);
}
var count = 1;
var highlightAllWords = function(topics) {
Object.keys(topics.topics).forEach(function(topic) {
if (count <= 6) {
highlightTopic(topic);
if (topic !== null || !topic.length) {
count += 1;
}
}
});
};
function highlightTopic(topic) {
// let found = 0;
let isCompleted = false;
if (topic == null || topic.length === 0) return;
var topicRegex = new RegExp(topic, 'gi');
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT, {
acceptNode: function(node) {
var result = NodeFilter.FILTER_SKIP;
if (topicRegex.test(node.nodeValue)) {
// found += 1;
// if (found <= 6) {
result = NodeFilter.FILTER_ACCEPT;
return result;
// }
}
}
},
false
);
var skipTagName = {
NOSCRIPT: true,
SCRIPT: true,
STYLE: true
};
var nodeList = [];
// let count = 1;
console.log('count near nextNode', count);
while (treeWalker.nextNode()) {
if (!skipTagName[treeWalker.currentNode.parentNode.tagName]) {
nodeList.push(treeWalker.currentNode);
// count = count + 1;
// console.log('count:' + count);
}
}
nodeList.forEach(function(n) {
var rangeList = [];
// find sub-string ranges
var startingIndex = 0;
do {
// console.log(word, startingIndex, n.parentNode, n.textContent);
startingIndex = n.textContent.indexOf(topic, startingIndex + 1);
if (startingIndex !== -1) {
var topicRange = document.createRange();
topicRange.setStart(n, startingIndex);
topicRange.setEnd(n, startingIndex + topic.length);
rangeList.push(topicRange);
}
} while (startingIndex !== -1);
// highlight all ranges
rangeList.forEach(function(r) {
highlightRange(r);
});
});
}
var highlightRange = function(range) {
const bgColorCode = '#000000';
var anchor = document.createElement('A');
var selectorName = (anchor.className = 'highlighted_text');
anchor.classList.add('highlighted_text');
if (!ruleExistenceDict[bgColorCode]) {
sheet.insertRule(
[
'.',
selectorName,
' { background: #',
bgColorCode,
' !important; }'
].join(''),
0
);
ruleExistenceDict[bgColorCode] = true;
console.log(sheet);
}
anchor.appendChild(range.extractContents());
anchor.href = `https://app.com/profile/topics/${range.extractContents()}`;
range.insertNode(anchor);
};
Here is the full code:
https://gist.github.com/MilanRgm/5d6b9861be1326ba8b049ccfb6c3b376
You should declare the count variable inside the function, so that every time you refresh the page, the count will start from 1 again. Please update your code as follows:
var highlightAllWords = function(topics) {
var count = 1;
Object.keys(topics.topics).forEach(function(topic) {
if (count <= 6) {
highlightTopic(topic);
if (topic !== null || !topic.length) {
count += 1;
}
}
});
};
I am currently using the code below to detect if a URL is pasted into a contenteditable div. If a URL is pasted, it will automatically be converted into a link (surrounded by a tags).
How would I change this so that if the user pastes an image URL, it would be converted to <img src="https://example.com/image.jpg"> whilst also converting non-image URL's to standard links (surrounded by a tags).
var saveSelection, restoreSelection;
if (window.getSelection && document.createRange) {
saveSelection = function(containerEl) {
var range = window.getSelection().getRangeAt(0);
var preSelectionRange = range.cloneRange();
preSelectionRange.selectNodeContents(containerEl);
preSelectionRange.setEnd(range.startContainer, range.startOffset);
var start = preSelectionRange.toString().length;
return {
start: start,
end: start + range.toString().length
}
};
} else if (document.selection) {
}
function createLink(matchedTextNode) {
var el = document.createElement("a");
el.href = matchedTextNode.data;
el.appendChild(matchedTextNode);
return el;
}
function shouldLinkifyContents(el) {
return el.tagName != "A";
}
function surroundInElement(el, regex, surrounderCreateFunc, shouldSurroundFunc) {
var child = el.lastChild;
while (child) {
if (child.nodeType == 1 && shouldSurroundFunc(el)) {
surroundInElement(child, regex, createLink, shouldSurroundFunc);
} else if (child.nodeType == 3) {
surroundMatchingText(child, regex, surrounderCreateFunc);
}
child = child.previousSibling;
}
}
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
var parent = textNode.parentNode;
var result, surroundingNode, matchedTextNode, matchLength, matchedText;
while ( textNode && (result = regex.exec(textNode.data)) ) {
matchedTextNode = textNode.splitText(result.index);
matchedText = result[0];
matchLength = matchedText.length;
textNode = (matchedTextNode.length > matchLength) ?
matchedTextNode.splitText(matchLength) : null;
surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
parent.insertBefore(surroundingNode, matchedTextNode);
parent.removeChild(matchedTextNode);
}
}
var textbox = $('.editable')[0];
var urlRegex = /http(s?):\/\/($|[^\s]+)/;
function updateLinks() {
var savedSelection = saveSelection(textbox);
surroundInElement(textbox, urlRegex, createLink, shouldLinkifyContents);
restoreSelection(textbox, savedSelection);
}
var $textbox = $(textbox);
$(document).ready(function () {
$textbox.focus();
var keyTimer = null, keyDelay = 1000;
$textbox.keyup(function() {
if (keyTimer) {
window.clearTimeout(keyTimer);
}
keyTimer = window.setTimeout(function() {
updateLinks();
keyTimer = null;
}, keyDelay);
});
});
Did you try to parse the pasted url, and search for ending extension (jpg,gif,png) ?
It should be simple, if the ending is matching one of those, then you wrap the url into an href propriety.
Did You wrote this code by yourself?
Here you can read about strings methods to do this:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
I have a script that is searching for duplicated text strings in an array and changing the colors.
function checkDuplicates() {
var values = new Array();
var $input = $('input[type=\'text\']');
var error = 0;
$input.each(function() {
$(this).removeClass('double-error');
var that = this;
if (that.value!='') {
values[that.value] = 0;
$('input[type=\'text\']').each(function() {
if (this.value == that.value) {
values[that.value]++;
}
});
} //endif
});
$input.each(function(key) {
if (values[this.value]>1) {
error++;
$(this).addClass('double-error');
}
});
return (error <= 0); //returns false or true
}
<style type="text/css">
.double-error {
color:red;
border:1px solid red;
}
</style>
This is working fine.
However, I need to count duplicated strings and add keep track of whether they are the first occurence of that word, the second occurence of that word, etc.
For example:
Given john, john, peter, doe, peter, john, the result would be john-1, john-2, peter-1, doe, peter-2, john-3.
This is what I currently have:
function eliminateDuplicates() {
var values = new Array();
var $input = $('input[type=\'text\']');
var error = 0;
$input.each(function() {
$(this).removeClass('double-error');
var that = this;
if (that.value!='') {
values[that.value] = 0;
$('input[type=\'text\']').each(function() {
if (this.value == that.value) {
values[that.value]++;
}
});
}
});
$input.each(function(key) {
if (values[this.value]>1) {
error++;
myArray = values[this.value];
for (var i = 0; i < myArray; i++) {
$(this).parent()
.find('input[type=\'text\']')
.val(this.value + '-' + i);
}
}
});
return error <= 0; //return error > 0 ? false : true;
}
But I got this result:
john-0-1-2, john-0-1-2, peter-0-1, doe, peter-0-1, john-0-1-2
What's wrong?
I make some modification:
function eliminateDuplicates() {
var values = new Array();
var $input = $('input[type=\'text\']');
var error = 0;
$input.each(function() {
$(this).removeClass('double-error');
var that = this;
if (that.value!='') {
values[that.value] = 0;
$('input[type=\'text\']').each(function() {
if (this.value == that.value) {
values[that.value]++;
}
});
}
});
$input.each(function(key) {
if (values[this.value]>1) {
var name=this.value;
var names = values[this.value];
values[this.value]++;
for (var i = 0; i < names; i++){
$(this).parent().find('input[type=\'text\']').val(name + '-' + i);
}
}
});
checkDoubles();
return error <= 0; //return error > 0 ? false : true;
}
now I getting counts in sequence but not from 1.
for example if I have 4 duplicated names (Peter) i getting:
Peter-3, Peter-4, Peter-5, Peter-6.
but I need
Peter-1, Peter-2, Peter-3, Peter-4.
what wrong?
I think you're looking for something like this:
function eliminateDuplicates() {
var repeats = {};
var error = false;
//cache inputs
var $inputs = $("input[type='text']");
//loop through inputs and update repeats
for (i = 0; i < $inputs.length; ++i) {
//cache current element
var cur = $inputs[i];
//remove class
$(cur).removeClass("double-error");
//get text of this element
var text = $(cur).val();
//no text -- continue
if (text === "") {
continue;
}
//first time we've came across this value -- intialize it's counter to 1
if ((text in repeats) === false) {
repeats[text] = 1;
}
//repeat offender. Increment its counter.
else {
repeats[text] = repeats[text] + 1;
}
//update the the value for this one
$(cur).val(text + "-" + repeats[text]);
}
return error; // always returns false since I'm not sure
// when it's supposed to return true.
}
PS: I didn't understand when error was supposed to be true, so it's always false. Nevertheless, this should be enough to get you going.
PPS: Plunker here.
Basically I've made a program where words are inputted and their definitions are found using Wordnik API. Each word is then displayed dynamically and the definition is shown on click. Here's that code:
function define(arr) {
return new Promise(function(resolve, reject) {
var client = [];
var definitions = {};
for (var i = 0, len = arr.length; i < len; i++) {
(function(i) {
client[i] = new XMLHttpRequest();
client[i].onreadystatechange = function() {
if (client[i].readyState === 4 && client[i].status === 200) {
if (client[i].responseText.length === 0) {
console.log(client[i].responseText);
client.responseText[0] = {
word: arr[i],
text: 'Definition not found'
};
}
definitions[arr[i]] = JSON.parse(client[i].responseText);
if (Object.keys(definitions).length === arr.length) {
resolve(definitions);
}
}
};
client[i].open('GET', 'http://api.wordnik.com:80/v4/word.json/' + arr[i] +
'/definitions?limit=1&includeRelated=false&sourceDictionaries=all&useCanonical=false&includeTags=false&api_key=',
true);
client[i].send();
})(i);
}
});
}
function makeFlashCards() {
var data = document.getElementById('inputText').value;
var wordsToDefine = ignore(makeArr(findUniq(data)));
define(wordsToDefine).then(function(result) {
success(result);
}).catch(function(reason) {
console.log('this shouldnt run');
});
}
function success(obj) {
document.getElementById('form').innerHTML = '';
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
addElement('div', obj[prop][0].word);
}
}
attachDefinition(obj);
}
function addElement(type, word) {
var newElement = document.createElement(type);
var content = document.createTextNode(word);
newElement.appendChild(content);
var referenceNode = document.getElementById('form');
document.body.insertBefore(newElement, referenceNode);
newElement.id = word;
newElement.className = "flashcards";
}
function attachDefinition(obj) {
var classArr = document.getElementsByClassName('flashcards');
for (let i = 0, len = classArr.length; i < len; i++) {
classArr[i].addEventListener('click', function() {
cardClicked.call(this, obj);
});
}
}
function cardClicked(obj) {
var el = document.getElementById(this.id);
if (obj[this.id].length !== 0) {
if (this.innerHTML.split(' ').length === 1) {
var img = document.createElement('img');
img.src = 'https://www.wordnik.com/img/wordnik_badge_a2.png';
el.innerHTML = obj[this.id][0].text
+ ' ' + obj[this.id][0].attributionText + '<br>';
el.style['font-weight'] = 'normal';
el.style['font-size'] = '16px';
el.style['text-align'] = 'left';
el.style['overflow'] = 'auto';
el.appendChild(img);
} else {
el.innerHTML = obj[this.id][0].word;
el.style['font-weight'] = 'bold';
el.style['font-size'] = '36px';
el.style['text-align'] = 'center';
el.style['overflow'] = 'visible';
}
}
}
When the define function is given an array with all valid words, the program works as expected however if any word in the array argument is not valid the program doesn't add click event handlers to each element. I think this might have to do with the catch being triggered.
When an invalid word is requested Wordnik API sends back an empty array which might be the root of this problem. I tried to account for this by adding
if (client[i].responseText.length === 0) {
console.log(client[i].responseText);
client.responseText[0] = {
word: arr[i],
text: 'Definition not found'
};
but this conditional never ends up running.
I need some way of filtering out the empty array responses so the catch is not triggered and the program can run smoothly.
When you get to if (client[i].responseText.length === 0) make sure that client[i].responseText is returning empty string. It is probably undefined in which case client[i].responseText.length will throw an error and this will cause the catch block to execute.
function makePromise() {
return new Promise(function(resolve, reject) {
var test = undefined;
if (test.length === 0) {
resolve("resolved");
}
});
}
makePromise().then(console.log).catch(function(res) {
console.log('Error was thrown')
});
Try changing that condition to:
if (client[i].responseText && client[i].responseText.length === 0)