├── .gitignore
├── PostgreSQL_cheatsheet.pdf
├── README.md
├── nbproject
├── customs.json
├── project.properties
└── project.xml
├── postDrafts
└── Android Pay.md
└── public_html
├── AppsScript
├── Apps
│ ├── EventManager.js
│ ├── EventsDateManager.js
│ ├── Insta2Drive.js
│ └── SiteMonitor.js
├── Breezometer-API
│ └── FetchDataToSheet.js
├── Gcal
│ └── EarningCalls.js
├── Gmail
│ └── FetchMailsToSheet.js
├── Search
│ └── lego.js
├── UI
│ ├── DatePicker.js
│ └── htmlDialog.js
├── Utils
│ ├── CheckSheetAndMail.js
│ ├── cleanSheetRange.js
│ ├── dateUtils.js
│ ├── moneyUtils.js
│ ├── parseHTML.js
│ └── runMeAgain.js
└── YouTube
│ └── ytStats.js
├── ListOfScripts.md
├── css
├── bootstrap-responsive.css
├── bootstrap-responsive.min.css
├── bootstrap.css
└── bootstrap.min.css
├── filesList.php
├── img
├── glyphicons-halflings-white.png
└── glyphicons-halflings.png
├── index.html
└── js
├── bootstrap.js
└── bootstrap.min.js
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | nbproject/private/private.xml
3 |
4 | nbproject/private/private.properties
5 |
--------------------------------------------------------------------------------
/PostgreSQL_cheatsheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greenido/AppsScriptBests/9470b0ef54139f4f13b419c140ff889ff0d9aa80/PostgreSQL_cheatsheet.pdf
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AppsScriptBests
2 | ===============
3 |
4 | A little repo to keep track on Google Apps scripts.
5 | Mainly, to see what are a good ways to solve common challenges.
6 |
7 | See: http://greenido.wordpress.com/?s=apps+script to read more.
8 | ### Few Examples
9 |
10 | * [Youtube Analytics Dashboard With Apps Script](https://greenido.wordpress.com/2014/08/04/youtube-analytics-dashboard-with-apps-script)
11 |
12 | * https://greenido.wordpress.com/2014/04/29/monitor-your-site-with-apps-script/
13 |
14 | * https://greenido.wordpress.com/2013/07/24/big-query-power-with-javascript/
15 |
16 | ## Current components
17 |
18 | * [Apps](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps)
19 | * [Apps/EventManager.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/EventManager.js)
20 | * [Apps/EventsDateManager.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/EventsDateManager.js)
21 | * [Apps/Insta2Drive.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/Insta2Drive.js)
22 | * [Apps/SiteMonitor.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/SiteMonitor.js)
23 | * [Gcal](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Gcal)
24 | * [Gcal/EarningCalls.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Gcal/EarningCalls.js)
25 | * [Search](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Search)
26 | * [Search/lego.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Search/lego.js)
27 | * [UI](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI)
28 | * [UI/DatePicker.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI/DatePicker.js)
29 | * [UI/htmlDialog.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI/htmlDialog.js)
30 | * [Utils](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils)
31 | * [Utils/cleanSheetRange.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/cleanSheetRange.js)
32 | * [Utils/dateUtils.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/dateUtils.js)
33 | * [Utils/moneyUtils.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/moneyUtils.js)
34 | * [Utils/parseHTML.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/parseHTML.js)
35 | * [Utils/runMeAgain.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/runMeAgain.js)
36 | * [YouTube](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/YouTube)
37 | * [YouTube/ytStats.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/YouTube/ytStats.js)
38 |
39 | ### Other Sources
40 |
50 |
51 | ### Todo
52 |
53 | 1. Use a side bar to hold the menu for all the content
54 | 2. Allow a short html/markup explanation page per module
55 | 3. Add an example to track a form and send an email when new ppl filled it.
56 | 4. Add an example to track PSI.
57 | 5. Build a wish list with ideas for libs.
58 |
59 |
60 |
61 | [](https://github.com/igrigorik/ga-beacon)
62 |
63 |
--------------------------------------------------------------------------------
/nbproject/customs.json:
--------------------------------------------------------------------------------
1 | {
2 | "attributes": {
3 | "author": {
4 | "context": "meta"
5 | }
6 | },
7 | "elements": {}
8 | }
--------------------------------------------------------------------------------
/nbproject/project.properties:
--------------------------------------------------------------------------------
1 | auxiliary.org-netbeans-modules-web-clientproject-api.js_2e_libs_2e_folder=js
2 | config.folder=${file.reference.AppsScriptBests-config}
3 | file.reference.AppsScriptBests-config=config
4 | file.reference.AppsScriptBests-public_html=public_html
5 | file.reference.AppsScriptBests-test=test
6 | files.encoding=UTF-8
7 | site.root.folder=${file.reference.AppsScriptBests-public_html}
8 | test.folder=${file.reference.AppsScriptBests-test}
9 |
--------------------------------------------------------------------------------
/nbproject/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | org.netbeans.modules.web.clientproject
4 |
5 |
6 | AppsScriptBests
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/postDrafts/Android Pay.md:
--------------------------------------------------------------------------------
1 | Hey! There is a new digital wallet it calls Android Pay. Google’s new service provides a way to instantly pay for stuff not only in real world stores, but also from mobile apps.
2 |
3 | Android Pay will power in-app and tap-to-pay purchases on mobile devices.
4 |
5 | (img of the pay in action)[]
6 |
7 | In terms of adoption, seven out of ten Android devices are ready for Pay and around 700,000 merchants can accept it in their stores.
8 |
9 |
10 | See it in action
11 | [youtube=https://www.youtube.com/watch?v=OueObu2aA_M]
12 |
13 |
14 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Apps/EventManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Google Apps Script that supports an event registration site with embeded form.
3 | * for more details: https://github.com/greenido/events-site-template
4 | */
5 |
6 | // These variables are used throughout the script
7 | //
8 | // This is the string you use to indicate a "yes" RSVP
9 | var YES = "Yes";
10 |
11 | // This is the string you use to indicate a "no" RSVP
12 | var NO = "No";
13 |
14 | // This string indicates someones on the waitlist
15 | var WAITLIST = "Waitlist";
16 |
17 | // The number of questions on the form.
18 | var NUM_FORM_QUESTIONS = 12;
19 |
20 | // This function is called by a trigger when the form is submitted.
21 | // It sends a confirmation email to the person who just submitted the form,
22 | // using the template in cell A1 of the Email Templates sheet.
23 | function sendInitialConfirmationEmail() {
24 | var ss = SpreadsheetApp.getActiveSpreadsheet();
25 | var dataSheet = ss.getSheetByName("Registration");
26 | var dataRange = dataSheet.getRange(2, 2, dataSheet.getMaxRows() - 1, NUM_FORM_QUESTIONS + 1);
27 | var startRow = 2; // First row of data to process
28 | var templateSheet = ss.getSheetByName("Email Templates");
29 | var emailTemplate = "";
30 |
31 | // Create one JavaScript object per row of data.
32 | objects = getRowsData(dataSheet, dataRange);
33 |
34 | // For every row object, create a personalized email from a template and send
35 | // it to the appropriate person.
36 | for (var i = 0; i < objects.length; ++i) {
37 | // Get a row object
38 | var rowData = objects[i];
39 | if (rowData.status != YES &&
40 | rowData.status != NO &&
41 | rowData.status != WAITLIST) { // Prevents sending duplicate confirmations
42 |
43 | var numYes = 0;
44 |
45 | // Go through each existing row and count the number of "YES" responses
46 | for (var j = 0; j < objects.length; ++j) {
47 | var registrations = objects[j];
48 | if (registrations.status == YES) {
49 | ++numYes;
50 | }
51 | }
52 |
53 | //
54 | // GI: Important - this is the new location of our MAX number of users!
55 | var max = dataSheet.getRange("O2").getValue();
56 | if ((numYes < max)) {
57 | //set the right template (registration confirmed)
58 | emailTemplate = templateSheet.getRange("A1").getValue();
59 | dataSheet.getRange(startRow + i, NUM_FORM_QUESTIONS + 2).setValue(YES);
60 | // Make sure the cell is updated right away in case the script is interrupted
61 | SpreadsheetApp.flush();
62 | } else {
63 | dataSheet.getRange(startRow + i, NUM_FORM_QUESTIONS + 2).setValue(WAITLIST);
64 | //set the right template (waitlist)
65 | emailTemplate = templateSheet.getRange("A2").getValue();
66 |
67 | // Make sure the cell is updated right away in case the script is interrupted
68 | SpreadsheetApp.flush();
69 | }
70 | // Generate a personalized email.
71 | // Given a template string, replace markers (for instance ${"First Name"}) with
72 | // the corresponding value in a row object (for instance rowData.firstName).
73 | var emailText = fillInTemplateFromObject(emailTemplate, rowData);
74 | var emailSubject = dataSheet.getRange("P2").getValue() + " Registration";
75 |
76 | MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText);
77 | objects = getRowsData(dataSheet, dataRange);
78 | }
79 | }
80 | }
81 |
82 | //this is what gets called when we want to update the waitlist.
83 | function updateWaitlist() {
84 | var ss = SpreadsheetApp.getActiveSpreadsheet();
85 | var dataSheet = ss.getSheetByName("Registration");
86 | var dataRange = dataSheet.getRange(2, 2, dataSheet.getMaxRows() - 1, NUM_FORM_QUESTIONS + 1);
87 | var startRow = 2; // First row of data to process
88 | var templateSheet = ss.getSheetByName("Email Templates");
89 | var emailTemplate = templateSheet.getRange("A3").getValue();
90 |
91 | // Create one JavaScript object per row of data.
92 | objects = getRowsData(dataSheet, dataRange);
93 | // For every row object, create a personalized email from a template and send
94 | // it to the appropriate person.
95 | for (var i = 0; i < objects.length; ++i) {
96 | // Get a row object
97 | var rowData = objects[i];
98 | if (rowData.status == WAITLIST) { //Only contact people who are waitlisted
99 |
100 | var numYes = 0;
101 | //go through each existing row and count the number of "YES"
102 | for (var j = 0; j < objects.length; ++j) {
103 | var registrations = objects[j];
104 | Logger.log(registrations.status);
105 | if (registrations.status == YES) {
106 | ++numYes;
107 | }
108 | }
109 | var max = dataSheet.getRange("O2").getValue();
110 | if ((numYes < max)) {
111 | //set the right template (registration confirmed)
112 | dataSheet.getRange(startRow + i, NUM_FORM_QUESTIONS + 2).setValue(YES);
113 | // Generate a personalized email.
114 | // Given a template string, replace markers (for instance ${"First Name"}) with
115 | // the corresponding value in a row object (for instance rowData.firstName).
116 | var emailText = fillInTemplateFromObject(emailTemplate, rowData);
117 | var emailSubject = dataSheet.getRange("K2").getValue() + " Registration";
118 |
119 | // GI: If you wish to send the email by using alias email add this:
120 | // MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {name: "Ido", replyTo: "example@gmail.com"});
121 | MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText);
122 | objects = getRowsData(dataSheet, dataRange);
123 |
124 | // Make sure the cell is updated right away in case the script is interrupted
125 | SpreadsheetApp.flush();
126 | }
127 |
128 | }
129 | }
130 | }
131 |
132 | //
133 | //Call this when you want to send the call for Feedback email AFTER the event is done
134 | //
135 | function sendFeedbackEmail() {
136 | var ss = SpreadsheetApp.getActiveSpreadsheet();
137 | var dataSheet = ss.getSheetByName("Registration");
138 | var dataRange = dataSheet.getRange(2, 2, dataSheet.getMaxRows() - 1, NUM_FORM_QUESTIONS + 1);
139 | var startRow = 2; // First row of data to process
140 | var templateSheet = ss.getSheetByName("Email Templates");
141 | var emailTemplate = templateSheet.getRange("A5").getValue();
142 |
143 | // Create one JavaScript object per row of data.
144 | objects = getRowsData(dataSheet, dataRange);
145 | // For every row object, create a personalized email from a template and send
146 | // it to the appropriate person.
147 | for (var i = 0; i < objects.length; ++i) {
148 | // Get a row object
149 | var rowData = objects[i];
150 | if (rowData.status == YES) { //Only contact people who are 'yes' status
151 | // Generate a personalized email.
152 | // Given a template string, replace markers (for instance ${"First Name"}) with
153 | // the corresponding value in a row object (for instance rowData.firstName).
154 | var emailText = fillInTemplateFromObject(emailTemplate, rowData);
155 | var emailSubject = dataSheet.getRange("P2").getValue() + " Reminder";
156 |
157 | MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText);
158 |
159 | // Make sure the cell is updated right away in case the script is interrupted
160 | SpreadsheetApp.flush();
161 | }
162 | }
163 | }
164 |
165 |
166 |
167 | //Call this when you want to send the reminder email to confirmed attendees
168 | //about a week before the event
169 | function sendReminderEmail() {
170 | var ss = SpreadsheetApp.getActiveSpreadsheet();
171 | var dataSheet = ss.getSheetByName("Registration");
172 | var dataRange = dataSheet.getRange(2, 2, dataSheet.getMaxRows() - 1, NUM_FORM_QUESTIONS + 1);
173 | var startRow = 2; // First row of data to process
174 | var templateSheet = ss.getSheetByName("Email Templates");
175 | var emailTemplate = templateSheet.getRange("A4").getValue();
176 |
177 | // Create one JavaScript object per row of data.
178 | objects = getRowsData(dataSheet, dataRange);
179 | // For every row object, create a personalized email from a template and send
180 | // it to the appropriate person.
181 | for (var i = 0; i < objects.length; ++i) {
182 | // Get a row object
183 | var rowData = objects[i];
184 | if (rowData.status == YES) { //Only contact people who are 'yes' status
185 | // Generate a personalized email.
186 | // Given a template string, replace markers (for instance ${"First Name"}) with
187 | // the corresponding value in a row object (for instance rowData.firstName).
188 | var emailText = fillInTemplateFromObject(emailTemplate, rowData);
189 | var emailSubject = dataSheet.getRange("P2").getValue() + " Reminder";
190 |
191 | MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText);
192 |
193 |
194 | // Make sure the cell is updated right away in case the script is interrupted
195 | SpreadsheetApp.flush();
196 | }
197 | }
198 | }
199 |
200 | // Replaces markers in a template string with values define in a JavaScript data object.
201 | // Arguments:
202 | // - template: string containing markers, for instance ${"Column name"}
203 | // - data: JavaScript object with values to that will replace markers. For instance
204 | // data.columnName will replace marker ${"Column name"}
205 | // Returns a string without markers. If no data is found to replace a marker, it is
206 | // simply removed.
207 | function fillInTemplateFromObject(template, data) {
208 | var email = template;
209 | // Search for all the variables to be replaced, for instance ${"Column name"}
210 | var templateVars = template.match(/\$\{\"[^\"]+\"\}/g);
211 |
212 | // Replace variables from the template with the actual values from the data object.
213 | // If no value is available, replace with the empty string.
214 | for (var i = 0; i < templateVars.length; ++i) {
215 | // normalizeHeader ignores ${"} so we can call it directly here.
216 | var variableData = data[normalizeHeader(templateVars[i])];
217 | email = email.replace(templateVars[i], variableData || "");
218 | }
219 |
220 | return email;
221 | }
222 |
223 |
224 |
225 |
226 |
227 | //////////////////////////////////////////////////////////////////////////////////////////
228 | //
229 | // The code below is reused from the 'Reading Spreadsheet data using JavaScript Objects'
230 | // tutorial.
231 | //
232 | //////////////////////////////////////////////////////////////////////////////////////////
233 |
234 | // getRowsData iterates row by row in the input range and returns an array of objects.
235 | // Each object contains all the data for a given row, indexed by its normalized column name.
236 | // Arguments:
237 | // - sheet: the sheet object that contains the data to be processed
238 | // - range: the exact range of cells where the data is stored
239 | // - columnHeadersRowIndex: specifies the row number where the column names are stored.
240 | // This argument is optional and it defaults to the row immediately above range;
241 | // Returns an Array of objects.
242 | function getRowsData(sheet, range, columnHeadersRowIndex) {
243 | columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
244 | var numColumns = range.getEndColumn() - range.getColumn() + 1;
245 | var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
246 | var headers = headersRange.getValues()[0];
247 | return getObjects(range.getValues(), normalizeHeaders(headers));
248 | }
249 |
250 | // For every row of data in data, generates an object that contains the data. Names of
251 | // object fields are defined in keys.
252 | // Arguments:
253 | // - data: JavaScript 2d array
254 | // - keys: Array of Strings that define the property names for the objects to create
255 | function getObjects(data, keys) {
256 | var objects = [];
257 | for (var i = 0; i < data.length; ++i) {
258 | var object = {};
259 | var hasData = false;
260 | for (var j = 0; j < data[i].length; ++j) {
261 | var cellData = data[i][j];
262 | if (isCellEmpty(cellData)) {
263 | continue;
264 | }
265 | object[keys[j]] = cellData;
266 | hasData = true;
267 | }
268 | if (hasData) {
269 | objects.push(object);
270 | }
271 | }
272 | return objects;
273 | }
274 |
275 | // Returns an Array of normalized Strings.
276 | // Arguments:
277 | // - headers: Array of Strings to normalize
278 | function normalizeHeaders(headers) {
279 | var keys = [];
280 | for (var i = 0; i < headers.length; ++i) {
281 | var key = normalizeHeader(headers[i]);
282 | if (key.length > 0) {
283 | keys.push(key);
284 | }
285 | }
286 | return keys;
287 | }
288 |
289 | // Normalizes a string, by removing all alphanumeric characters and using mixed case
290 | // to separate words. The output will always start with a lower case letter.
291 | // This function is designed to produce JavaScript object property names.
292 | // Arguments:
293 | // - header: string to normalize
294 | // Examples:
295 | // "First Name" -> "firstName"
296 | // "Market Cap (millions) -> "marketCapMillions
297 | // "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
298 | function normalizeHeader(header) {
299 | var key = "";
300 | var upperCase = false;
301 | for (var i = 0; i < header.length; ++i) {
302 | var letter = header[i];
303 | if (letter == " " && key.length > 0) {
304 | upperCase = true;
305 | continue;
306 | }
307 | if (!isAlnum(letter)) {
308 | continue;
309 | }
310 | if (key.length == 0 && isDigit(letter)) {
311 | continue; // first character must be a letter
312 | }
313 | if (upperCase) {
314 | upperCase = false;
315 | key += letter.toUpperCase();
316 | } else {
317 | key += letter.toLowerCase();
318 | }
319 | }
320 | return key;
321 | }
322 |
323 | // Returns true if the cell where cellData was read from is empty.
324 | // Arguments:
325 | // - cellData: string
326 | function isCellEmpty(cellData) {
327 | return typeof(cellData) == "string" && cellData == "";
328 | }
329 |
330 | // Returns true if the character char is alphabetical, false otherwise.
331 | function isAlnum(char) {
332 | return char >= 'A' && char <= 'Z' ||
333 | char >= 'a' && char <= 'z' ||
334 | isDigit(char);
335 | }
336 |
337 | // Returns true if the character char is a digit, false otherwise.
338 | function isDigit(char) {
339 | return char >= '0' && char <= '9';
340 | }
341 |
342 | function setMaxAttendees() {
343 | var max = Browser.inputBox("Please enter the maximum number of attendees:");
344 | var matchDigits = /^\d+$/;
345 | if (max.search(matchDigits) == -1) {
346 | Browser.msgBox("Invalid input.");
347 | } else {
348 | SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Registration").getRange("J2").setValue(max);
349 | }
350 | }
351 |
352 | function onOpen() {
353 | var ss = SpreadsheetApp.getActiveSpreadsheet();
354 | var menuEntries = [ {name: "Set Max Attendees", functionName: "setMaxAttendees"},
355 | {name: "Update Waitlist", functionName: "updateWaitlist"},
356 | {name: "Send Initial Emails", functionName: "sendInitialConfirmationEmail"},
357 | {name: "Send Reminder Emails", functionName: "sendReminderEmail"},
358 | {name: "Export Range as CSV", functionName: "saveAsCSV"}];
359 | ss.addMenu("Hackathon", menuEntries);
360 | }
361 |
362 | function saveAsCSV() {
363 | // Prompts the user for the file name
364 | var fileName = Browser.inputBox("Save CSV file as (e.g. myCSVFile):");
365 |
366 | // Check that the file name entered wasn't empty
367 | if (fileName.length !== 0) {
368 | // Add the ".csv" extension to the file name
369 | fileName = fileName + ".csv";
370 | // Convert the range data to CSV format
371 | var csvFile = convertRangeToCsvFile_(fileName);
372 | // Create a file in the Docs List with the given name and the CSV data
373 | DocsList.createFile(fileName, csvFile);
374 | } else {
375 | Browser.msgBox("Error: Please enter a CSV file name.");
376 | }
377 | }
378 |
379 | function convertRangeToCsvFile_(csvFileName) {
380 | // Get the selected range in the spreadsheet
381 | var ws = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection();
382 | try {
383 | var data = ws.getValues();
384 | var csvFile = undefined;
385 |
386 | // Loop through the data in the range and build a string with the CSV data
387 | if (data.length > 1) {
388 | var csv = "";
389 | for (var row = 0; row < data.length; row++) {
390 | for (var col = 0; col < data[row].length; col++) {
391 | if (data[row][col].toString().indexOf(",") != -1) {
392 | data[row][col] = "\"" + data[row][col] + "\"";
393 | }
394 | }
395 |
396 | // Join each row's columns
397 | // Add a carriage return to end of each row, except for the last one
398 | if (row < data.length-1) {
399 | csv += data[row].join(",") + "\r\n";
400 | } else {
401 | csv += data[row];
402 | }
403 | }
404 | csvFile = csv;
405 | }
406 | return csvFile;
407 | }
408 | catch(err) {
409 | Logger.log(err);
410 | Browser.msgBox(err);
411 | }
412 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/Apps/EventsDateManager.js:
--------------------------------------------------------------------------------
1 | // EventManagerV3 glued together by mhawksey http://www.google.com/profiles/m.hawksey
2 | // Related blog post http://mashe.hawksey.info/eventmanagerv3/
3 | // With some code (settings, importIntoCalendar, sendEmails) from
4 | // Romain Vialard's http://www.google.com/profiles/romain.vialard
5 | // Manage your events: Calendar Importer and Registration Form
6 | // https://spreadsheets.google.com/ccc?key=tCHuQkkKh_r69bQGt4yJmNQ
7 | var ss = SpreadsheetApp.getActiveSpreadsheet();
8 | var BOOKING_ACTION_COL = 10;
9 | function onOpen() {
10 | var conf = ss.getSheetByName("Templates").getRange("B3").getValue();
11 | if (conf == "") {
12 | ss.toast("Click on setup to start", "Welcome", 10);
13 | }
14 | var menuEntries = [{name: "Process Events", functionName: "importIntoCalendar"}, {name: "Process Bookings", functionName: "sendBookingConf"}, {name: "Email joining instructions", functionName: "sendJoinEmails"}];
15 | ss.addMenu("Event Manager", menuEntries);
16 | }
17 | function settings() {
18 | var calendarName = Browser.inputBox("First create a calendar in Google Calendar and enter its name here:");
19 | if (calendarName != "cancel" && calendarName != "") {
20 | var templateSheet = ss.getSheetByName("Templates");
21 | templateSheet.getRange("E1").setValue(calendarName);
22 | var formURL = ss.getFormUrl();
23 | templateSheet.getRange("B3").setValue(formURL);
24 | var calTimeZone = CalendarApp.openByName(calendarName).getTimeZone();
25 | ss.setSpreadsheetTimeZone(calTimeZone);
26 | var timeZone = ss.getSpreadsheetTimeZone();
27 | var siteUrl = Browser.inputBox("If you would like to update events to a Sites page enter your announcement page url");
28 | if (siteUrl != "cancel" && siteUrl != "") {
29 | templateSheet.getRange("E2").setValue(siteUrl);
30 | }
31 | var adminEmail = Browser.inputBox("Please enter your administrator email address");
32 | if (adminEmail != "cancel" && adminEmail != "") {
33 | templateSheet.getRange("B4").setValue(adminEmail);
34 | }
35 | ss.toast("You can now import events in your calendar. Time Zone is currently set to: " + timeZone + ".", "Set up completed!", -1);
36 | SpreadsheetApp.flush();
37 | }
38 | }
39 | function importIntoCalendar() {
40 | var dataSheet = ss.getSheetByName("Put your events here");
41 | var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows(), dataSheet.getMaxColumns());
42 | var templateSheet = ss.getSheetByName("Templates");
43 | var calendarName = templateSheet.getRange("E1").getValue();
44 | var siteUrl = templateSheet.getRange("E2").getValue();
45 | if (calendarName != "") {
46 | var cal = CalendarApp.getCalendarsByName(calendarName)[0];
47 | var eventTitleTemplate = templateSheet.getRange("E3").getValue();
48 | var descriptionTemplate = templateSheet.getRange("E4").getValue();
49 | // Create one JavaScript object per row of data.
50 | objects = getRowsData(dataSheet, dataRange);
51 | // For every row object, create a personalized email from a template and send
52 | // it to the appropriate person.
53 | for (var i = 0; i < objects.length; ++i) {
54 | // Get a row object
55 | var rowData = objects[i];
56 | if (rowData.eventId && rowData.eventTitle && rowData.action == "Y") {
57 | var eventTitle = fillInTemplateFromObject(eventTitleTemplate, rowData);
58 | var description = fillInTemplateFromObject(descriptionTemplate, rowData);
59 | // add to calendar bit
60 | if (rowData.endDate == "All-day") {
61 | cal.createAllDayEvent(eventTitle, rowData.startDate, rowData.endDate, {location: rowData.location, description: description}).addEmailReminder(15).setTag("Event ID", rowData.eventId);
62 | }
63 | else {
64 | cal.createEvent(eventTitle, rowData.startDate, rowData.endDate, {location: rowData.location, description: description}).addEmailReminder(15).setTag("Event ID", rowData.eventId);
65 | }
66 | // add to site bit
67 | if (siteUrl != "") {
68 | var page = SitesApp.getPageByUrl(siteUrl);
69 | var announcement = page.createAnnouncement(rowData.eventTitle, description);
70 | }
71 | // create event sheet
72 | var temp = ss.getSheetByName("EventTMP");
73 | var eventSheet = ss.insertSheet("Event " + rowData.eventId, {template: temp});
74 | eventSheet.getRange(1, 2, 1, 1).setValue(rowData.numberOfPlaces);
75 | eventSheet.getRange(1, 3, 1, 1).setValue(rowData.eventTitle);
76 | eventSheet.getRange(2, 3, 1, 1).setValue(rowData.location);
77 | eventSheet.getRange(3, 6, 1, 1).setValue(rowData.startDate);
78 | eventSheet.getRange(3, 8, 1, 1).setValue(rowData.endDate);
79 | dataSheet.getRange(i + 2, 1, 1, 1).setValue("");
80 | dataSheet.getRange(i + 2, 2, 1, 1).setValue("Added " + new Date()).setBackgroundRGB(221, 221, 221);
81 | dataSheet.getRange(i + 2, 1, 1, dataSheet.getMaxColumns()).setBackgroundRGB(221, 221, 221);
82 | // Make sure the cell is updated right away in case the script is interrupted
83 | SpreadsheetApp.flush();
84 | }
85 | }
86 | ss.toast("People can now register to those events", "Events imported");
87 | }
88 | }
89 | function onFormSubmit() {
90 | var dataSheet = ss.getSheetByName("Bookings");
91 | var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows(), dataSheet.getMaxColumns());
92 | var templateSheet = ss.getSheetByName("Templates");
93 | var emailTemplate = templateSheet.getRange("E6").getValue();
94 | var adminEmail = templateSheet.getRange("B4").getValue()
95 | // Create one JavaScript object per row of data.
96 | data = getRowsData(dataSheet, dataRange);
97 | for (var i = 0; i < data.length; ++i) {
98 | // Get a row object
99 | var row = data[i];
100 | row.rowNumber = i + 2;
101 | if (!row.action) { // if no state notify admin of booking
102 | var emailText = fillInTemplateFromObject(emailTemplate, row);
103 | var emailSubject = "Booking Approval Request ID: " + row.rowNumber;
104 | MailApp.sendEmail(adminEmail, emailSubject, emailText);
105 | dataSheet.getRange(row.rowNumber, BOOKING_ACTION_COL).setValue("TBC"); //9 is the column number for 'Action'
106 | }
107 | }
108 | }
109 | function sendBookingConf() {
110 | var dataSheet = ss.getSheetByName("Bookings");
111 | var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows(), dataSheet.getMaxColumns());
112 | var templateSheet = ss.getSheetByName("Templates");
113 | var emailSubjectTemplate = templateSheet.getRange("B1").getValue();
114 | var emailTemplate = templateSheet.getRange("B2").getValue();
115 | var emailSentColumn = BOOKING_ACTION_COL;
116 | // To add guests into Calendar
117 | var calendarDataSheet = ss.getSheetByName("Put your events here");
118 | var calendarDataRange = calendarDataSheet.getRange(2, 1, calendarDataSheet.getMaxRows(), calendarDataSheet.getMaxColumns());
119 | var calendarName = templateSheet.getRange("E1").getValue();
120 | // Create one JavaScript object per row of data.
121 | calendarObjects = getRowsData(calendarDataSheet, calendarDataRange);
122 | // Create one JavaScript object per row of data.
123 | objects = getRowsData(dataSheet, dataRange);
124 | // For every row object, create a personalized email from a template and send
125 | // it to the appropriate person.
126 | for (var i = 0; i < objects.length; ++i) {
127 | // Get a row object
128 | var rowData = objects[i];
129 | if (rowData.action == "Y") { // Prevents sending duplicates
130 | // add guest in calendar
131 | for (var j = 0; j < calendarObjects.length; ++j) {
132 | // Get a row object
133 | var calendarRowData = calendarObjects[j];
134 | if (calendarRowData.eventId == rowData.eventId) {
135 | var cal = CalendarApp.openByName(calendarName);
136 | if (calendarRowData.endDate == "All-day") {
137 | var getDate = new Date(calendarRowData.startDate).getTime();
138 | var endDate = new Date().setTime(getDate + 86400000);
139 | var events = cal.getEvents(new Date(calendarRowData.startDate), new Date(endDate));
140 | }
141 | else {
142 | var events = cal.getEvents(new Date(calendarRowData.startDate), new Date(calendarRowData.endDate));
143 | }
144 | for (var k in events) {
145 | if (events[k].getTag("Event ID") == rowData.eventId) {
146 | events[k].addGuest(rowData.email);
147 | j = calendarObjects.length;
148 | }
149 | }
150 | }
151 | }
152 | // Generate a personalized email.
153 | // Given a template string, replace markers (for instance ${"First Name"}) with
154 | // the corresponding value in a row object (for instance rowData.firstName).
155 | calendarRowData.bookingId = rowData.eventId + "-B" + (i + 2);
156 | calendarRowData.firstName = rowData.firstName;
157 | var emailSubject = fillInTemplateFromObject(emailSubjectTemplate, calendarRowData);
158 | var emailText = fillInTemplateFromObject(emailTemplate, calendarRowData);
159 | var emailAddress = rowData.email;
160 | MailApp.sendEmail(emailAddress, emailSubject, emailText, {htmlBody: emailText});
161 | // add booking to right event sheet
162 | dataSheet.getRange(i + 2, emailSentColumn).setValue(calendarRowData.bookingId).setBackgroundRGB(221, 221, 221);
163 | var eventSheet = ss.getSheetByName("Event " + rowData.eventId);
164 | var rowNum = eventSheet.getLastRow() + 1;
165 | eventSheet.getRange(rowNum, 3, 1, 1).setValue(calendarRowData.bookingId);
166 | eventSheet.getRange(rowNum, 4, 1, 1).setValue(rowData.timestamp);
167 | eventSheet.getRange(rowNum, 5, 1, 1).setValue(rowData.firstName);
168 | eventSheet.getRange(rowNum, 6, 1, 1).setValue(rowData.lastName);
169 | eventSheet.getRange(rowNum, 7, 1, 1).setValue(rowData.email);
170 | eventSheet.getRange(rowNum, 8, 1, 1).setValue(rowData.organisationName);
171 | eventSheet.getRange(rowNum, 9, 1, 1).setValue(rowData.startPostcode);
172 | eventSheet.getRange(rowNum, 10, 1, 1).setValue(rowData.preferredMode);
173 | eventSheet.getRange(rowNum, 11, 1, 1).setValue(rowData.otherInfo);
174 | eventSheet.getRange(rowNum, 12, 1, 1).setValue(rowData.comments);
175 | // Make sure the cell is updated right away in case the script is interrupted
176 | // Add delegate to Contacts
177 | var curDate = Utilities.formatDate(new Date(), "GMT", "dd/MM/yy HH:mm");
178 | var c = ContactsApp.findByEmailAddress(rowData.email);
179 | if (!c) {
180 | var c = ContactsApp.createContact(rowData.firstName, rowData.lastName, rowData.email);
181 | var prop = {};
182 | prop.Organisation = rowData.organisationName;
183 | prop.Added = curDate;
184 | c.setUserDefinedFields(prop);
185 | var group = ContactsApp.findContactGroup(rowData.organisationName);
186 | if (!group) {
187 | var group = ContactsApp.createContactGroup(rowData.organisationName);
188 | }
189 | c.addToGroup(group);
190 | } else {
191 | c.setUserDefinedField("Last activity", curDate);
192 | }
193 | SpreadsheetApp.flush();
194 | }
195 | }
196 | ss.toast("", "Emails sent", -1);
197 | }
198 | // Code to send joining instructions - based on simple mail merge code from
199 | // Tutorial: Simple Mail Merge
200 | // Hugo Fierro, Google Spreadsheet Scripts Team
201 | // March 2009
202 | function sendJoinEmails() {
203 | var ss = SpreadsheetApp.getActiveSpreadsheet();
204 | var dataSheet = ss.getActiveSheet();
205 | var eventName = ss.getRange("C1").getValue();// pull event name from sheet
206 | var location = ss.getRange("C2").getValue();// pull event location
207 | var emailCount = 0;
208 | var dataRange = dataSheet.getRange(5, 3, dataSheet.getMaxRows(), dataSheet.getMaxColumns());
209 | var templateSheet = ss.getSheetByName("Templates");
210 | var emailTemplate = templateSheet.getRange("B6").getValue();
211 | var emailSubject = templateSheet.getRange("B5").getValue();
212 | emailSubject = emailSubject.replace('${"Event Name"}', eventName);
213 | // Create one JavaScript object per row of data.
214 | objects = getRowsData(dataSheet, dataRange, 4);
215 | // For every row object, create a personalized email from a template and send
216 | // it to the appropriate person.
217 | for (var i = 0; i < objects.length; ++i) {
218 | // Get a row object
219 | var rowData = objects[i];
220 | rowData.eventName = eventName;
221 | rowData.rowNumber = i + 5;
222 | // Generate a personalized email.
223 | if (!rowData.emailed) {
224 | if (rowData.startPostcode && (location != "Online" || location)) {
225 | rowData.directions = getMapDirections_(rowData.startPostcode, location, rowData.mode);
226 | }
227 | var emailText = fillInTemplateFromObject(emailTemplate, rowData);
228 | try {
229 | MailApp.sendEmail(rowData.emailAddress, emailSubject, 'Please view in HTML capable email client.', {htmlBody: emailText});
230 | emailCount++;
231 | dataSheet.getRange(rowData.rowNumber, 2).setValue(Utilities.formatDate(new Date(), "GMT", "dd/MM/yy HH:mm"));
232 | } catch (e) {
233 | Browser.msgBox("There was a problem sending to " + rowData.emailAddress);
234 | }
235 | }
236 | }
237 | ss.toast("Joining instructions have been sent to " + emailCount + " delegates", "Joining instructions sent", 5);
238 | }
239 |
240 |
241 | // Modified from Send customized driving directions in a mail merge template
242 | // http://code.google.com/googleapps/appsscript/templates.html
243 | function getMapDirections_(start, end, mode) {
244 | // Generate personalized static map with directions.
245 | switch (mode)
246 | {
247 | case "Cycling":
248 | var directions = Maps.newDirectionFinder()
249 | .setOrigin(start)
250 | .setDestination(end)
251 | .setMode(Maps.DirectionFinder.Mode.BICYCLING)
252 | .getDirections();
253 | break;
254 | case "Walking":
255 | var directions = Maps.newDirectionFinder()
256 | .setOrigin(start)
257 | .setDestination(end)
258 | .setMode(Maps.DirectionFinder.Mode.WALKING)
259 | .getDirections();
260 | break;
261 | default:
262 | var directions = Maps.newDirectionFinder()
263 | .setOrigin(start)
264 | .setDestination(end)
265 | .setMode(Maps.DirectionFinder.Mode.DRIVING)
266 | .getDirections();
267 | }
268 | var currentLabel = 0;
269 | var directionsHtml = "";
270 | var map = Maps.newStaticMap().setSize(500, 350);
271 | map.setMarkerStyle(Maps.StaticMap.MarkerSize.SMALL, "red", null);
272 | map.addMarker(start);
273 | map.addMarker(end);
274 | var r1 = new RegExp('', 'g');
275 | var r2 = new RegExp('
', 'g');
276 | var points = [];
277 | var distance = 0;
278 | var time = 0;
279 | for (var i in directions.routes) {
280 | for (var j in directions.routes[i].legs) {
281 | for (var k in directions.routes[i].legs[j].steps) {
282 | var step = directions.routes[i].legs[j].steps[k];
283 | distance += directions.routes[i].legs[j].steps[k].distance.value;
284 | time += directions.routes[i].legs[j].steps[k].duration.value;
285 | var path = Maps.decodePolyline(step.polyline.points);
286 | points = points.concat(path);
287 | var text = step.html_instructions;
288 | text = text.replace(r1, '');
289 | text = text.replace(r2, '');
290 | directionsHtml += "" + (++currentLabel) + " - " + text;
291 | }
292 | }
293 | }
294 | // be conservative, and only sample 100 times...
295 | var lpoints = [];
296 | if (points.length < 200)
297 | lpoints = points;
298 | else {
299 | var pCount = (points.length / 2);
300 | var step = parseInt(pCount / 100);
301 | for (var i = 0; i < 100; ++i) {
302 | lpoints.push(points[i * step * 2]);
303 | lpoints.push(points[(i * step * 2) + 1]);
304 | }
305 | }
306 | // make the polyline if (lpoints.length>0) {
307 | var pline = Maps.encodePolyline(lpoints);
308 | map.addPath(pline);
309 |
310 |
311 | var mapLink = "Click here for complete map and directions";
312 | var dir = 'Travel Directions
' + mapLink +
313 | "Summary of Route" + "Distance: " +
314 | Math.round(0.00621371192 * distance) / 10 + " miles" +
315 | "Time: " +
316 | Math.floor(time / 60) + " minutes" + directionsHtml;
317 | return dir;
318 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/Apps/Insta2Drive.js:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // Search, Fetch and save images from Instagram by tags.
4 | //
5 | function fetchInstagram() {
6 | // Feel free to change this one
7 | var TAG_NAME = 'snowboarding';
8 |
9 | // You need to replace todo with your client-id
10 | // Get it at: http://instagram.com/developer/clients/manage/
11 | var CLIENT_ID = 'todo';
12 | var url = 'https://api.instagram.com/v1/tags/' + TAG_NAME + '/media/recent?client_id=' + CLIENT_ID;
13 |
14 | // fetch the top 10 results
15 | while (i < 10) {
16 | //let us fetch the details from API. This will give you the details of photos and URL
17 | var response = UrlFetchApp.fetch(url).getContentText();
18 | var responseObj = JSON.parse(response);
19 | var photoData = responseObj.data;
20 |
21 | //iterate over this data
22 | for (var i in photoData) {
23 | var imageUrl = photoData[i].images.standard_resolution.url;
24 | Logger.log("image url: "+imageUrl);
25 | //Todo: open this comment if you wish to save the file: fetchImageToDrive_(imageUrl);
26 | }
27 |
28 | //Get the url of the next page
29 | url = responseObj.pagination.next_url;
30 | }
31 | }
32 |
33 | //
34 | // fetch and save the file into drive
35 | //
36 | function fetchImageToDrive_(imageUrl) {
37 | var imageBlob = UrlFetchApp.fetch(imageUrl).getBlob();
38 | // Create image file in drive
39 | var image = DriveApp.createFile(imageBlob);
40 | // return the URL of the newly created image in drive so we could log it.
41 | return image.getUrl();
42 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/Apps/SiteMonitor.js:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Monitor your sites and track their up time with response codes.
4 | * We can control the flow of the emails' alerts from a google form that will give us the option
5 | * to mute.
6 | *
7 | * @author Ido Green | @greenido
8 | *
9 | * @see: http://greenido.wordpress.com/2014/04/29/monitor-your-site-with-apps-script/
10 | *
11 | */
12 |
13 | //
14 | // Let the user control the monitor: start and stop function.
15 | //
16 | function onOpen() {
17 | var sheet = SpreadsheetApp.getActiveSpreadsheet();
18 | var menu = [
19 | {name: "Start ", functionName: "init"},
20 | {name: "Stop", functionName: "removeJobs"}
21 | ];
22 |
23 | sheet.addMenu("Monitor", menu);
24 | }
25 |
26 | //
27 | //
28 | //
29 | function init() {
30 | removeJobs(true);
31 | var sheet = SpreadsheetApp.getActiveSpreadsheet();
32 | var urls = sheet.getSheets()[0].getRange("B2").getValue();
33 | urls = urls.replace(/\s/g, "").split(",");
34 | //var time = (new Date()).getTime();
35 | var db = ScriptDb.getMyDb();
36 |
37 | for (i=0; i 1) {
61 | // Out process is down!
62 | code = 503; // = Our service is not available
63 | }
64 | else {
65 | code = response.getResponseCode();
66 | }
67 | } catch(error) {}
68 |
69 | item.status = checkAndNotify(item, code);
70 | db.save(item);
71 | }
72 | }
73 |
74 | //
75 | //
76 | //
77 | function checkAndNotify(item, code) {
78 | var status = item.status;
79 | // We are cool
80 | if (code === 200) {
81 | return code;
82 | }
83 |
84 | // Site was up previously but is now down
85 | if (code === 503 && (status === 200)) {
86 | // Run another check after 1 minutes to prevent false positives
87 | quickCheck();
88 | return code;
89 | }
90 |
91 | // Site was down previously but up on second check
92 | if (code === 200 && status === 503) {
93 | logToSheet(item.url, "Tweet Process is Up");
94 | return code;
95 | }
96 |
97 | // Site was down previously and is down again
98 | if ((code === -1 || code === 503) && status === 503 ) {
99 | quickCheck();
100 | logToSheet(item.url, "Tweet Process is Down");
101 | return code;
102 | }
103 |
104 | return code;
105 | }
106 |
107 | //
108 | // /use our sheet as the logger to make it easy to debreif cases.
109 | //
110 | function logToSheet(url, message) {
111 |
112 | var sheets = SpreadsheetApp.getActiveSpreadsheet();
113 |
114 | var emailRow = sheets.getSheets()[1].getLastRow();
115 | var toStopEmails = sheets.getSheets()[1].getRange(emailRow, 2).getValue();
116 |
117 | var logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
118 | var row = logSheet.getLastRow() + 1;
119 | var time = new Date();
120 |
121 | logSheet.getRange(row,1).setValue(time);
122 | logSheet.getRange(row,2).setValue(message + " : " + url);
123 |
124 | var alert = "Site: " + url + " is " + message.toLowerCase() + " To stop emails: http://goo.gl/todo-your-own-sheet";
125 |
126 | if (toStopEmails === "Yes") {
127 | Logger.log("We got an alert: " + alert + " But we are not sending emails!");
128 | }
129 | else {
130 | Logger.log("Send an alert: " + alert);
131 | MailApp.sendEmail(logSheet.getRange("B3").getValue(), "AGF Site " + message, alert);
132 | }
133 |
134 | // If you have a sheet with a form this is the location to see if you 'stoped' the notifications
135 | //
136 | // if (sheet.getRange("B4").getValue().toLowerCase() == "yes") {
137 | // time = new Date(time.getTime() + 15000);
138 | // CalendarApp.createEvent(alert, time, time).addSmsReminder(0);
139 | // }
140 |
141 | return;
142 | }
143 |
144 | //
145 | //
146 | //
147 | function secondCheck() {
148 | var triggers = ScriptApp.getProjectTriggers();
149 | for (var i=0; i -1) {
50 | cell.offset(index, 2).setValue(row);
51 | Logger.log("key: " + i + " val: "+ row);
52 | }
53 | else if (i.indexOf("breezometer_aqi") > -1) {
54 | cell.offset(index, 3).setValue(row);
55 | Logger.log("key: " + i + " val: "+ row);
56 | }
57 | else if (i.indexOf("dominant_pollutant_canonical_name") > -1) {
58 | cell.offset(index, 4).setValue(row);
59 | Logger.log("key: " + i + " val: "+ row);
60 | }
61 | else if (i.indexOf("breezometer_color") > -1) {
62 | cell.offset(index, 5).setValue(row);
63 | Logger.log("key: " + i + " val: "+ row);
64 | }
65 | else if (i.indexOf("dominant_pollutant_description") > -1) {
66 | cell.offset(index, 6).setValue(row);
67 | Logger.log("key: " + i + " val: "+ row);
68 | }
69 | else if (i.indexOf("dominant_pollutant_text") > -1) {
70 | cell.offset(index, 7).setValue(row);
71 | Logger.log("key: " + i + " val: "+ row);
72 | }
73 | else if (i.indexOf("random_recommendations") > -1) {
74 | cell.offset(index, 8).setValue(row);
75 | Logger.log("key: " + i + " val: "+ row);
76 | }
77 | }
78 | Logger.log("done with: "+ url);
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Gcal/EarningCalls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Fetch Earning Calls dates and put them on Google Calander
3 | * @Author: Ido Green | @greenido | +GreenIdo
4 | * @Date: Feb 2015
5 | */
6 |
7 |
8 | /**
9 | * 0. Delete all the old/current events.
10 | * 1. Run on your tickers
11 | * 2. Fetch the eraning call for each.
12 | * 3. Create a cal event.
13 | */
14 | function createEraningCallsEvents() {
15 |
16 | // start with a clean cal
17 | deleteAllEarningEvents();
18 |
19 | var ss = SpreadsheetApp.getActiveSpreadsheet();
20 | var sheet = ss.getSheetByName("metaData");
21 | var earningCalendar = CalendarApp.getCalendarById("TODO-email");
22 |
23 | var tickers = sheet.getRange("A:A");
24 | var values = tickers.getValues();
25 |
26 | for (var row in values) {
27 | var ticker = values[row][0];
28 | Logger.log(row +") Working ticker: " + ticker);
29 | if (ticker === "" || ticker.length < 1) {
30 | break;
31 | }
32 |
33 | // get the earning date
34 | var d = getEarningDate(ticker);
35 | if (d !== "N/A") {
36 | // update a new event for this date
37 | createEvent(earningCalendar, d, ticker);
38 | }
39 | else {
40 | Logger.log("Could not add event for: " + ticker + " because didn't get a date");
41 | }
42 | }
43 | }
44 |
45 | //
46 | // Delete all the events we currently have in our cal.
47 | //
48 | function deleteAllEarningEvents() {
49 | var earningCalendar = CalendarApp.getCalendarById("TODO-email");
50 | var now = new Date();
51 | var aYearFromNow = new Date(now.getTime() + (365 * 24 * 60 * 60 * 1000));
52 | var events = earningCalendar.getEvents(now, aYearFromNow, {search: 'Earning call'});
53 | Logger.log('Number of events: ' + events.length);
54 | for (var i=0; i < events.length; i++) {
55 | events[i].deleteEvent();
56 | }
57 | Logger.log("Removed all the events");
58 | }
59 |
60 | //
61 | // unit test for getting the date from finviz
62 | //
63 | function testGetEarningDate() {
64 | var d1 = getEarningDate("GOLD");
65 | Logger.log("GOLD for: " + JSON.stringify(d1));
66 |
67 | var d2 = getEarningDate("LVS");
68 | Logger.log("LVS for: " + JSON.stringify(d2));
69 | }
70 |
71 | //
72 | // Return the eraning date
73 | // year, month, day, hour, minute, second, millisecond
74 | // var d = new Date(99, 5, 24, 11, 33, 30, 0);
75 | //
76 | function getEarningDate(ticker) {
77 | try {
78 | var urlToFetch = "TODO" + ticker;
79 | var urlOptions = { "followRedirects" : true };
80 | var response = UrlFetchApp.fetch(urlToFetch, urlOptions);
81 | var text = response.getContentText();
82 | var inx0 = text.indexOf("snapshot-table2");
83 | var inx1 = text.indexOf(">Earnings", inx0 + 20);
84 | var inx2 = text.indexOf("", inx1 + 10) + 3;
85 | var inx3 = text.indexOf("", inx2);
86 | var dateStr = text.substring(inx2, inx3);
87 | dateStr = dateStr.replace("AMC" ,"");
88 | dateStr = dateStr.replace("BMO" ,"");
89 | Logger.log("Stock: " + ticker + " | Date: " + dateStr);
90 |
91 | var dateMonth = dateStr.substring(0,3);
92 | var dateDay = dateStr.substring(4);
93 |
94 | // convert 3 letters month into a number (1-12) of the month.
95 | var dateMonthNum = "JanFebMarAprMayJunJulAugSepOctNovDec".indexOf(dateMonth) / 3 + 1 ;
96 |
97 | var curMonth = ( (new Date()).getMonth() ) + 1; // 0-11
98 | var curYear = (new Date()).getYear();
99 | var monthsGap = dateMonthNum - curMonth;
100 |
101 | if (monthsGap >= 0 && monthsGap < 5) {
102 | var d = new Date( curYear, dateMonthNum - 1, dateDay , 22, 30, 00, 0);
103 | return d;
104 | } else {
105 | if (curMonth > 9) {
106 | // let's check if we have a month 10,11,12 or later: 1,2,3
107 | if (dateMonthNum > 9 || dateMonthNum <= 12) {
108 | var d = new Date( curYear, dateMonthNum - 1, dateDay , 22, 30, 00, 0);
109 | return d;
110 | } else {
111 | if (dateMonthNum > 0 || dateMonthNum <= 3) {
112 | var d = new Date( curYear+1, dateMonthNum - 1, dateDay , 22, 30, 00, 0);
113 | return d;
114 | }
115 | }
116 | }
117 | else {
118 | Logger.log("** Warning: The earning month is: " + dateMonth +
119 | " (and we are at: " + curMonth +
120 | ") which does not make sense - so we skip it");
121 | }
122 | }
123 |
124 | }
125 | catch(err) {
126 | Logger.log("Err: Could not get the date from " + urlToFetch +
127 | " * Err: " + JSON.stringify(err));
128 | }
129 |
130 | return "N/A";
131 | }
132 |
133 |
134 | //
135 | // Helper function to get cal id/name/params
136 | //
137 | function findOurCal() {
138 | var calendar = CalendarApp.getCalendarById("TODO-email");
139 | Logger.log('** The calendar is named "%s".', calendar.getName());
140 |
141 | var calendars = CalendarApp.getCalendarsByName('Earnings Release');
142 | Logger.log('Found %s matching calendars.', calendars.length);
143 | Logger.log('Our Cal: ' + JSON.stringify(calendars[0]));
144 | Logger.log("It got Id: " + calendars[0].getId());
145 | }
146 |
147 | //
148 | // Unit test for creating a new calendar event
149 | //
150 | function testCreateEvent() {
151 | var calendar = CalendarApp.getCalendarById("TODO-email");
152 | var d = new Date( 2015, 1, 10 , 20, 30, 00, 0);
153 | createEvent(calendar, d, "Testing 123");
154 | }
155 |
156 | /**
157 | * Creates a calendar event for the Eraning call
158 | * For more information on using Calendar events, see
159 | * https://developers.google.com/apps-script/class_calendarevent.
160 | * @param {type} cal - our cal for this calls
161 | * @param {type} sDate - the start date for the event
162 | * @param {type} ticker - the ticker for the company
163 | * @returns nothing
164 | */
165 | function createEvent(cal, sDate, ticker) {
166 | var title = 'Earning call: ' + ticker;
167 | var start = sDate;
168 | var end = new Date(sDate.getTime() + (30 * 60 * 1000));
169 | var desc = 'Earning Call for ' + ticker;
170 |
171 | var event = cal.createEvent(title, start, end, {
172 | description : desc
173 | });
174 | Logger.log("Created event: " + JSON.stringify(event));
175 | };
176 |
177 | /**
178 | * Creates an event, invite guests, book rooms, and sends invitation emails.
179 | * For more information on using Calendar events, see
180 | * https://developers.google.com/apps-script/class_calendarevent.
181 | */
182 | function createEventInvitePeople() {
183 | var calId = 'your_cal_id';
184 | var room1CalId = 'a_room_cal_id';
185 | var room2CalId = 'another_room_cal_id';
186 | var guest1Email = 'guest1@yourdomain.com';
187 | var guest2Email = 'guest2@yourdomain.com';
188 | var invitees = room1CalId + ',' + room2CalId + ',' + guest1Email + ',' +
189 | guest2Email;
190 |
191 | var cal = CalendarApp.getCalendarById(calId);
192 | var title = 'Script Center Demo Event';
193 | var start = new Date("April 1, 2012 08:00:00 PDT");
194 | var end = new Date("April 1, 2012 10:00:00 PDT");
195 | var desc = 'Created using Apps Script';
196 | var loc = 'Script Center';
197 | var send = 'true';
198 |
199 | var event = cal.createEvent(title, start, end, {
200 | description : desc,
201 | location : loc,
202 | guests : invitees,
203 | sendInvites : send
204 | });
205 | };
206 |
207 | /**
208 | * Creates an event that recurs weekly for 10 weeks. These settings
209 | * are very simple; recurring events can become quite complex. Search for
210 | * 'google apps script class recurrence' to get more details.
211 | * For more information on using Calendar events, see
212 | * https://developers.google.com/apps-script/class_calendarevent.
213 | */
214 | function createEventSeries() {
215 | var calId = 'your_cal_id';
216 | var cal = CalendarApp.getCalendarById(calId);
217 | var title = 'Script Center Demo Recurring Event';
218 | var start = new Date("April 1, 2012 08:00:00 PDT");
219 | var end = new Date("April 1, 2012 10:00:00 PDT");
220 | var desc = 'Created using Apps Script';
221 | var loc = 'Script Center';
222 |
223 | var recurrence = CalendarApp.newRecurrence();
224 | recurrence.addWeeklyRule().times(10);
225 | var series = cal.createEventSeries(title, start, end, recurrence, {
226 | description : desc,
227 | location : loc
228 | });
229 | };
230 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Gmail/FetchMailsToSheet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Fetch Mails from a certain label and put them (after a little parsing)
3 | * in a nice sheet.
4 | * Later, we can work on them in the sheet or export the data (CSV, anyone?)
5 | * to work on it offline with a good/little DB.
6 | *
7 | * @Author: Ido Green | @greenido | +GreenIdo
8 | * @Date: Feb 2015
9 | */
10 |
11 |
12 |
13 | //
14 | // Run on all the emails that we have under label: investing/News/bloomberg
15 | // Parse the news item from each email and save it into the sheet.
16 | //
17 | function fetchNews() {
18 | var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("raw-news");
19 |
20 | var label = GmailApp.getUserLabelByName("TODO");
21 |
22 | // We will use this meta data sheet to keep track on what is going on with the process
23 | var metaDataS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("metaData");
24 | var curRow = ss.getLastRow() + 1;
25 | if (curRow < 2) {
26 | // Let's make sure we won't erase our headers (never).
27 | curRow = 2;
28 | }
29 | metaDataS.getRange('B3').setValue(curRow);
30 | var amountOfThreads = 10;
31 |
32 | for (var from = metaDataS.getRange('B1').getValue(); from < 1000; from = from + amountOfThreads) {
33 | metaDataS.getRange('B1').setValue(from);
34 | metaDataS.getRange('B2').setValue(from + amountOfThreads);
35 | var threads = label.getThreads(from, amountOfThreads);
36 |
37 | for (var i = 0; i < threads.length; i++) {
38 | metaDataS.getRange('B1').setValue(from + i);
39 | var message = threads[i].getMessages()[0];
40 | var body = message.getPlainBody();
41 | var subject = message.getSubject();
42 | var msgDate = message.getDate();
43 | var id = message.getId();
44 |
45 | Logger.log(i + ") msg: " + id + " On: " + msgDate); // + " text: "+body);
46 | var items = breakNewsToItems(body);
47 | Logger.log("We got: " + items);
48 | for (var k = 1; k < items.length; k++) {
49 | ss.getRange('A' + curRow).setValue(id + "-" + k);
50 | ss.getRange('B' + curRow).setValue(msgDate);
51 | ss.getRange('C' + curRow).setValue(items[k]);
52 | curRow++;
53 | metaDataS.getRange('B3').setValue(curRow);
54 | }
55 | }
56 | }
57 | //
58 | SpreadsheetApp.getActiveSpreadsheet().toast("Hey, We are done! All the news are here.", "OK", 10);
59 | }
60 |
61 | // This is how a news Item looks like:
62 | //
63 | //x) bla-bla
64 | //
65 | //
66 | // Return: array of news items
67 | function breakNewsToItems(rawText) {
68 | items = [];
69 | for (var i = 1; i < 20; i++) {
70 | var indexStr = i + ")";
71 | var inx1 = rawText.indexOf(indexStr);
72 | if (inx1 > 0) {
73 | var inx2 = rawText.indexOf("", inx1 + 15);
74 | var newsItem = rawText.substring(inx1, inx2);
75 | items[i] = newsItem;
76 | }
77 | else {
78 | break;
79 | }
80 | }
81 | //
82 | Logger.log("Found " + i + " Items");
83 | return items;
84 | }
85 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Search/lego.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Fetch lego emails from Gmail and add YT videos and images (from google search) per subject.
3 | *
4 | * @Author: Ido Green
5 | * @Date: JAN 2015
6 | *
7 | * Psst... follow the TODOs so it will work.
8 | */
9 |
10 | // Fetch 'only' the last 300 emails in that specific label.
11 | var MAX_EMAILS = 300;
12 |
13 | /**
14 | * Main menu
15 | */
16 | function onOpen() {
17 | var ui = SpreadsheetApp.getUi();
18 | ui.createMenu(' לגו חלל')
19 | .addItem('הבא מיילים', 'getLegoMsg')
20 | .addToUi();
21 | }
22 |
23 | /**
24 | * Get all the lego emails and put their subject (=url) in a cell
25 | */
26 | function getLegoMsg() {
27 | var ss = SpreadsheetApp.getActiveSheet();
28 |
29 | var label = GmailApp.getUserLabelByName("LEGO");
30 | var threads = label.getThreads(0, MAX_EMAILS);
31 | var j=2;
32 | for (var i = 0; i < threads.length; i++) {
33 | var subject = threads[i].getFirstMessageSubject();
34 | if (subject.indexOf("amazon") > 1 ) {
35 | ss.getRange('A'+j).setValue(subject);
36 | var inx1 = subject.indexOf('/', 25);
37 | var legoTitle = subject.substring(22, inx1);
38 | fetchImages(legoTitle, j, ss);
39 | fetchVideo(legoTitle, j, ss);
40 | j++;
41 | }
42 | }
43 | SpreadsheetApp.getActiveSpreadsheet().toast("Hey, We are done! All the Legos are here.", "OK :)", 10);
44 | }
45 |
46 | function fetchImages(legoTitle, curRow, ss) {
47 | var url = "https://www.googleapis.com/customsearch/v1?cx=TODO&q=" +legoTitle +
48 | "&imgSize=medium&key=TODO";
49 | var json = UrlFetchApp.fetch(url).getContentText();
50 | var data = JSON.parse(json);
51 |
52 | var img1 = data.items[0].pagemap.cse_image[0].src;
53 | Logger.log("Got from search: " + img1);
54 | var imgTag = '=image("' + img1 + '")';
55 | ss.getRange('B'+curRow).setValue(imgTag);
56 | }
57 |
58 | //
59 | //
60 | // https://gdata.youtube.com/feeds/api/videos?q=lego-star-war&v=2
61 | function fetchVideo(legoTitle, curRow, ss) {
62 | var url = "https://gdata.youtube.com/feeds/api/videos?q=" + legoTitle +"&v=2&alt=json";
63 | var json = UrlFetchApp.fetch(url).getContentText();
64 | var data = JSON.parse(json);
65 |
66 | if (data.feed.entry === undefined) {
67 | ss.getRange('C'+curRow).setValue("No video");
68 | }
69 | else {
70 | var video1 = data.feed.entry[0];
71 | var video1title = video1.title.$t;
72 | var video1url = video1.link[0].href;
73 | // update the sheet
74 | ss.getRange('C'+curRow).setValue(video1title);
75 | ss.getRange('D'+curRow).setValue(video1url);
76 | ss.getRange('E'+curRow).setValue(data.feed.entry[1].link[0].href);
77 | ss.getRange('F'+curRow).setValue(data.feed.entry[2].link[0].href);
78 | }
79 | }
80 |
81 |
82 | /**
83 | * Retrieves all inbox threads and logs the respective subject lines.
84 | * For more information on using the GMail API, see
85 | * https://developers.google.com/apps-script/class_gmailapp
86 | */
87 | function processInbox() {
88 | // get all threads in inbox
89 | var threads = GmailApp.getInboxThreads();
90 | for (var i = 0; i < threads.length; i++) {
91 | // get all messages in a given thread
92 | var messages = threads[i].getMessages();
93 | // iterate over each message
94 | for (var j = 0; j < messages.length; j++) {
95 | // log message subject
96 | Logger.log(messages[j].getSubject());
97 | }
98 | }
99 | };
100 |
101 | /**
102 | * Retrieves a given user label by name and logs the number of unread threads
103 | * associated with that that label.
104 | * For more information on interacting with GMail labels, see
105 | * https://developers.google.com/apps-script/class_gmaillabel
106 | *
107 | * @ labelName - the label we wish to work on.
108 | */
109 | function processLabel(labelName) {
110 | // get the label for given name
111 | var label = GmailApp.getUserLabelByName(labelName);
112 | // get count of all threads in the given label
113 | var threadCount = label.getUnreadCount();
114 | Logger.log(threadCount);
115 | };
116 |
117 |
118 |
--------------------------------------------------------------------------------
/public_html/AppsScript/UI/DatePicker.js:
--------------------------------------------------------------------------------
1 | function showDatesDialog() {
2 | var app = UiApp.createApplication();
3 | var panel = app.createVerticalPanel();
4 |
5 | var dateLabel1 = app.createLabel('Start Date');
6 | var dateLabel2 = app.createLabel('End Date');
7 |
8 | var dateBox1 = app.createDateBox().setId('date1');
9 | var dateBox2 = app.createDateBox().setId('date2');
10 |
11 | var button = app.createButton('Submit');
12 |
13 | var dateInfo1 = app.createLabel().setId('dateInfo1').setVisible(false);
14 | var dateInfo2 = app.createLabel().setId('dateInfo2').setVisible(false);
15 |
16 | var handler = app.createServerClickHandler('showDates');
17 | handler.addCallbackElement(panel);
18 |
19 | button.addClickHandler(handler);
20 |
21 | // Put all the UI elements on our main panel (like in the good old days of GWT)
22 | panel.add(dateLabel1)
23 | .add(dateBox1)
24 | .add(dateLabel2)
25 | .add(dateBox2)
26 | .add(button)
27 | .add(dateInfo1)
28 | .add(dateInfo2);
29 |
30 | app.add(panel);
31 |
32 | var ss = SpreadsheetApp.getActive();
33 | // show the UI
34 | ss.show(app);
35 | }
36 |
37 | //
38 | // Print the dates on the UI
39 | //
40 | function showDates(e){
41 | var app = UiApp.getActiveApplication();
42 | // Get the elements by Ids and print the dates
43 | app.getElementById('dateInfo1').setVisible(true).setText(e.parameter.date1.toString());
44 | app.getElementById('dateInfo2').setVisible(true).setText(e.parameter.date2.toString());
45 |
46 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/UI/htmlDialog.js:
--------------------------------------------------------------------------------
1 | var start = new Date().getTime();
2 | Logger.log("Going to fetch something");
3 | //
4 | // do some work
5 | //
6 | var end = new Date().getTime();
7 | var execTime = (end - start) / 1000;
8 |
9 | // Our amazing HTML5 dialog :)
10 | var htmlApp = HtmlService
11 | .createHtmlOutput('It took us: ' + execTime +
12 | 'sec to run like crazy on the beach
')
13 | .setTitle('Tracker')
14 | .setWidth(250)
15 | .setHeight(300);
16 |
17 | SpreadsheetApp.getActiveSpreadsheet().show(htmlApp);
18 |
19 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/CheckSheetAndMail.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Alert when there are new memebers that sign to the group
3 | *
4 | * @author: Ido Green | @greenido
5 | * Update: 13/5/2015
6 | *
7 | */
8 |
9 | //
10 | // Check if new member signed on our registration form and email this info on a daily basis.
11 | //
12 | function checkForNewMembers() {
13 | var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
14 | var lastRow = getFirstEmptyRow(ss);
15 |
16 | var sMsgs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("msgs");
17 | var lastTotal = sMsgs.getRange("B25").getValue();
18 |
19 | if (lastRow > lastTotal) {
20 | sMsgs.getRange("B25").setValue(lastRow);
21 | MailApp.sendEmail("TODO-fill-your-email", "New Members sign up",
22 | "Yo! We got " + (lastRow - lastTotal) + " new members. Please add them.");
23 | }
24 | else {
25 | Logger.log("It's all good. We still standing on " + lastRow + " members");
26 | }
27 | }
28 |
29 | //
30 | // Get the last empty row (quickly)
31 | //
32 | function getFirstEmptyRow(spr) {
33 | var column = spr.getRange('A:A');
34 | var values = column.getValues(); // get all data in one call
35 | var ct = 0;
36 | while ( values[ct][0] != "" ) {
37 | ct++;
38 | }
39 | return (ct);
40 | }
41 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/cleanSheetRange.js:
--------------------------------------------------------------------------------
1 | //
2 | // Clean the values from a sheet
3 | //
4 | function cleanSheet(sheetName, fRow, fCol, tRow, tCol) {
5 | var ss = SpreadsheetApp.getActiveSpreadsheet();
6 | var aSheet = ss.getSheetByName(sheetName);
7 | aSheet.getRange(fRow, fCol, tRow, tCol).clear({contentsOnly: true});
8 | Logger.log("Done cleaning " + sheetName + " on range: (" +
9 | fRow + ", " + fCol + ", " + tRow + ", " + tCol + ")");
10 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/dateUtils.js:
--------------------------------------------------------------------------------
1 | //
2 | // return the current data in this format: mm/dd/yyyy
3 | //
4 | function getDateOfToday() {
5 | var today = new Date();
6 | var dd = today.getDate();
7 | var mm = today.getMonth()+1; //January is 0!
8 |
9 | var yyyy = today.getFullYear();
10 | if(dd<10){
11 | dd='0'+dd;
12 | }
13 | if(mm<10){
14 | mm='0'+mm;
15 | }
16 | var todayStr = mm+'/'+dd+'/'+yyyy;
17 | return todayStr;
18 | }
19 |
20 |
21 | //
22 | // 0 = sunday , 1 = monday.... 6=SAT
23 | //
24 | function getDayOfWeek() {
25 | var d=new Date();
26 | Logger.log(d.getDay());
27 | return d.getDay();
28 | }
29 |
30 | //
31 | // Return true if the current day is SUN or SAT
32 | // As we are not working on weekends (=no new data).
33 | //
34 | function isWeekend() {
35 | var curDay = getDayOfWeek();
36 | if ( curDay === 0 || curDay === 6) {
37 | return true;
38 | }
39 | else {
40 | return false;
41 | }
42 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/moneyUtils.js:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // Format a number with -+,.
4 | //
5 | Number.prototype.formatMoney = function(c, d, t){
6 | var n = this,
7 | c = isNaN(c = Math.abs(c)) ? 2 : c,
8 | d = (d === undefined) ? "." : d,
9 | t = (t === undefined) ? "," : t,
10 | s = n < 0 ? "-" : "",
11 | i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "",
12 | j = (j = i.length) > 3 ? j % 3 : 0;
13 | return s + (j ? i.substr(0, j) + t : "") +
14 | i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) +
15 | (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
16 | };
17 |
18 | //
19 | // clean prices from things like: $ or ,
20 | //
21 | function cleanSymbolFromDigits(sym) {
22 | var re = /^[A-Za-z]+$/;
23 | var newSym = sym;
24 | if (!re.test(sym)) {
25 | newSym = sym.substring(0, sym.length-1);
26 | }
27 | return newSym;
28 | }
29 |
30 | //
31 | // Do we have a valid number
32 | //
33 | function isNumber(n) {
34 | return !isNaN(parseFloat(n)) && isFinite(n);
35 | }
36 |
37 | //
38 | // Check if we have a float
39 | //
40 | function isFloat(n) {
41 | return n === +n && n !== (n|0);
42 | }
43 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/parseHTML.js:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this license header, choose License Headers in Project Properties.
3 | * To change this template file, choose Tools | Templates
4 | * and open the template in the editor.
5 | */
6 |
7 | function doGet() {
8 | var html = UrlFetchApp.fetch('http://en.wikipedia.org/wiki/Document_Object_Model').getContentText();
9 | var doc = XmlService.parse(html);
10 | var html = doc.getRootElement();
11 | var menu = getElementsByClassName(html, 'vertical-navbox nowraplinks')[0];
12 | var output = XmlService.getRawFormat().format(menu);
13 | return HtmlService.createHtmlOutput(output);
14 | }
15 |
16 | //We fetch the HTML through UrlFetch
17 | //We use the XMLService to parse this HTML
18 | //Then we can use a specific function to grab the element we want in the DOM tree (like getElementsByClassName)
19 | //And we convert back this element to HTML
20 | //Or we could get all the links / anchors available in this menu and display them
21 | function doGet() {
22 | var html = UrlFetchApp.fetch('http://en.wikipedia.org/wiki/Document_Object_Model').getContentText();
23 | var doc = XmlService.parse(html);
24 | var html = doc.getRootElement();
25 | var menu = getElementsByClassName(html, 'vertical-navbox nowraplinks')[0];
26 | var output = '';
27 | var linksInMenu = getElementsByTagName(menu, 'a');
28 | for (i in linksInMenu)
29 | output += XmlService.getRawFormat().format(linksInMenu[i]) + '
';
30 | return HtmlService.createHtmlOutput(output);
31 | }
32 |
33 | //Available functions
34 | //Contents
35 | //1 getElementById
36 | //2 getElementsByClassName
37 | //3 getElementsByTagName
38 |
39 | //getElementById
40 | function getElementById(element, idToFind) {
41 | var descendants = element.getDescendants();
42 | for (i in descendants) {
43 | var elt = descendants[i].asElement();
44 | if (elt != null) {
45 | var id = elt.getAttribute('id');
46 | if (id != null && id.getValue() == idToFind)
47 | return elt;
48 | }
49 | }
50 | }
51 |
52 | //getElementsByClassName
53 | function getElementsByClassName(element, classToFind) {
54 | var data = [];
55 | var descendants = element.getDescendants();
56 | descendants.push(element);
57 | for (i in descendants) {
58 | var elt = descendants[i].asElement();
59 | if (elt !== null) {
60 | var classes = elt.getAttribute('class');
61 | if (classes !== null) {
62 | classes = classes.getValue();
63 | if (classes === classToFind)
64 | data.push(elt);
65 | else {
66 | classes = classes.split(' ');
67 | for (j in classes) {
68 | if (classes[j] === classToFind) {
69 | data.push(elt);
70 | break;
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 | return data;
78 | }
79 | //getElementsByTagName
80 | function getElementsByTagName(element, tagName) {
81 | var data = [];
82 | var descendants = element.getDescendants();
83 | for (i in descendants) {
84 | var elt = descendants[i].asElement();
85 | if (elt !== null && elt.getName() === tagName)
86 | data.push(elt);
87 | }
88 | return data;
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/public_html/AppsScript/Utils/runMeAgain.js:
--------------------------------------------------------------------------------
1 | /**
2 | * When you wish to run a task more then once.
3 | * These 2 helper function will help you set another task (or more) to 'finish' the job.
4 | * @author Ido Green | @greenido
5 | *
6 | */
7 |
8 | //
9 | // We will call this function to update
10 | // (!) Make sure to replace the TODO
11 | //
12 | function updateXX() {
13 | // Do some work here
14 | Logger.log("** Going to work hard & long **");
15 |
16 | // run me again in 7min = 7*60*1000
17 | var tmpTrigger = runMeAgainInXmin(420000, "TODO-your-function-name-here");
18 | Logger.log("our trigger for updateXX: " + tmpTrigger.getUniqueId());
19 |
20 | // Save our triggerId so we can remove it after the execution.
21 | // It's also helpful to have all our meta data in a sheet to debug issues.
22 | SpreadsheetApp.getActiveSpreadsheet().getSheetByName("metaData").getRange(1,1).setValue(tmpTrigger.getUniqueId());
23 | }
24 |
25 |
26 | //
27 | // Run me in X milliseconds into the future
28 | //
29 | function runMeAgainInXmin(waitForMin, functionToRun) {
30 | Logger.log("** Going to set a new trigger for: " + functionToRun +
31 | " to run in: " + waitForMin);
32 | var oneTimeOnly = ScriptApp.newTrigger(functionToRun).timeBased()
33 | .after(waitForMin).create();
34 | return oneTimeOnly;
35 | }
--------------------------------------------------------------------------------
/public_html/AppsScript/YouTube/ytStats.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Ido's YouTube Dashboard Stats example
3 | * Fetch stats on videos and channel by using YT APIs.
4 | 1. It using the ATOM feeds - v1.
5 | 2. For the channel we are using the v3 version of the API.
6 |
7 | * @Author: Ido Green
8 | * @Date: Aug 2014
9 | *
10 | */
11 |
12 |
13 | /**
14 | * This function uses the YouTube Analytics API to fetch data about the
15 | * authenticated user's channel, creating a new Google Sheet in the user's Drive
16 | * with the data.
17 | *
18 | * The first part of this sample demonstrates a simple YouTube Analytics API
19 | * call. This function first fetches the active user's channel ID. Using that
20 | * ID, the function makes a YouTube Analytics API call to retrieve views,
21 | * likes, dislikes and shares for the last 30 days. The API returns the data
22 | * in a response object that contains a 2D array.
23 | *
24 | * The second part of the sample constructs a Spreadsheet. This spreadsheet
25 | * is placed in the authenticated user's Google Drive with the name
26 | * 'YouTube Report' and date range in the title. The function populates the
27 | * spreadsheet with the API response, then locks columns and rows that will
28 | * define a chart axes. A stacked column chart is added for the spreadsheet.
29 |
30 | @see:
31 | * https://developers.google.com/youtube/analytics/v1/code_samples/apps-script
32 | * https://developers.google.com/youtube/analytics/sample-requests#channel-time-based-reports
33 | * https://developers.google.com/apis-explorer/#p/youtubeAnalytics/v1/youtubeAnalytics.reports.query
34 | */
35 | function spreadsheetAnalytics() {
36 | // Get the channel ID
37 | var myChannels = YouTube.Channels.list('id', {mine: true});
38 | var channel = myChannels.items[0];
39 | var channelId = channel.id;
40 | // Set the dates for our report
41 | var today = new Date();
42 | var monthAgo12 = new Date();
43 | monthAgo12.setMonth(today.getMonth() - 11);
44 | var todayFormatted = Utilities.formatDate(today, 'UTC', 'yyyy-MM-dd')
45 | var oneMonthAgoFormatted = Utilities.formatDate(monthAgo12, 'UTC', 'yyyy-MM-dd');
46 |
47 | // The YouTubeAnalytics.Reports.query() function has four required parameters and one optional
48 | // parameter. The first parameter identifies the channel or content owner for which you are
49 | // retrieving data. The second and third parameters specify the start and end dates for the
50 | // report, respectively. The fourth parameter identifies the metrics that you are retrieving.
51 | // The fifth parameter is an object that contains any additional optional parameters
52 | // (dimensions, filters, sort, etc.) that you want to set.
53 | var analyticsResponse = YouTubeAnalytics.Reports.query(
54 | 'channel==' + channelId,
55 | oneMonthAgoFormatted,
56 | todayFormatted,
57 | // dimensions=day metrics=views,estimatedMinutesWatched,averageViewDuration,averageViewPercentage,subscribersGained
58 | 'views,estimatedMinutesWatched,averageViewDuration,averageViewPercentage,likes,dislikes,shares',
59 | {
60 | dimensions: 'day',
61 | sort: '-day'
62 | });
63 |
64 | // Create a new Spreadsheet with rows and columns corresponding to our dates
65 | var ssName = 'YouTube channel report ' + oneMonthAgoFormatted + ' - ' + todayFormatted;
66 | var numRows = analyticsResponse.rows.length;
67 | var numCols = analyticsResponse.columnHeaders.length;
68 |
69 | // Add an extra row for column headers
70 | var ssNew = SpreadsheetApp.create(ssName, numRows + 1, numCols);
71 |
72 | // Get the first sheet
73 | var sheet = ssNew.getSheets()[0];
74 |
75 | // Get the range for the title columns
76 | // Remember, spreadsheets are 1-indexed, whereas arrays are 0-indexed
77 | var headersRange = sheet.getRange(1, 1, 1, numCols);
78 | var headers = [];
79 |
80 | // These column headers will correspond with the metrics requested
81 | // in the initial call: views, likes, dislikes, shares
82 | for(var i in analyticsResponse.columnHeaders) {
83 | var columnHeader = analyticsResponse.columnHeaders[i];
84 | var columnName = columnHeader.name;
85 | headers[i] = columnName;
86 | }
87 | // This takes a 2 dimensional array
88 | headersRange.setValues([headers]);
89 |
90 | // Bold and freeze the column names
91 | headersRange.setFontWeight('bold');
92 | sheet.setFrozenRows(1);
93 |
94 | // Get the data range and set the values
95 | var dataRange = sheet.getRange(2, 1, numRows, numCols);
96 | dataRange.setValues(analyticsResponse.rows);
97 |
98 |
99 | // Bold and freeze the dates
100 | var dateHeaders = sheet.getRange(1, 1, numRows, 1);
101 | dateHeaders.setFontWeight('bold');
102 | sheet.setFrozenColumns(1);
103 |
104 | // Include the headers in our range. The headers are used
105 | // to label the axes
106 | var range = sheet.getRange(1, 1, numRows, numCols);
107 | var chart = sheet.newChart()
108 | .asColumnChart()
109 | .setStacked()
110 | .addRange(range)
111 | .setPosition(4, 2, 10, 10)
112 | .build();
113 | sheet.insertChart(chart);
114 | }
115 |
116 | //
117 | // A Helper function to extract the ID of our video
118 | // It works both on version of links:
119 | // 1. https://www.youtube.com/watch?v=BuHEhmp47VE
120 | // 2. http://youtu.be/75EuHl6CSTo
121 | //
122 | function extractVideoID() {
123 | var curSheet = SpreadsheetApp.getActiveSheet();
124 | var ytLinks = curSheet.getRange("D:D");
125 | var totalRows = ytLinks.getNumRows();
126 | var ytVal = ytLinks.getValues();
127 | // let's run on the rows
128 | for (var i = 1; i <= totalRows - 1; i++) {
129 | var curLink = ytVal[i][0];
130 | if (curLink == "") {
131 | break;
132 | }
133 |
134 | var videoID = "";
135 | var inx1 = curLink.indexOf('watch?v=') + 8;
136 | if (inx1 == 7) {
137 | // check if it's the short format: http://youtu.be/75EuHl6CSTo
138 | if (curLink != "" && curLink.indexOf("youtu.be") > 0) {
139 | videoID = curLink.substr(16, curLink.length);
140 | }
141 | }
142 | else {
143 | // we have the link in this format: https://www.youtube.com/watch?v=YIgSucMNFAo
144 | var inx2 = curLink.indexOf("&", inx1);
145 |
146 | if (inx2 > inx1) {
147 | videoID = curLink.substr(inx1, inx2-inx1);
148 | } else {
149 | videoID = curLink.substr(inx1, curLink.length);
150 | }
151 | }
152 |
153 | curSheet.getRange("E" + (i+1)).setValue(videoID);
154 | }
155 | var htmlMsg = HtmlService
156 | .createHtmlOutput('Done - Please check the IDs on Column D:D
').setTitle('YT Dashboard Example').setWidth(450).setHeight(300);
157 | SpreadsheetApp.getActiveSpreadsheet().show(htmlMsg);
158 | }
159 |
160 | //
161 | // Run on all the rows and according to the video ID fetch the feed
162 | //
163 | function fetchAllData() {
164 | var start = new Date().getTime();
165 |
166 | var curSheet = SpreadsheetApp.getActiveSheet();
167 | var ytIds = curSheet.getRange("E:E");
168 | var totalRows = ytIds.getNumRows();
169 | var ytVal = ytIds.getValues();
170 | var errMsg = "Errors:
";
171 | // let's run on the rows after the header row
172 | for (var i = 1; i <= totalRows - 1; i++) {
173 | // e.g. for a call: https://gdata.youtube.com/feeds/api/videos/YIgSucMNFAo?v=2&prettyprint=true
174 | if (ytVal[i] === "") {
175 | Logger.log("We stopped at row: " + (i+1));
176 | break;
177 | }
178 | var link = "https://gdata.youtube.com/feeds/api/videos/" + ytVal[i] + "?v=2&prettyprint=true";
179 | try {
180 | fetchYTdata(link, i+1);
181 | }
182 | catch (err) {
183 | errMsg += "- Line: " + i + " we could not fetch data for ID: " + ytVal[i] + "
";
184 | Logger.log("*** ERR: We have issue with " + ytVal[i] + " On line: " + i);
185 | }
186 | }
187 |
188 | var end = new Date().getTime();
189 | var execTime = (end - start) / 1000;
190 | var htmlApp = HtmlService
191 | .createHtmlOutput('Done updating!
It took us: '+ execTime + 'sec. to update: ' +
192 | (i+1) + ' videos
' + errMsg).setTitle('GStudio Rock').setWidth(450).setHeight(450);
193 | SpreadsheetApp.getActiveSpreadsheet().show(htmlApp);
194 | }
195 |
196 |
197 | //
198 | // Read YT stats data on our videos and fill the sheet with the data
199 | //
200 | function fetchYTdata(url, curRow) {
201 | //var url = 'https://gdata.youtube.com/feeds/api/videos/Eb7rzMxHyOk?v=2&prettyprint=true';
202 | var rawData = UrlFetchApp.fetch(url).getContentText();
203 | //Logger.log(rawData);
204 |
205 | // published 2014-05-09T06:22:52.000Z
206 | var inx1 = rawData.indexOf('published>') + 10;
207 | var inx2 = rawData.indexOf("T", inx1);
208 | var publishedDate = rawData.substr(inx1, inx2-inx1);
209 |
210 | // viewCount='16592'
211 | var inx1 = rawData.indexOf('viewCount') + 11;
212 | var inx2 = rawData.indexOf("'/>", inx1);
213 | var totalViews = rawData.substr(inx1, inx2-inx1);
214 |
215 | //
216 | var inx1 = rawData.indexOf('duration seconds') + 18;
217 | var inx2 = rawData.indexOf("'/>", inx1);
218 | var durationSec = rawData.substr(inx1, inx2-inx1);
219 |
220 | Logger.log(curRow + ") TotalViews: " + totalViews + " durationSec: " + durationSec);
221 |
222 | // update the sheet
223 | var ss = SpreadsheetApp.getActiveSheet();
224 | ss.getRange("C" + curRow).setValue(publishedDate);
225 | ss.getRange("G" + curRow).setValue(totalViews);
226 | ss.getRange("H" + curRow).setValue(durationSec);
227 |
228 | }
229 |
230 | //
231 | // Our custom menu
232 | //
233 | function onOpen() {
234 | var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
235 | var entries = [{ name : "Update Stats", functionName : "fetchAllData"},
236 | { name : "Extract Video IDs", functionName : "extractVideoID"}
237 | ];
238 | spreadsheet.addMenu("YT Dashboard", entries);
239 | };
240 |
241 |
242 | //////////////////////////////////////////////////////////////////////////////////////////
243 | ///////// Version 2.0 with YT Analytics --> should move it to another file ///////////////
244 | //////////////////////////////////////////////////////////////////////////////////////////
245 |
246 | /**
247 | * GStudio Stats
248 | * Fetch stats on our videos by using YT ATOM feeds.
249 | *
250 | * @Author: Ido Green
251 | * @Date: July 2014
252 | *
253 | * @see: https://github.com/greenido/AppsScriptBests
254 | * https://developers.google.com/youtube/analytics/v1/code_samples/apps-script
255 | * https://developers.google.com/youtube/analytics/sample-requests#channel-time-based-reports
256 | * https://developers.google.com/apis-explorer/#p/youtubeAnalytics/v1/youtubeAnalytics.reports.query
257 |
258 | * Older doc on working with YT API:
259 | * https://docs.google.com/a/google.com/document/d/1Htgm9LieOWe-DHxAWzpKxc5qfjNZm79jGSyy9BXl1Xc/edit
260 | */
261 |
262 |
263 |
264 | function testGetVideoEstimatedMinutesWatched() {
265 | var coltPerf = getVideoEstimatedMinutesWatched('zVK6TKSx1lU');
266 | Logger.log("colt perf video: \n" + coltPerf);
267 | }
268 |
269 | //
270 | // Fetch the estimated Minutes Watched on specific video
271 | // ToDo:
272 | // gain more metrics for example: views,estimatedMinutesWatched,averageViewDuration,likes,dislikes,shares
273 | function getVideoEstimatedMinutesWatched(videoId) {
274 | var myChannels = YouTube.Channels.list('id', {mine: true});
275 | var channel = myChannels.items[0];
276 | var channelId = channel.id;
277 |
278 | if (channelId) {
279 | var today = new Date();
280 | var monthAgo12 = new Date();
281 | monthAgo12.setMonth(today.getMonth() - 11);
282 | var todayFormatted = Utilities.formatDate(today, 'UTC', 'yyyy-MM-dd')
283 | var MonthAgo12Formatted = Utilities.formatDate(monthAgo12, 'UTC', 'yyyy-MM-dd');
284 |
285 | var analyticsResponse = YouTubeAnalytics.Reports.query(
286 | 'channel==' + channelId,
287 | MonthAgo12Formatted,
288 | todayFormatted,
289 | 'views,estimatedMinutesWatched',
290 | {
291 | dimensions: 'video',
292 | filters: 'video==' + videoId
293 | });
294 |
295 | //Logger.log("analytics for " + videoId + ": \n " +analyticsResponse.rows);
296 | return analyticsResponse.rows[0];
297 | }
298 | else {
299 | return "N/A";
300 | }
301 | }
302 |
303 | //
304 | // We will use this function inorder to extract the IDs of the video out of YT links.
305 | // It's working with both long/short version of youtube.
306 | //
307 | // The function works on column J as the source and column K as the output.
308 | //
309 | function extractVideoID() {
310 | var curSheet = SpreadsheetApp.getActiveSheet();
311 | var ytLinks = curSheet.getRange("J:J");
312 | var totalRows = ytLinks.getNumRows();
313 | var ytVal = ytLinks.getValues();
314 | // let's run on the rows
315 | for (var i = 1; i <= totalRows - 1; i++) {
316 | var curLink = ytVal[i][0];
317 | var videoID = "";
318 | var inx1 = curLink.indexOf('watch?v=') + 8;
319 | if (inx1 == 7) {
320 | // check if it's the short format: http://youtu.be/75EuHl6CSTo
321 | if (curLink != "" && curLink.indexOf("youtu.be") > 0) {
322 | videoID = curLink.substr(16, curLink.length);
323 | }
324 | }
325 | else {
326 | // we have the link in this format: https://www.youtube.com/watch?v=BuHEhmp47VE
327 | var inx2 = curLink.indexOf("&", inx1);
328 |
329 | if (inx2 > inx1) {
330 | videoID = curLink.substr(inx1, inx2-inx1);
331 | } else {
332 | videoID = curLink.substr(inx1, curLink.length);
333 | }
334 | }
335 |
336 | curSheet.getRange("K" + (i+1)).setValue(videoID);
337 | }
338 | var htmlMsg = HtmlService
339 | .createHtmlOutput('Done - Please check the IDs on Column K:K
').setTitle('GStudio').setWidth(450).setHeight(300);
340 | SpreadsheetApp.getActiveSpreadsheet().show(htmlMsg);
341 | }
342 |
343 | //
344 | // A function we will use to run on a daily bases to refresh the stats
345 | //
346 | function fetchDataQ1Q2Q3() {
347 |
348 | var q3 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q3 - 2014");
349 | Logger.log("== Working on " + q3.getName());
350 | fetchAllData(q3);
351 |
352 | var q2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q2 - 2014");
353 | Logger.log("== Working on " + q2.getName());
354 | fetchAllData(q2);
355 |
356 | var q1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q1 - 2014");
357 | Logger.log("== Working on " + q1.getName());
358 | fetchAllData(q1);
359 | }
360 |
361 | //
362 | // CronJob helper function to by pass the 5min limit that: fetchDataQ1Q2Q3() got us into.
363 | //
364 | function fetchDataCronJobQ1_2014() {
365 | var q = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q1 - 2014");
366 | Logger.log("== Working on " + q.getName());
367 | fetchAllData(q);
368 | }
369 | function fetchDataCronJobQ2_2014() {
370 | var q = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q2 - 2014");
371 | Logger.log("== Working on " + q.getName());
372 | fetchAllData(q);
373 | }
374 | function fetchDataCronJobQ3_2014() {
375 | var q = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Q3 - 2014");
376 | Logger.log("== Working on " + q.getName());
377 | fetchAllData(q);
378 | }
379 | //
380 | // Run on all the rows and according to the video IDs fetch the feed of
381 | // YT API v1 and extract from it:
382 | // 1. Date published.
383 | // 2. Views.
384 | // 3. Duration in seconds.
385 | //
386 | function fetchAllData(cSheet) {
387 | var start = new Date().getTime();
388 |
389 | var curSheet = cSheet;
390 | var doWeHaveUser = false;
391 | if (curSheet == null || curSheet == undefined) {
392 | curSheet = SpreadsheetApp.getActiveSheet();
393 | doWeHaveUser = true;
394 | }
395 | var ytIds = curSheet.getRange("K:K");
396 | var totalRows = ytIds.getNumRows();
397 | var ytVal = ytIds.getValues();
398 | var errMsg = "Errors on " + curSheet.getName() + " :
";
399 | // let's run on the rows after the header row
400 | for (var i = 1; i <= totalRows - 1; i++) {
401 | // e.g. for a call: https://gdata.youtube.com/feeds/api/videos/Eb7rzMxHyOk?v=2&prettyprint=true
402 | if (ytVal[i] == "") {
403 | Logger.log("We stopped at row: " + (i+1));
404 | break;
405 | }
406 | var link = "https://gdata.youtube.com/feeds/api/videos/" + ytVal[i] + "?v=2&prettyprint=true";
407 | try {
408 | fetchYTdata(link, i+1, curSheet);
409 | }
410 | catch (err) {
411 | errMsg += "- Line: " + i + " we could not fetch data for ID: " + ytVal[i] + "
";
412 | Logger.log("*** ERR: We have issue with " + ytVal[i] + " On line: " + i + " with err: " + JSON.stringify(err));
413 | }
414 | }
415 |
416 | var end = new Date().getTime();
417 | var execTime = (end - start) / 1000;
418 | if (doWeHaveUser) {
419 | var htmlApp = HtmlService.createHtmlOutput('Done updating!
It took us: '+ execTime + 'sec. to update: ' +
420 | (i+1) + ' videos
' + errMsg).setTitle('GStudio Rock').setWidth(450).setHeight(450);
421 | SpreadsheetApp.getActiveSpreadsheet().show(htmlApp);
422 | }
423 |
424 | // Keeping us updated
425 | MailApp.sendEmail("btollefson@google.com", "GDStudio Tracking Daily Updater Status",
426 | "We've just updated all the stats on GDStudio Tracking (go/gdstudio-tracking). " + errMsg );
427 | }
428 |
429 |
430 | //
431 | // Read YT stats data on our videos and fill the sheet with the data:
432 | // 1. Date published - Column I
433 | // 2. Views - Column Q
434 | // 3. Duration in seconds - Column R
435 | //
436 | function fetchYTdata(url, curRow, ss) {
437 | // fetch the estimated min watched
438 | var videoId = ss.getRange("K" + curRow).getValue();
439 | var stats = getVideoEstimatedMinutesWatched(videoId);
440 | var totalViews = stats[1];
441 | var estimatedMin = stats[2];
442 | ss.getRange("Q" + curRow).setValue(totalViews);
443 | ss.getRange("V" + curRow).setValue(estimatedMin);
444 |
445 | Logger.log(curRow + ") TotalViews: " + totalViews + " estimatedMin: " + estimatedMin);
446 |
447 | // OLD CODE just for length of video and publish date:
448 | //var url = 'https://gdata.youtube.com/feeds/api/videos/Eb7rzMxHyOk?v=2&prettyprint=true';
449 | var rawData = UrlFetchApp.fetch(url).getContentText();
450 | //Logger.log(rawData);
451 |
452 | // published 2014-05-09T06:22:52.000Z
453 | var inx1 = rawData.indexOf('published>') + 10;
454 | var inx2 = rawData.indexOf("T", inx1);
455 | var publishedDate = rawData.substr(inx1, inx2-inx1);
456 |
457 | // viewCount='16592'
458 | // var inx1 = rawData.indexOf('viewCount') + 11;
459 | // var inx2 = rawData.indexOf("'/>", inx1);
460 | // var totalViews = rawData.substr(inx1, inx2-inx1);
461 |
462 | //
463 | var inx1 = rawData.indexOf('duration seconds') + 18;
464 | var inx2 = rawData.indexOf("'/>", inx1);
465 | var durationSec = rawData.substr(inx1, inx2-inx1);
466 |
467 | Logger.log(curRow + ") durationSec: " + durationSec);
468 |
469 | // update the sheet
470 | ss.getRange("I" + curRow).setValue(publishedDate);
471 | //ss.getRange("Q" + curRow).setValue(totalViews);
472 | ss.getRange("R" + curRow).setValue(durationSec);
473 |
474 |
475 |
476 | }
477 |
478 |
479 |
--------------------------------------------------------------------------------
/public_html/ListOfScripts.md:
--------------------------------------------------------------------------------
1 | * [Apps](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps)
2 | * [Apps/EventManager.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/EventManager.js)
3 | * [Apps/EventsDateManager.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/EventsDateManager.js)
4 | * [Apps/Insta2Drive.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/Insta2Drive.js)
5 | * [Apps/SiteMonitor.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Apps/SiteMonitor.js)
6 | * [Gcal](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Gcal)
7 | * [Gcal/EarningCalls.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Gcal/EarningCalls.js)
8 | * [Search](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Search)
9 | * [Search/lego.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Search/lego.js)
10 | * [UI](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI)
11 | * [UI/DatePicker.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI/DatePicker.js)
12 | * [UI/htmlDialog.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/UI/htmlDialog.js)
13 | * [Utils](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils)
14 | * [Utils/cleanSheetRange.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/cleanSheetRange.js)
15 | * [Utils/dateUtils.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/dateUtils.js)
16 | * [Utils/moneyUtils.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/moneyUtils.js)
17 | * [Utils/parseHTML.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/parseHTML.js)
18 | * [Utils/runMeAgain.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/Utils/runMeAgain.js)
19 | * [YouTube](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/YouTube)
20 | * [YouTube/ytStats.js](https://github.com/greenido/AppsScriptBests/tree/master/public_html/AppsScript/YouTube/ytStats.js)
21 |
--------------------------------------------------------------------------------
/public_html/css/bootstrap-responsive.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.2
3 | *
4 | * Copyright 2013 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world by @mdo and @fat.
9 | */
10 |
11 | .clearfix {
12 | *zoom: 1;
13 | }
14 |
15 | .clearfix:before,
16 | .clearfix:after {
17 | display: table;
18 | line-height: 0;
19 | content: "";
20 | }
21 |
22 | .clearfix:after {
23 | clear: both;
24 | }
25 |
26 | .hide-text {
27 | font: 0/0 a;
28 | color: transparent;
29 | text-shadow: none;
30 | background-color: transparent;
31 | border: 0;
32 | }
33 |
34 | .input-block-level {
35 | display: block;
36 | width: 100%;
37 | min-height: 30px;
38 | -webkit-box-sizing: border-box;
39 | -moz-box-sizing: border-box;
40 | box-sizing: border-box;
41 | }
42 |
43 | @-ms-viewport {
44 | width: device-width;
45 | }
46 |
47 | .hidden {
48 | display: none;
49 | visibility: hidden;
50 | }
51 |
52 | .visible-phone {
53 | display: none !important;
54 | }
55 |
56 | .visible-tablet {
57 | display: none !important;
58 | }
59 |
60 | .hidden-desktop {
61 | display: none !important;
62 | }
63 |
64 | .visible-desktop {
65 | display: inherit !important;
66 | }
67 |
68 | @media (min-width: 768px) and (max-width: 979px) {
69 | .hidden-desktop {
70 | display: inherit !important;
71 | }
72 | .visible-desktop {
73 | display: none !important ;
74 | }
75 | .visible-tablet {
76 | display: inherit !important;
77 | }
78 | .hidden-tablet {
79 | display: none !important;
80 | }
81 | }
82 |
83 | @media (max-width: 767px) {
84 | .hidden-desktop {
85 | display: inherit !important;
86 | }
87 | .visible-desktop {
88 | display: none !important;
89 | }
90 | .visible-phone {
91 | display: inherit !important;
92 | }
93 | .hidden-phone {
94 | display: none !important;
95 | }
96 | }
97 |
98 | .visible-print {
99 | display: none !important;
100 | }
101 |
102 | @media print {
103 | .visible-print {
104 | display: inherit !important;
105 | }
106 | .hidden-print {
107 | display: none !important;
108 | }
109 | }
110 |
111 | @media (min-width: 1200px) {
112 | .row {
113 | margin-left: -30px;
114 | *zoom: 1;
115 | }
116 | .row:before,
117 | .row:after {
118 | display: table;
119 | line-height: 0;
120 | content: "";
121 | }
122 | .row:after {
123 | clear: both;
124 | }
125 | [class*="span"] {
126 | float: left;
127 | min-height: 1px;
128 | margin-left: 30px;
129 | }
130 | .container,
131 | .navbar-static-top .container,
132 | .navbar-fixed-top .container,
133 | .navbar-fixed-bottom .container {
134 | width: 1170px;
135 | }
136 | .span12 {
137 | width: 1170px;
138 | }
139 | .span11 {
140 | width: 1070px;
141 | }
142 | .span10 {
143 | width: 970px;
144 | }
145 | .span9 {
146 | width: 870px;
147 | }
148 | .span8 {
149 | width: 770px;
150 | }
151 | .span7 {
152 | width: 670px;
153 | }
154 | .span6 {
155 | width: 570px;
156 | }
157 | .span5 {
158 | width: 470px;
159 | }
160 | .span4 {
161 | width: 370px;
162 | }
163 | .span3 {
164 | width: 270px;
165 | }
166 | .span2 {
167 | width: 170px;
168 | }
169 | .span1 {
170 | width: 70px;
171 | }
172 | .offset12 {
173 | margin-left: 1230px;
174 | }
175 | .offset11 {
176 | margin-left: 1130px;
177 | }
178 | .offset10 {
179 | margin-left: 1030px;
180 | }
181 | .offset9 {
182 | margin-left: 930px;
183 | }
184 | .offset8 {
185 | margin-left: 830px;
186 | }
187 | .offset7 {
188 | margin-left: 730px;
189 | }
190 | .offset6 {
191 | margin-left: 630px;
192 | }
193 | .offset5 {
194 | margin-left: 530px;
195 | }
196 | .offset4 {
197 | margin-left: 430px;
198 | }
199 | .offset3 {
200 | margin-left: 330px;
201 | }
202 | .offset2 {
203 | margin-left: 230px;
204 | }
205 | .offset1 {
206 | margin-left: 130px;
207 | }
208 | .row-fluid {
209 | width: 100%;
210 | *zoom: 1;
211 | }
212 | .row-fluid:before,
213 | .row-fluid:after {
214 | display: table;
215 | line-height: 0;
216 | content: "";
217 | }
218 | .row-fluid:after {
219 | clear: both;
220 | }
221 | .row-fluid [class*="span"] {
222 | display: block;
223 | float: left;
224 | width: 100%;
225 | min-height: 30px;
226 | margin-left: 2.564102564102564%;
227 | *margin-left: 2.5109110747408616%;
228 | -webkit-box-sizing: border-box;
229 | -moz-box-sizing: border-box;
230 | box-sizing: border-box;
231 | }
232 | .row-fluid [class*="span"]:first-child {
233 | margin-left: 0;
234 | }
235 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
236 | margin-left: 2.564102564102564%;
237 | }
238 | .row-fluid .span12 {
239 | width: 100%;
240 | *width: 99.94680851063829%;
241 | }
242 | .row-fluid .span11 {
243 | width: 91.45299145299145%;
244 | *width: 91.39979996362975%;
245 | }
246 | .row-fluid .span10 {
247 | width: 82.90598290598291%;
248 | *width: 82.8527914166212%;
249 | }
250 | .row-fluid .span9 {
251 | width: 74.35897435897436%;
252 | *width: 74.30578286961266%;
253 | }
254 | .row-fluid .span8 {
255 | width: 65.81196581196582%;
256 | *width: 65.75877432260411%;
257 | }
258 | .row-fluid .span7 {
259 | width: 57.26495726495726%;
260 | *width: 57.21176577559556%;
261 | }
262 | .row-fluid .span6 {
263 | width: 48.717948717948715%;
264 | *width: 48.664757228587014%;
265 | }
266 | .row-fluid .span5 {
267 | width: 40.17094017094017%;
268 | *width: 40.11774868157847%;
269 | }
270 | .row-fluid .span4 {
271 | width: 31.623931623931625%;
272 | *width: 31.570740134569924%;
273 | }
274 | .row-fluid .span3 {
275 | width: 23.076923076923077%;
276 | *width: 23.023731587561375%;
277 | }
278 | .row-fluid .span2 {
279 | width: 14.52991452991453%;
280 | *width: 14.476723040552828%;
281 | }
282 | .row-fluid .span1 {
283 | width: 5.982905982905983%;
284 | *width: 5.929714493544281%;
285 | }
286 | .row-fluid .offset12 {
287 | margin-left: 105.12820512820512%;
288 | *margin-left: 105.02182214948171%;
289 | }
290 | .row-fluid .offset12:first-child {
291 | margin-left: 102.56410256410257%;
292 | *margin-left: 102.45771958537915%;
293 | }
294 | .row-fluid .offset11 {
295 | margin-left: 96.58119658119658%;
296 | *margin-left: 96.47481360247316%;
297 | }
298 | .row-fluid .offset11:first-child {
299 | margin-left: 94.01709401709402%;
300 | *margin-left: 93.91071103837061%;
301 | }
302 | .row-fluid .offset10 {
303 | margin-left: 88.03418803418803%;
304 | *margin-left: 87.92780505546462%;
305 | }
306 | .row-fluid .offset10:first-child {
307 | margin-left: 85.47008547008548%;
308 | *margin-left: 85.36370249136206%;
309 | }
310 | .row-fluid .offset9 {
311 | margin-left: 79.48717948717949%;
312 | *margin-left: 79.38079650845607%;
313 | }
314 | .row-fluid .offset9:first-child {
315 | margin-left: 76.92307692307693%;
316 | *margin-left: 76.81669394435352%;
317 | }
318 | .row-fluid .offset8 {
319 | margin-left: 70.94017094017094%;
320 | *margin-left: 70.83378796144753%;
321 | }
322 | .row-fluid .offset8:first-child {
323 | margin-left: 68.37606837606839%;
324 | *margin-left: 68.26968539734497%;
325 | }
326 | .row-fluid .offset7 {
327 | margin-left: 62.393162393162385%;
328 | *margin-left: 62.28677941443899%;
329 | }
330 | .row-fluid .offset7:first-child {
331 | margin-left: 59.82905982905982%;
332 | *margin-left: 59.72267685033642%;
333 | }
334 | .row-fluid .offset6 {
335 | margin-left: 53.84615384615384%;
336 | *margin-left: 53.739770867430444%;
337 | }
338 | .row-fluid .offset6:first-child {
339 | margin-left: 51.28205128205128%;
340 | *margin-left: 51.175668303327875%;
341 | }
342 | .row-fluid .offset5 {
343 | margin-left: 45.299145299145295%;
344 | *margin-left: 45.1927623204219%;
345 | }
346 | .row-fluid .offset5:first-child {
347 | margin-left: 42.73504273504273%;
348 | *margin-left: 42.62865975631933%;
349 | }
350 | .row-fluid .offset4 {
351 | margin-left: 36.75213675213675%;
352 | *margin-left: 36.645753773413354%;
353 | }
354 | .row-fluid .offset4:first-child {
355 | margin-left: 34.18803418803419%;
356 | *margin-left: 34.081651209310785%;
357 | }
358 | .row-fluid .offset3 {
359 | margin-left: 28.205128205128204%;
360 | *margin-left: 28.0987452264048%;
361 | }
362 | .row-fluid .offset3:first-child {
363 | margin-left: 25.641025641025642%;
364 | *margin-left: 25.53464266230224%;
365 | }
366 | .row-fluid .offset2 {
367 | margin-left: 19.65811965811966%;
368 | *margin-left: 19.551736679396257%;
369 | }
370 | .row-fluid .offset2:first-child {
371 | margin-left: 17.094017094017094%;
372 | *margin-left: 16.98763411529369%;
373 | }
374 | .row-fluid .offset1 {
375 | margin-left: 11.11111111111111%;
376 | *margin-left: 11.004728132387708%;
377 | }
378 | .row-fluid .offset1:first-child {
379 | margin-left: 8.547008547008547%;
380 | *margin-left: 8.440625568285142%;
381 | }
382 | input,
383 | textarea,
384 | .uneditable-input {
385 | margin-left: 0;
386 | }
387 | .controls-row [class*="span"] + [class*="span"] {
388 | margin-left: 30px;
389 | }
390 | input.span12,
391 | textarea.span12,
392 | .uneditable-input.span12 {
393 | width: 1156px;
394 | }
395 | input.span11,
396 | textarea.span11,
397 | .uneditable-input.span11 {
398 | width: 1056px;
399 | }
400 | input.span10,
401 | textarea.span10,
402 | .uneditable-input.span10 {
403 | width: 956px;
404 | }
405 | input.span9,
406 | textarea.span9,
407 | .uneditable-input.span9 {
408 | width: 856px;
409 | }
410 | input.span8,
411 | textarea.span8,
412 | .uneditable-input.span8 {
413 | width: 756px;
414 | }
415 | input.span7,
416 | textarea.span7,
417 | .uneditable-input.span7 {
418 | width: 656px;
419 | }
420 | input.span6,
421 | textarea.span6,
422 | .uneditable-input.span6 {
423 | width: 556px;
424 | }
425 | input.span5,
426 | textarea.span5,
427 | .uneditable-input.span5 {
428 | width: 456px;
429 | }
430 | input.span4,
431 | textarea.span4,
432 | .uneditable-input.span4 {
433 | width: 356px;
434 | }
435 | input.span3,
436 | textarea.span3,
437 | .uneditable-input.span3 {
438 | width: 256px;
439 | }
440 | input.span2,
441 | textarea.span2,
442 | .uneditable-input.span2 {
443 | width: 156px;
444 | }
445 | input.span1,
446 | textarea.span1,
447 | .uneditable-input.span1 {
448 | width: 56px;
449 | }
450 | .thumbnails {
451 | margin-left: -30px;
452 | }
453 | .thumbnails > li {
454 | margin-left: 30px;
455 | }
456 | .row-fluid .thumbnails {
457 | margin-left: 0;
458 | }
459 | }
460 |
461 | @media (min-width: 768px) and (max-width: 979px) {
462 | .row {
463 | margin-left: -20px;
464 | *zoom: 1;
465 | }
466 | .row:before,
467 | .row:after {
468 | display: table;
469 | line-height: 0;
470 | content: "";
471 | }
472 | .row:after {
473 | clear: both;
474 | }
475 | [class*="span"] {
476 | float: left;
477 | min-height: 1px;
478 | margin-left: 20px;
479 | }
480 | .container,
481 | .navbar-static-top .container,
482 | .navbar-fixed-top .container,
483 | .navbar-fixed-bottom .container {
484 | width: 724px;
485 | }
486 | .span12 {
487 | width: 724px;
488 | }
489 | .span11 {
490 | width: 662px;
491 | }
492 | .span10 {
493 | width: 600px;
494 | }
495 | .span9 {
496 | width: 538px;
497 | }
498 | .span8 {
499 | width: 476px;
500 | }
501 | .span7 {
502 | width: 414px;
503 | }
504 | .span6 {
505 | width: 352px;
506 | }
507 | .span5 {
508 | width: 290px;
509 | }
510 | .span4 {
511 | width: 228px;
512 | }
513 | .span3 {
514 | width: 166px;
515 | }
516 | .span2 {
517 | width: 104px;
518 | }
519 | .span1 {
520 | width: 42px;
521 | }
522 | .offset12 {
523 | margin-left: 764px;
524 | }
525 | .offset11 {
526 | margin-left: 702px;
527 | }
528 | .offset10 {
529 | margin-left: 640px;
530 | }
531 | .offset9 {
532 | margin-left: 578px;
533 | }
534 | .offset8 {
535 | margin-left: 516px;
536 | }
537 | .offset7 {
538 | margin-left: 454px;
539 | }
540 | .offset6 {
541 | margin-left: 392px;
542 | }
543 | .offset5 {
544 | margin-left: 330px;
545 | }
546 | .offset4 {
547 | margin-left: 268px;
548 | }
549 | .offset3 {
550 | margin-left: 206px;
551 | }
552 | .offset2 {
553 | margin-left: 144px;
554 | }
555 | .offset1 {
556 | margin-left: 82px;
557 | }
558 | .row-fluid {
559 | width: 100%;
560 | *zoom: 1;
561 | }
562 | .row-fluid:before,
563 | .row-fluid:after {
564 | display: table;
565 | line-height: 0;
566 | content: "";
567 | }
568 | .row-fluid:after {
569 | clear: both;
570 | }
571 | .row-fluid [class*="span"] {
572 | display: block;
573 | float: left;
574 | width: 100%;
575 | min-height: 30px;
576 | margin-left: 2.7624309392265194%;
577 | *margin-left: 2.709239449864817%;
578 | -webkit-box-sizing: border-box;
579 | -moz-box-sizing: border-box;
580 | box-sizing: border-box;
581 | }
582 | .row-fluid [class*="span"]:first-child {
583 | margin-left: 0;
584 | }
585 | .row-fluid .controls-row [class*="span"] + [class*="span"] {
586 | margin-left: 2.7624309392265194%;
587 | }
588 | .row-fluid .span12 {
589 | width: 100%;
590 | *width: 99.94680851063829%;
591 | }
592 | .row-fluid .span11 {
593 | width: 91.43646408839778%;
594 | *width: 91.38327259903608%;
595 | }
596 | .row-fluid .span10 {
597 | width: 82.87292817679558%;
598 | *width: 82.81973668743387%;
599 | }
600 | .row-fluid .span9 {
601 | width: 74.30939226519337%;
602 | *width: 74.25620077583166%;
603 | }
604 | .row-fluid .span8 {
605 | width: 65.74585635359117%;
606 | *width: 65.69266486422946%;
607 | }
608 | .row-fluid .span7 {
609 | width: 57.18232044198895%;
610 | *width: 57.12912895262725%;
611 | }
612 | .row-fluid .span6 {
613 | width: 48.61878453038674%;
614 | *width: 48.56559304102504%;
615 | }
616 | .row-fluid .span5 {
617 | width: 40.05524861878453%;
618 | *width: 40.00205712942283%;
619 | }
620 | .row-fluid .span4 {
621 | width: 31.491712707182323%;
622 | *width: 31.43852121782062%;
623 | }
624 | .row-fluid .span3 {
625 | width: 22.92817679558011%;
626 | *width: 22.87498530621841%;
627 | }
628 | .row-fluid .span2 {
629 | width: 14.3646408839779%;
630 | *width: 14.311449394616199%;
631 | }
632 | .row-fluid .span1 {
633 | width: 5.801104972375691%;
634 | *width: 5.747913483013988%;
635 | }
636 | .row-fluid .offset12 {
637 | margin-left: 105.52486187845304%;
638 | *margin-left: 105.41847889972962%;
639 | }
640 | .row-fluid .offset12:first-child {
641 | margin-left: 102.76243093922652%;
642 | *margin-left: 102.6560479605031%;
643 | }
644 | .row-fluid .offset11 {
645 | margin-left: 96.96132596685082%;
646 | *margin-left: 96.8549429881274%;
647 | }
648 | .row-fluid .offset11:first-child {
649 | margin-left: 94.1988950276243%;
650 | *margin-left: 94.09251204890089%;
651 | }
652 | .row-fluid .offset10 {
653 | margin-left: 88.39779005524862%;
654 | *margin-left: 88.2914070765252%;
655 | }
656 | .row-fluid .offset10:first-child {
657 | margin-left: 85.6353591160221%;
658 | *margin-left: 85.52897613729868%;
659 | }
660 | .row-fluid .offset9 {
661 | margin-left: 79.8342541436464%;
662 | *margin-left: 79.72787116492299%;
663 | }
664 | .row-fluid .offset9:first-child {
665 | margin-left: 77.07182320441989%;
666 | *margin-left: 76.96544022569647%;
667 | }
668 | .row-fluid .offset8 {
669 | margin-left: 71.2707182320442%;
670 | *margin-left: 71.16433525332079%;
671 | }
672 | .row-fluid .offset8:first-child {
673 | margin-left: 68.50828729281768%;
674 | *margin-left: 68.40190431409427%;
675 | }
676 | .row-fluid .offset7 {
677 | margin-left: 62.70718232044199%;
678 | *margin-left: 62.600799341718584%;
679 | }
680 | .row-fluid .offset7:first-child {
681 | margin-left: 59.94475138121547%;
682 | *margin-left: 59.838368402492065%;
683 | }
684 | .row-fluid .offset6 {
685 | margin-left: 54.14364640883978%;
686 | *margin-left: 54.037263430116376%;
687 | }
688 | .row-fluid .offset6:first-child {
689 | margin-left: 51.38121546961326%;
690 | *margin-left: 51.27483249088986%;
691 | }
692 | .row-fluid .offset5 {
693 | margin-left: 45.58011049723757%;
694 | *margin-left: 45.47372751851417%;
695 | }
696 | .row-fluid .offset5:first-child {
697 | margin-left: 42.81767955801105%;
698 | *margin-left: 42.71129657928765%;
699 | }
700 | .row-fluid .offset4 {
701 | margin-left: 37.01657458563536%;
702 | *margin-left: 36.91019160691196%;
703 | }
704 | .row-fluid .offset4:first-child {
705 | margin-left: 34.25414364640884%;
706 | *margin-left: 34.14776066768544%;
707 | }
708 | .row-fluid .offset3 {
709 | margin-left: 28.45303867403315%;
710 | *margin-left: 28.346655695309746%;
711 | }
712 | .row-fluid .offset3:first-child {
713 | margin-left: 25.69060773480663%;
714 | *margin-left: 25.584224756083227%;
715 | }
716 | .row-fluid .offset2 {
717 | margin-left: 19.88950276243094%;
718 | *margin-left: 19.783119783707537%;
719 | }
720 | .row-fluid .offset2:first-child {
721 | margin-left: 17.12707182320442%;
722 | *margin-left: 17.02068884448102%;
723 | }
724 | .row-fluid .offset1 {
725 | margin-left: 11.32596685082873%;
726 | *margin-left: 11.219583872105325%;
727 | }
728 | .row-fluid .offset1:first-child {
729 | margin-left: 8.56353591160221%;
730 | *margin-left: 8.457152932878806%;
731 | }
732 | input,
733 | textarea,
734 | .uneditable-input {
735 | margin-left: 0;
736 | }
737 | .controls-row [class*="span"] + [class*="span"] {
738 | margin-left: 20px;
739 | }
740 | input.span12,
741 | textarea.span12,
742 | .uneditable-input.span12 {
743 | width: 710px;
744 | }
745 | input.span11,
746 | textarea.span11,
747 | .uneditable-input.span11 {
748 | width: 648px;
749 | }
750 | input.span10,
751 | textarea.span10,
752 | .uneditable-input.span10 {
753 | width: 586px;
754 | }
755 | input.span9,
756 | textarea.span9,
757 | .uneditable-input.span9 {
758 | width: 524px;
759 | }
760 | input.span8,
761 | textarea.span8,
762 | .uneditable-input.span8 {
763 | width: 462px;
764 | }
765 | input.span7,
766 | textarea.span7,
767 | .uneditable-input.span7 {
768 | width: 400px;
769 | }
770 | input.span6,
771 | textarea.span6,
772 | .uneditable-input.span6 {
773 | width: 338px;
774 | }
775 | input.span5,
776 | textarea.span5,
777 | .uneditable-input.span5 {
778 | width: 276px;
779 | }
780 | input.span4,
781 | textarea.span4,
782 | .uneditable-input.span4 {
783 | width: 214px;
784 | }
785 | input.span3,
786 | textarea.span3,
787 | .uneditable-input.span3 {
788 | width: 152px;
789 | }
790 | input.span2,
791 | textarea.span2,
792 | .uneditable-input.span2 {
793 | width: 90px;
794 | }
795 | input.span1,
796 | textarea.span1,
797 | .uneditable-input.span1 {
798 | width: 28px;
799 | }
800 | }
801 |
802 | @media (max-width: 767px) {
803 | body {
804 | padding-right: 20px;
805 | padding-left: 20px;
806 | }
807 | .navbar-fixed-top,
808 | .navbar-fixed-bottom,
809 | .navbar-static-top {
810 | margin-right: -20px;
811 | margin-left: -20px;
812 | }
813 | .container-fluid {
814 | padding: 0;
815 | }
816 | .dl-horizontal dt {
817 | float: none;
818 | width: auto;
819 | clear: none;
820 | text-align: left;
821 | }
822 | .dl-horizontal dd {
823 | margin-left: 0;
824 | }
825 | .container {
826 | width: auto;
827 | }
828 | .row-fluid {
829 | width: 100%;
830 | }
831 | .row,
832 | .thumbnails {
833 | margin-left: 0;
834 | }
835 | .thumbnails > li {
836 | float: none;
837 | margin-left: 0;
838 | }
839 | [class*="span"],
840 | .uneditable-input[class*="span"],
841 | .row-fluid [class*="span"] {
842 | display: block;
843 | float: none;
844 | width: 100%;
845 | margin-left: 0;
846 | -webkit-box-sizing: border-box;
847 | -moz-box-sizing: border-box;
848 | box-sizing: border-box;
849 | }
850 | .span12,
851 | .row-fluid .span12 {
852 | width: 100%;
853 | -webkit-box-sizing: border-box;
854 | -moz-box-sizing: border-box;
855 | box-sizing: border-box;
856 | }
857 | .row-fluid [class*="offset"]:first-child {
858 | margin-left: 0;
859 | }
860 | .input-large,
861 | .input-xlarge,
862 | .input-xxlarge,
863 | input[class*="span"],
864 | select[class*="span"],
865 | textarea[class*="span"],
866 | .uneditable-input {
867 | display: block;
868 | width: 100%;
869 | min-height: 30px;
870 | -webkit-box-sizing: border-box;
871 | -moz-box-sizing: border-box;
872 | box-sizing: border-box;
873 | }
874 | .input-prepend input,
875 | .input-append input,
876 | .input-prepend input[class*="span"],
877 | .input-append input[class*="span"] {
878 | display: inline-block;
879 | width: auto;
880 | }
881 | .controls-row [class*="span"] + [class*="span"] {
882 | margin-left: 0;
883 | }
884 | .modal {
885 | position: fixed;
886 | top: 20px;
887 | right: 20px;
888 | left: 20px;
889 | width: auto;
890 | margin: 0;
891 | }
892 | .modal.fade {
893 | top: -100px;
894 | }
895 | .modal.fade.in {
896 | top: 20px;
897 | }
898 | }
899 |
900 | @media (max-width: 480px) {
901 | .nav-collapse {
902 | -webkit-transform: translate3d(0, 0, 0);
903 | }
904 | .page-header h1 small {
905 | display: block;
906 | line-height: 20px;
907 | }
908 | input[type="checkbox"],
909 | input[type="radio"] {
910 | border: 1px solid #ccc;
911 | }
912 | .form-horizontal .control-label {
913 | float: none;
914 | width: auto;
915 | padding-top: 0;
916 | text-align: left;
917 | }
918 | .form-horizontal .controls {
919 | margin-left: 0;
920 | }
921 | .form-horizontal .control-list {
922 | padding-top: 0;
923 | }
924 | .form-horizontal .form-actions {
925 | padding-right: 10px;
926 | padding-left: 10px;
927 | }
928 | .media .pull-left,
929 | .media .pull-right {
930 | display: block;
931 | float: none;
932 | margin-bottom: 10px;
933 | }
934 | .media-object {
935 | margin-right: 0;
936 | margin-left: 0;
937 | }
938 | .modal {
939 | top: 10px;
940 | right: 10px;
941 | left: 10px;
942 | }
943 | .modal-header .close {
944 | padding: 10px;
945 | margin: -10px;
946 | }
947 | .carousel-caption {
948 | position: static;
949 | }
950 | }
951 |
952 | @media (max-width: 979px) {
953 | body {
954 | padding-top: 0;
955 | }
956 | .navbar-fixed-top,
957 | .navbar-fixed-bottom {
958 | position: static;
959 | }
960 | .navbar-fixed-top {
961 | margin-bottom: 20px;
962 | }
963 | .navbar-fixed-bottom {
964 | margin-top: 20px;
965 | }
966 | .navbar-fixed-top .navbar-inner,
967 | .navbar-fixed-bottom .navbar-inner {
968 | padding: 5px;
969 | }
970 | .navbar .container {
971 | width: auto;
972 | padding: 0;
973 | }
974 | .navbar .brand {
975 | padding-right: 10px;
976 | padding-left: 10px;
977 | margin: 0 0 0 -5px;
978 | }
979 | .nav-collapse {
980 | clear: both;
981 | }
982 | .nav-collapse .nav {
983 | float: none;
984 | margin: 0 0 10px;
985 | }
986 | .nav-collapse .nav > li {
987 | float: none;
988 | }
989 | .nav-collapse .nav > li > a {
990 | margin-bottom: 2px;
991 | }
992 | .nav-collapse .nav > .divider-vertical {
993 | display: none;
994 | }
995 | .nav-collapse .nav .nav-header {
996 | color: #777777;
997 | text-shadow: none;
998 | }
999 | .nav-collapse .nav > li > a,
1000 | .nav-collapse .dropdown-menu a {
1001 | padding: 9px 15px;
1002 | font-weight: bold;
1003 | color: #777777;
1004 | -webkit-border-radius: 3px;
1005 | -moz-border-radius: 3px;
1006 | border-radius: 3px;
1007 | }
1008 | .nav-collapse .btn {
1009 | padding: 4px 10px 4px;
1010 | font-weight: normal;
1011 | -webkit-border-radius: 4px;
1012 | -moz-border-radius: 4px;
1013 | border-radius: 4px;
1014 | }
1015 | .nav-collapse .dropdown-menu li + li a {
1016 | margin-bottom: 2px;
1017 | }
1018 | .nav-collapse .nav > li > a:hover,
1019 | .nav-collapse .nav > li > a:focus,
1020 | .nav-collapse .dropdown-menu a:hover,
1021 | .nav-collapse .dropdown-menu a:focus {
1022 | background-color: #f2f2f2;
1023 | }
1024 | .navbar-inverse .nav-collapse .nav > li > a,
1025 | .navbar-inverse .nav-collapse .dropdown-menu a {
1026 | color: #999999;
1027 | }
1028 | .navbar-inverse .nav-collapse .nav > li > a:hover,
1029 | .navbar-inverse .nav-collapse .nav > li > a:focus,
1030 | .navbar-inverse .nav-collapse .dropdown-menu a:hover,
1031 | .navbar-inverse .nav-collapse .dropdown-menu a:focus {
1032 | background-color: #111111;
1033 | }
1034 | .nav-collapse.in .btn-group {
1035 | padding: 0;
1036 | margin-top: 5px;
1037 | }
1038 | .nav-collapse .dropdown-menu {
1039 | position: static;
1040 | top: auto;
1041 | left: auto;
1042 | display: none;
1043 | float: none;
1044 | max-width: none;
1045 | padding: 0;
1046 | margin: 0 15px;
1047 | background-color: transparent;
1048 | border: none;
1049 | -webkit-border-radius: 0;
1050 | -moz-border-radius: 0;
1051 | border-radius: 0;
1052 | -webkit-box-shadow: none;
1053 | -moz-box-shadow: none;
1054 | box-shadow: none;
1055 | }
1056 | .nav-collapse .open > .dropdown-menu {
1057 | display: block;
1058 | }
1059 | .nav-collapse .dropdown-menu:before,
1060 | .nav-collapse .dropdown-menu:after {
1061 | display: none;
1062 | }
1063 | .nav-collapse .dropdown-menu .divider {
1064 | display: none;
1065 | }
1066 | .nav-collapse .nav > li > .dropdown-menu:before,
1067 | .nav-collapse .nav > li > .dropdown-menu:after {
1068 | display: none;
1069 | }
1070 | .nav-collapse .navbar-form,
1071 | .nav-collapse .navbar-search {
1072 | float: none;
1073 | padding: 10px 15px;
1074 | margin: 10px 0;
1075 | border-top: 1px solid #f2f2f2;
1076 | border-bottom: 1px solid #f2f2f2;
1077 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1078 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1079 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
1080 | }
1081 | .navbar-inverse .nav-collapse .navbar-form,
1082 | .navbar-inverse .nav-collapse .navbar-search {
1083 | border-top-color: #111111;
1084 | border-bottom-color: #111111;
1085 | }
1086 | .navbar .nav-collapse .nav.pull-right {
1087 | float: none;
1088 | margin-left: 0;
1089 | }
1090 | .nav-collapse,
1091 | .nav-collapse.collapse {
1092 | height: 0;
1093 | overflow: hidden;
1094 | }
1095 | .navbar .btn-navbar {
1096 | display: block;
1097 | }
1098 | .navbar-static .navbar-inner {
1099 | padding-right: 10px;
1100 | padding-left: 10px;
1101 | }
1102 | }
1103 |
1104 | @media (min-width: 980px) {
1105 | .nav-collapse.collapse {
1106 | height: auto !important;
1107 | overflow: visible !important;
1108 | }
1109 | }
1110 |
--------------------------------------------------------------------------------
/public_html/css/bootstrap-responsive.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Responsive v2.3.2
3 | *
4 | * Copyright 2013 Twitter, Inc
5 | * Licensed under the Apache License v2.0
6 | * http://www.apache.org/licenses/LICENSE-2.0
7 | *
8 | * Designed and built with all the love in the world by @mdo and @fat.
9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
10 |
--------------------------------------------------------------------------------
/public_html/filesList.php:
--------------------------------------------------------------------------------
1 | $value) {
17 | if (!in_array($value, array("." , ".." , ".DS_Store"))) {
18 | if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
19 | $result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
20 | }
21 | else {
22 | $result[] = $value;
23 | }
24 | }
25 | }
26 | return $result;
27 | }
28 |
29 | //
30 | // Start the party
31 | //
32 | $path = 'AppsScript';
33 | //$files = dirToArray($path);
34 | exec("find . -follow", $files);
35 | print_r($files);
36 |
37 | foreach ($files as $name) {
38 | if (strpos($name, "AppsScript/" ) > 0) {
39 | if ( !strpos($name, ".DS_Store") ) {
40 | $name = str_replace("./AppsScript/", "", $name);
41 | $fName = "* [$name](" . $gitPathPre . "/" . $name . ")\n";
42 | array_push($fullList, $fName );
43 | file_put_contents($mdFileName, $fName, FILE_APPEND);
44 | }
45 | }
46 | }
47 | echo "\n\n=== list of files: \n";
48 | print_r($fullList);
49 |
50 |
--------------------------------------------------------------------------------
/public_html/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greenido/AppsScriptBests/9470b0ef54139f4f13b419c140ff889ff0d9aa80/public_html/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/public_html/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/greenido/AppsScriptBests/9470b0ef54139f4f13b419c140ff889ff0d9aa80/public_html/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/public_html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | A little Repo to hold all the great libraries, utils and class around Apps Script
9 |
10 |
11 |
12 |
13 |
14 |
15 | Todo - Create a list of the current components
16 |
17 | Todo - build a wish list with ideas for libs/components
18 |
19 | Other Sources
20 |
33 |
34 | TODO:
35 | 1. Use a side bar to hold the menu for all the content
36 | 2. Allow a short html/markup explanation page per module
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/public_html/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap.js by @fat & @mdo
3 | * Copyright 2013 Twitter, Inc.
4 | * http://www.apache.org/licenses/LICENSE-2.0.txt
5 | */
6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('').insertBefore(e(this)).on("click",r),s.toggleClass("open")),n.focus(),!1},keydown:function(n){var r,s,o,u,a,f;if(!/(38|40|27)/.test(n.keyCode))return;r=e(this),n.preventDefault(),n.stopPropagation();if(r.is(".disabled, :disabled"))return;u=i(r),a=u.hasClass("open");if(!a||a&&n.keyCode==27)return n.which==27&&u.find(t).focus(),r.click();s=e("[role=menu] li:not(.divider):visible a",u);if(!s.length)return;f=s.index(s.filter(":focus")),n.keyCode==38&&f>0&&f--,n.keyCode==40&&f').appendTo(document.body),this.$backdrop.click(this.options.backdrop=="static"?e.proxy(this.$element[0].focus,this.$element[0]):e.proxy(this.hide,this)),i&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in");if(!t)return;i?this.$backdrop.one(e.support.transition.end,t):t()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),e.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(e.support.transition.end,t):t()):t&&t()}};var n=e.fn.modal;e.fn.modal=function(n){return this.each(function(){var r=e(this),i=r.data("modal"),s=e.extend({},e.fn.modal.defaults,r.data(),typeof n=="object"&&n);i||r.data("modal",i=new t(this,s)),typeof n=="string"?i[n]():s.show&&i.show()})},e.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},e.fn.modal.Constructor=t,e.fn.modal.noConflict=function(){return e.fn.modal=n,this},e(document).on("click.modal.data-api",'[data-toggle="modal"]',function(t){var n=e(this),r=n.attr("href"),i=e(n.attr("data-target")||r&&r.replace(/.*(?=#[^\s]+$)/,"")),s=i.data("modal")?"toggle":e.extend({remote:!/#/.test(r)&&r},i.data(),n.data());t.preventDefault(),i.modal(s).one("hide",function(){n.focus()})})}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("tooltip",e,t)};t.prototype={constructor:t,init:function(t,n,r){var i,s,o,u,a;this.type=t,this.$element=e(n),this.options=this.getOptions(r),this.enabled=!0,o=this.options.trigger.split(" ");for(a=o.length;a--;)u=o[a],u=="click"?this.$element.on("click."+this.type,this.options.selector,e.proxy(this.toggle,this)):u!="manual"&&(i=u=="hover"?"mouseenter":"focus",s=u=="hover"?"mouseleave":"blur",this.$element.on(i+"."+this.type,this.options.selector,e.proxy(this.enter,this)),this.$element.on(s+"."+this.type,this.options.selector,e.proxy(this.leave,this)));this.options.selector?this._options=e.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(t){return t=e.extend({},e.fn[this.type].defaults,this.$element.data(),t),t.delay&&typeof t.delay=="number"&&(t.delay={show:t.delay,hide:t.delay}),t},enter:function(t){var n=e.fn[this.type].defaults,r={},i;this._options&&e.each(this._options,function(e,t){n[e]!=t&&(r[e]=t)},this),i=e(t.currentTarget)[this.type](r).data(this.type);if(!i.options.delay||!i.options.delay.show)return i.show();clearTimeout(this.timeout),i.hoverState="in",this.timeout=setTimeout(function(){i.hoverState=="in"&&i.show()},i.options.delay.show)},leave:function(t){var n=e(t.currentTarget)[this.type](this._options).data(this.type);this.timeout&&clearTimeout(this.timeout);if(!n.options.delay||!n.options.delay.hide)return n.hide();n.hoverState="out",this.timeout=setTimeout(function(){n.hoverState=="out"&&n.hide()},n.options.delay.hide)},show:function(){var t,n,r,i,s,o,u=e.Event("show");if(this.hasContent()&&this.enabled){this.$element.trigger(u);if(u.isDefaultPrevented())return;t=this.tip(),this.setContent(),this.options.animation&&t.addClass("fade"),s=typeof this.options.placement=="function"?this.options.placement.call(this,t[0],this.$element[0]):this.options.placement,t.detach().css({top:0,left:0,display:"block"}),this.options.container?t.appendTo(this.options.container):t.insertAfter(this.$element),n=this.getPosition(),r=t[0].offsetWidth,i=t[0].offsetHeight;switch(s){case"bottom":o={top:n.top+n.height,left:n.left+n.width/2-r/2};break;case"top":o={top:n.top-i,left:n.left+n.width/2-r/2};break;case"left":o={top:n.top+n.height/2-i/2,left:n.left-r};break;case"right":o={top:n.top+n.height/2-i/2,left:n.left+n.width}}this.applyPlacement(o,s),this.$element.trigger("shown")}},applyPlacement:function(e,t){var n=this.tip(),r=n[0].offsetWidth,i=n[0].offsetHeight,s,o,u,a;n.offset(e).addClass(t).addClass("in"),s=n[0].offsetWidth,o=n[0].offsetHeight,t=="top"&&o!=i&&(e.top=e.top+i-o,a=!0),t=="bottom"||t=="top"?(u=0,e.left<0&&(u=e.left*-2,e.left=0,n.offset(e),s=n[0].offsetWidth,o=n[0].offsetHeight),this.replaceArrow(u-r+s,s,"left")):this.replaceArrow(o-i,o,"top"),a&&n.offset(e)},replaceArrow:function(e,t,n){this.arrow().css(n,e?50*(1-e/t)+"%":"")},setContent:function(){var e=this.tip(),t=this.getTitle();e.find(".tooltip-inner")[this.options.html?"html":"text"](t),e.removeClass("fade in top bottom left right")},hide:function(){function i(){var t=setTimeout(function(){n.off(e.support.transition.end).detach()},500);n.one(e.support.transition.end,function(){clearTimeout(t),n.detach()})}var t=this,n=this.tip(),r=e.Event("hide");this.$element.trigger(r);if(r.isDefaultPrevented())return;return n.removeClass("in"),e.support.transition&&this.$tip.hasClass("fade")?i():n.detach(),this.$element.trigger("hidden"),this},fixTitle:function(){var e=this.$element;(e.attr("title")||typeof e.attr("data-original-title")!="string")&&e.attr("data-original-title",e.attr("title")||"").attr("title","")},hasContent:function(){return this.getTitle()},getPosition:function(){var t=this.$element[0];return e.extend({},typeof t.getBoundingClientRect=="function"?t.getBoundingClientRect():{width:t.offsetWidth,height:t.offsetHeight},this.$element.offset())},getTitle:function(){var e,t=this.$element,n=this.options;return e=t.attr("data-original-title")||(typeof n.title=="function"?n.title.call(t[0]):n.title),e},tip:function(){return this.$tip=this.$tip||e(this.options.template)},arrow:function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(t){var n=t?e(t.currentTarget)[this.type](this._options).data(this.type):this;n.tip().hasClass("in")?n.hide():n.show()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var n=e.fn.tooltip;e.fn.tooltip=function(n){return this.each(function(){var r=e(this),i=r.data("tooltip"),s=typeof n=="object"&&n;i||r.data("tooltip",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.tooltip.Constructor=t,e.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},e.fn.tooltip.noConflict=function(){return e.fn.tooltip=n,this}}(window.jQuery),!function(e){"use strict";var t=function(e,t){this.init("popover",e,t)};t.prototype=e.extend({},e.fn.tooltip.Constructor.prototype,{constructor:t,setContent:function(){var e=this.tip(),t=this.getTitle(),n=this.getContent();e.find(".popover-title")[this.options.html?"html":"text"](t),e.find(".popover-content")[this.options.html?"html":"text"](n),e.removeClass("fade top bottom left right in")},hasContent:function(){return this.getTitle()||this.getContent()},getContent:function(){var e,t=this.$element,n=this.options;return e=(typeof n.content=="function"?n.content.call(t[0]):n.content)||t.attr("data-content"),e},tip:function(){return this.$tip||(this.$tip=e(this.options.template)),this.$tip},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}});var n=e.fn.popover;e.fn.popover=function(n){return this.each(function(){var r=e(this),i=r.data("popover"),s=typeof n=="object"&&n;i||r.data("popover",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.popover.Constructor=t,e.fn.popover.defaults=e.extend({},e.fn.tooltip.defaults,{placement:"right",trigger:"click",content:"",template:''}),e.fn.popover.noConflict=function(){return e.fn.popover=n,this}}(window.jQuery),!function(e){"use strict";function t(t,n){var r=e.proxy(this.process,this),i=e(t).is("body")?e(window):e(t),s;this.options=e.extend({},e.fn.scrollspy.defaults,n),this.$scrollElement=i.on("scroll.scroll-spy.data-api",r),this.selector=(this.options.target||(s=e(t).attr("href"))&&s.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.$body=e("body"),this.refresh(),this.process()}t.prototype={constructor:t,refresh:function(){var t=this,n;this.offsets=e([]),this.targets=e([]),n=this.$body.find(this.selector).map(function(){var n=e(this),r=n.data("target")||n.attr("href"),i=/^#\w/.test(r)&&e(r);return i&&i.length&&[[i.position().top+(!e.isWindow(t.$scrollElement.get(0))&&t.$scrollElement.scrollTop()),r]]||null}).sort(function(e,t){return e[0]-t[0]}).each(function(){t.offsets.push(this[0]),t.targets.push(this[1])})},process:function(){var e=this.$scrollElement.scrollTop()+this.options.offset,t=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,n=t-this.$scrollElement.height(),r=this.offsets,i=this.targets,s=this.activeTarget,o;if(e>=n)return s!=(o=i.last()[0])&&this.activate(o);for(o=r.length;o--;)s!=i[o]&&e>=r[o]&&(!r[o+1]||e<=r[o+1])&&this.activate(i[o])},activate:function(t){var n,r;this.activeTarget=t,e(this.selector).parent(".active").removeClass("active"),r=this.selector+'[data-target="'+t+'"],'+this.selector+'[href="'+t+'"]',n=e(r).parent("li").addClass("active"),n.parent(".dropdown-menu").length&&(n=n.closest("li.dropdown").addClass("active")),n.trigger("activate")}};var n=e.fn.scrollspy;e.fn.scrollspy=function(n){return this.each(function(){var r=e(this),i=r.data("scrollspy"),s=typeof n=="object"&&n;i||r.data("scrollspy",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.scrollspy.Constructor=t,e.fn.scrollspy.defaults={offset:10},e.fn.scrollspy.noConflict=function(){return e.fn.scrollspy=n,this},e(window).on("load",function(){e('[data-spy="scroll"]').each(function(){var t=e(this);t.scrollspy(t.data())})})}(window.jQuery),!function(e){"use strict";var t=function(t){this.element=e(t)};t.prototype={constructor:t,show:function(){var t=this.element,n=t.closest("ul:not(.dropdown-menu)"),r=t.attr("data-target"),i,s,o;r||(r=t.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,""));if(t.parent("li").hasClass("active"))return;i=n.find(".active:last a")[0],o=e.Event("show",{relatedTarget:i}),t.trigger(o);if(o.isDefaultPrevented())return;s=e(r),this.activate(t.parent("li"),n),this.activate(s,s.parent(),function(){t.trigger({type:"shown",relatedTarget:i})})},activate:function(t,n,r){function o(){i.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),t.addClass("active"),s?(t[0].offsetWidth,t.addClass("in")):t.removeClass("fade"),t.parent(".dropdown-menu")&&t.closest("li.dropdown").addClass("active"),r&&r()}var i=n.find("> .active"),s=r&&e.support.transition&&i.hasClass("fade");s?i.one(e.support.transition.end,o):o(),i.removeClass("in")}};var n=e.fn.tab;e.fn.tab=function(n){return this.each(function(){var r=e(this),i=r.data("tab");i||r.data("tab",i=new t(this)),typeof n=="string"&&i[n]()})},e.fn.tab.Constructor=t,e.fn.tab.noConflict=function(){return e.fn.tab=n,this},e(document).on("click.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(t){t.preventDefault(),e(this).tab("show")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.typeahead.defaults,n),this.matcher=this.options.matcher||this.matcher,this.sorter=this.options.sorter||this.sorter,this.highlighter=this.options.highlighter||this.highlighter,this.updater=this.options.updater||this.updater,this.source=this.options.source,this.$menu=e(this.options.menu),this.shown=!1,this.listen()};t.prototype={constructor:t,select:function(){var e=this.$menu.find(".active").attr("data-value");return this.$element.val(this.updater(e)).change(),this.hide()},updater:function(e){return e},show:function(){var t=e.extend({},this.$element.position(),{height:this.$element[0].offsetHeight});return this.$menu.insertAfter(this.$element).css({top:t.top+t.height,left:t.left}).show(),this.shown=!0,this},hide:function(){return this.$menu.hide(),this.shown=!1,this},lookup:function(t){var n;return this.query=this.$element.val(),!this.query||this.query.length"+t+""})},render:function(t){var n=this;return t=e(t).map(function(t,r){return t=e(n.options.item).attr("data-value",r),t.find("a").html(n.highlighter(r)),t[0]}),t.first().addClass("active"),this.$menu.html(t),this},next:function(t){var n=this.$menu.find(".active").removeClass("active"),r=n.next();r.length||(r=e(this.$menu.find("li")[0])),r.addClass("active")},prev:function(e){var t=this.$menu.find(".active").removeClass("active"),n=t.prev();n.length||(n=this.$menu.find("li").last()),n.addClass("active")},listen:function(){this.$element.on("focus",e.proxy(this.focus,this)).on("blur",e.proxy(this.blur,this)).on("keypress",e.proxy(this.keypress,this)).on("keyup",e.proxy(this.keyup,this)),this.eventSupported("keydown")&&this.$element.on("keydown",e.proxy(this.keydown,this)),this.$menu.on("click",e.proxy(this.click,this)).on("mouseenter","li",e.proxy(this.mouseenter,this)).on("mouseleave","li",e.proxy(this.mouseleave,this))},eventSupported:function(e){var t=e in this.$element;return t||(this.$element.setAttribute(e,"return;"),t=typeof this.$element[e]=="function"),t},move:function(e){if(!this.shown)return;switch(e.keyCode){case 9:case 13:case 27:e.preventDefault();break;case 38:e.preventDefault(),this.prev();break;case 40:e.preventDefault(),this.next()}e.stopPropagation()},keydown:function(t){this.suppressKeyPressRepeat=~e.inArray(t.keyCode,[40,38,9,13,27]),this.move(t)},keypress:function(e){if(this.suppressKeyPressRepeat)return;this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break;case 9:case 13:if(!this.shown)return;this.select();break;case 27:if(!this.shown)return;this.hide();break;default:this.lookup()}e.stopPropagation(),e.preventDefault()},focus:function(e){this.focused=!0},blur:function(e){this.focused=!1,!this.mousedover&&this.shown&&this.hide()},click:function(e){e.stopPropagation(),e.preventDefault(),this.select(),this.$element.focus()},mouseenter:function(t){this.mousedover=!0,this.$menu.find(".active").removeClass("active"),e(t.currentTarget).addClass("active")},mouseleave:function(e){this.mousedover=!1,!this.focused&&this.shown&&this.hide()}};var n=e.fn.typeahead;e.fn.typeahead=function(n){return this.each(function(){var r=e(this),i=r.data("typeahead"),s=typeof n=="object"&&n;i||r.data("typeahead",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.typeahead.defaults={source:[],items:8,menu:'',item:' ',minLength:1},e.fn.typeahead.Constructor=t,e.fn.typeahead.noConflict=function(){return e.fn.typeahead=n,this},e(document).on("focus.typeahead.data-api",'[data-provide="typeahead"]',function(t){var n=e(this);if(n.data("typeahead"))return;n.typeahead(n.data())})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.options=e.extend({},e.fn.affix.defaults,n),this.$window=e(window).on("scroll.affix.data-api",e.proxy(this.checkPosition,this)).on("click.affix.data-api",e.proxy(function(){setTimeout(e.proxy(this.checkPosition,this),1)},this)),this.$element=e(t),this.checkPosition()};t.prototype.checkPosition=function(){if(!this.$element.is(":visible"))return;var t=e(document).height(),n=this.$window.scrollTop(),r=this.$element.offset(),i=this.options.offset,s=i.bottom,o=i.top,u="affix affix-top affix-bottom",a;typeof i!="object"&&(s=o=i),typeof o=="function"&&(o=i.top()),typeof s=="function"&&(s=i.bottom()),a=this.unpin!=null&&n+this.unpin<=r.top?!1:s!=null&&r.top+this.$element.height()>=t-s?"bottom":o!=null&&n<=o?"top":!1;if(this.affixed===a)return;this.affixed=a,this.unpin=a=="bottom"?r.top-n:null,this.$element.removeClass(u).addClass("affix"+(a?"-"+a:""))};var n=e.fn.affix;e.fn.affix=function(n){return this.each(function(){var r=e(this),i=r.data("affix"),s=typeof n=="object"&&n;i||r.data("affix",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.affix.Constructor=t,e.fn.affix.defaults={offset:0},e.fn.affix.noConflict=function(){return e.fn.affix=n,this},e(window).on("load",function(){e('[data-spy="affix"]').each(function(){var t=e(this),n=t.data();n.offset=n.offset||{},n.offsetBottom&&(n.offset.bottom=n.offsetBottom),n.offsetTop&&(n.offset.top=n.offsetTop),t.affix(n)})})}(window.jQuery);
--------------------------------------------------------------------------------