feisty meow concerns codebase  2.140
js_marks_maker.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : marks_maker_javascript *
4 * Author : Chris Koeritz *
5 * *
6 * Purpose: *
7 * *
8 * Turns a link database in HOOPLE format into a web page, when given a *
9 * suitable template file. The template file must have the phrase: *
10 * $INSERT_LINKS_HERE *
11 * at the point where the generated links are supposed to be stored. *
12 * *
13 *******************************************************************************
14 * Copyright (c) 2005-$now By Author. This program is free software; you can *
15 * redistribute it and/or modify it under the terms of the GNU General Public *
16 * License as published by the Free Software Foundation; either version 2 of *
17 * the License or (at your option) any later version. This is online at: *
18 * http://www.fsf.org/copyleft/gpl.html *
19 * Please send any updates to: fred@gruntose.com *
20 \*****************************************************************************/
21 
22 #include "bookmark_tree.h"
23 
26 #include <basis/astring.h>
27 #include <basis/functions.h>
28 #include <basis/guards.h>
29 #include <filesystem/byte_filer.h>
30 #include <filesystem/filename.h>
31 #include <loggers/file_logger.h>
32 #include <structures/stack.h>
34 #include <textual/list_parsing.h>
35 #include <timely/time_stamp.h>
36 
37 using namespace application;
38 using namespace basis;
39 using namespace filesystem;
40 using namespace loggers;
41 using namespace nodes;
42 using namespace structures;
43 using namespace textual;
44 using namespace timely;
45 
46 //#define DEBUG_MARKS
47  // uncomment to have more debugging noise.
48 
49 #undef BASE_LOG
50 #define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT)
51 #undef LOG
52 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), \
53  a_sprintf("line %d: ", _categories._line_number) + s, ALWAYS_PRINT)
54 
55 const int MAX_FILE_SIZE = 4 * MEGABYTE;
56  // the largest file we'll read.
57 
59 
60 class marks_maker_javascript : public application_shell
61 {
62 public:
63  marks_maker_javascript() : application_shell(), _need_closure(false),
64  _loader_count(0), _link_spool(0), _functions_pending(0) {}
65  DEFINE_CLASS_NAME("marks_maker_javascript");
66  virtual int execute();
67  int print_instructions(const filename &program_name);
68 
69  int write_marks_page(const astring &output_filename,
70  const astring &template_filename);
71  // given a tree of links, this writes out a web page to "output_filename"
72  // using a template file "template_filename".
73 
74 private:
75  bookmark_tree _categories; // our tree of categories.
76  bool _need_closure; // true if our <div> needs a closure.
77  int _loader_count; // count of the loader functions.
78  int _link_spool; // count of which link we're writing.
79  stack<astring> _functions_pending; // used for javascript node functions.
80 
81 //this needs to gather any strings that would have gone into functions.
82 //instead, they need to be written into the current node's string.
83 //when a new function def would be seen, then we need to push a new node
84 //for accumulating the text.
85 
86  // these handle outputting text for categories and links.
87  void write_category(inner_mark_tree *node, astring &output);
88  void write_link(inner_mark_tree *node, const link_record &linko,
89  astring &output);
90 };
91 
93 
95 {
96  a_sprintf to_show("%s:\n\
97 This program needs three filenames as command line parameters. The -i flag\n\
98 is used to specify the input filename, the -t flag specifies a template web\n\
99 page which is used as the wrapper around the links, and the -o flag specifies\n\
100 the web page to be created. The input file is expected to be in the HOOPLE\n\
101 link database format. The output file will be created from the template file\n\
102 by finding the phrase $INSERT_LINKS_HERE in it and replacing that with html\n\
103 formatted link and categories from the input file. Another tag of $TODAYS_DATE\n\
104 will be replaced with the date when the output file is regenerated.\n\
105 The HOOPLE link format is documented here:\n\
106  http://feistymeow.org/guides/link_database/format_manifesto.txt\n\
107 ", program_name.basename().raw().s(), program_name.basename().raw().s());
108  program_wide_logger::get().log(to_show, ALWAYS_PRINT);
109  return 12;
110 }
111 
112 void marks_maker_javascript::write_category(inner_mark_tree *node, astring &output)
113 {
114  FUNCDEF("write_category");
115  // output a javascript line for the category.
116 
117  int node_num = node->_uid;
118  inner_mark_tree *parent = dynamic_cast<inner_mark_tree *>(node->parent());
119  int parent_node = parent? parent->_uid : -1;
120  // the parent node for root is a negative one.
121  astring chewed_name = node->name();
122  for (int i = chewed_name.end(); i >= 0; i--) {
123  // escape any raw single quotes that we see.
124  if (chewed_name[i] == '\'') {
125  chewed_name.zap(i, i);
126  chewed_name.insert(i, "\\'");
127  }
128  }
129  output += a_sprintf(" b.add(%d, %d, '%s');\n", node_num, parent_node,
130  chewed_name.s());
131 }
132 
133 void marks_maker_javascript::write_link(inner_mark_tree *node, const link_record &linko,
134  astring &output)
135 {
136  FUNCDEF("write_link");
137  // write a javascript link definition.
138  int parent_node = node->_uid;
139  astring chewed_name = linko._description;
140  for (int i = chewed_name.end(); i >= 0; i--) {
141  // escape any raw single quotes that we see.
142  if (chewed_name[i] == '\'') {
143  chewed_name.zap(i, i);
144  chewed_name.insert(i, "\\'");
145  }
146  }
147 
148  if (!linko._url) {
149  // this just appears to be a comment line.
150  if (!linko._description) return; // it's a nothing line.
151 
152 /*
153 //hmmm: probably not what we want.
154 //hmmm: why not, again?
155  output += linko._description;
156  output += "<br>";
157  output += parser_bits::platform_eol_to_chars();
158 */
159  return;
160  }
161 
162  // generate a function header if the number of links is a multiple of 100.
163  if (! (_link_spool % 100) ) {
164  if (_link_spool) {
165  // close out the previous function and set a timeout.
166  output += " setTimeout('run_tree_loaders()', 0);\n";
167  output += "}\n";
168  }
169 
170  output += a_sprintf("function tree_loader_%d() {\n", _loader_count++);
171  }
172  _link_spool++;
173 
174  output += a_sprintf(" b.add(%d, %d, '%s', '%s');\n",
175  linko._uid, parent_node, chewed_name.s(), linko._url.s());
176 }
177 
178 int marks_maker_javascript::execute()
179 {
180  FUNCDEF("execute");
182 
183  command_line cmds(_global_argc, _global_argv); // process the command line parameters.
184  astring input_filename; // we'll store our link database name here.
185  astring output_filename; // where the web page we're creating goes.
186  astring template_filename; // the wrapper html code that we'll stuff.
187  if (!cmds.get_value('i', input_filename, false))
188  return print_instructions(cmds.program_name());
189  if (!cmds.get_value('o', output_filename, false))
190  return print_instructions(cmds.program_name());
191  if (!cmds.get_value('t', template_filename, false))
192  return print_instructions(cmds.program_name());
193 
194  BASE_LOG(astring("input file: ") + input_filename);
195  BASE_LOG(astring("output file: ") + output_filename);
196  BASE_LOG(astring("template file: ") + template_filename);
197 
198  int ret = _categories.read_csv_file(input_filename);
199  if (ret) return ret;
200 
201  ret = write_marks_page(output_filename, template_filename);
202  if (ret) return ret;
203 
204  return 0;
205 }
206 
207 int marks_maker_javascript::write_marks_page(const astring &output_filename,
208  const astring &template_filename)
209 {
210  FUNCDEF("write_marks_page");
211  astring long_string;
212  // this is our accumulator of links. it is the semi-final result that will
213  // be injected into the template file.
214 
215  // add our target layer so that we can write to a useful place.
216  long_string += "<div class=\"marks_target\" id=\"martarg\">Marks Target</div>\n";
217 
218  // add the tree style and creation of the tree object into the text.
219  long_string += "\n<div class=\"dtree\">\n";
220  long_string += "<script type=\"text/javascript\">\n";
221  long_string += "<!--\n";
222 
223  long_string += "function open_mark(url) {\n";
224  long_string += " window.open(url, '', '');\n";
225  long_string += "}\n";
226 
227  // the list of functions is used for calling into the tree loaders
228  // without blocking.
229  long_string += " b = new dTree('b');\n";
231  long_string += " b.config.useCookies = false;\n";
232  long_string += " b.config.folderLinks = false;\n";
233 
234  // traverse the tree in prefix order.
235  tree::iterator itty = _categories.access_root().start(tree::prefix);
236  tree *curr = NULL_POINTER;
237  while ( (curr = itty.next()) ) {
238  inner_mark_tree *nod = (inner_mark_tree *)curr;
239  // print out the category on this node.
240  write_category(nod, long_string);
241 
242  // print the link for all of the ones stored at this node.
243  for (int i = 0; i < nod->_links.elements(); i++) {
244  link_record *lin = nod->_links.borrow(i);
245  write_link(nod, *lin, long_string);
246  }
247  }
248 
249  // close the block of script in the output.
250  long_string += " setTimeout('run_tree_loaders()', 0);\n";
251  long_string += "}\n\n";
252 
253  long_string += a_sprintf("function tree_loader_%d()"
254  "{ setTimeout('run_tree_loaders()', 0); }\n", _loader_count++);
255 
256  long_string += "\nconst max_funcs = 1000;\n";
257  long_string += "var loader_functions = new Array(max_funcs);\n";
258  long_string += "var curr_func = 0;\n";
259  long_string += "var done_rendering = false;\n\n";
260 
261  long_string += a_sprintf("for (var i = 0; i < %d; i++) {\n", _loader_count);
262  long_string += " loader_functions[curr_func++] "
263  "= 'tree_loader_' + i + '()';\n";
264  long_string += "}\n";
265 
266  long_string += "var run_index = 0;\n";
267  long_string += "function run_tree_loaders() {\n";
268  long_string += " if (done_rendering) return;\n";
269  long_string += " if (run_index >= curr_func) {\n";
270 
271  long_string += " if (document.getElementById) {\n";
272  long_string += " x = document.getElementById('martarg');\n";
273  long_string += " x.innerHTML = '';\n";
274  long_string += " x.innerHTML = b;\n";
275  long_string += " } else { document.write(b); }\n";
276 //not a very graceful degradation. we should use the other options from:
277 // http://www.quirksmode.org/js/layerwrite.html
278  long_string += " done_rendering = true;\n";
279  long_string += " return;\n";
280  long_string += " }\n";
281  long_string += " var next_func = loader_functions[run_index++];\n";
282  long_string += " setTimeout(next_func, 0);\n";
283  long_string += "}\n";
284 
285  long_string += a_sprintf(" run_tree_loaders();\n", _loader_count);
286 
287  long_string += "//-->\n";
288  long_string += "</script>\n";
289  long_string += "<p><a href=\"javascript: b.openAll();\">open all</a> | "
290  "<a href=\"javascript: b.closeAll();\">close all</a></p>\n";
291  long_string += "</div>\n";
292 
293  byte_filer template_file(template_filename, "r");
294  astring full_template;
295  if (!template_file.good())
296  non_continuable_error(class_name(), func, "the template file could not be opened");
297  template_file.read(full_template, MAX_FILE_SIZE);
298  template_file.close();
299 
300  // javascript output needs some extra junk added to the header section.
301  int indy = full_template.ifind("</title>");
302  if (negative(indy))
303  non_continuable_error(class_name(), func, "the template file is missing "
304  "a <head> declaration");
305 //hmmm: the path here must be configurable!
306  full_template.insert(indy + 8, "\n\n"
307  "<link rel=\"StyleSheet\" href=\"/yeti/javascript/dtree/dtree.css\" "
308  "type=\"text/css\" />\n"
309  "<script type=\"text/javascript\" src=\"/yeti/javascript/"
310  "dtree/dtree.js\"></script>\n");
311 
312  // replace the tag with the long string we created.
313  bool found_it = full_template.replace("$INSERT_LINKS_HERE", long_string);
314  if (!found_it)
315  non_continuable_error(class_name(), func, "the template file is missing "
316  "the insertion point");
317  full_template.replace("$TODAYS_DATE", time_stamp::notarize(true));
318 
319  filename outname(output_filename);
320  if (outname.exists()) {
321  non_continuable_error(class_name(), func, astring("the output file ")
322  + output_filename + " already exists. It would be over-written if "
323  "we continued.");
324  }
325 
326  byte_filer output_file(output_filename, "w");
327  if (!output_file.good())
328  non_continuable_error(class_name(), func, "the output file could not be opened");
329  // write the newly generated web page out now.
330  output_file.write(full_template);
331  output_file.close();
332 
333 // show the tree.
334 // BASE_LOG("");
335 // BASE_LOG(_categories.access_root().text_form());
336 
337  BASE_LOG(a_sprintf("wrote %d links in %d categories.",
338  _categories.link_count(), _categories.category_count()));
339  BASE_LOG(astring(""));
340 
341  return 0;
342 }
343 
345 
346 HOOPLE_MAIN(marks_maker_javascript, )
347 
int print_instructions(bool good, const astring &program_name)
Definition: checker.cpp:45
The application_shell is a base object for console programs.
a_sprintf is a specialization of astring that provides printf style support.
Definition: astring.h:440
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
bool replace(const astring &tag, const astring &replacement)
replaces the first occurrence of "tag" text with the "replacement".
Definition: astring.cpp:905
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition: astring.cpp:521
void insert(int position, const astring &to_insert)
Copies "to_insert" into "this" at the "position".
Definition: astring.cpp:892
int end() const
returns the index of the last (non-null) character in the string.
Definition: astring.h:86
int ifind(char to_find, int position=0, bool reverse=false) const
like the find() methods above, but case-insensitive.
Definition: astring.cpp:571
Provides file managment services using the standard I/O support.
Definition: byte_filer.h:32
Provides operations commonly needed on file names.
Definition: filename.h:64
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition: filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition: filename.cpp:385
listo_links _links
An object representing the interstitial cell in most linked data structures.
Definition: node.h:41
tree * next()
Returns a pointer to the next tree in the direction of traversal.
Definition: tree.cpp:257
A dynamically linked tree with an arbitrary number of branches.
Definition: tree.h:40
int elements() const
the maximum number of elements currently allowed in this amorph.
Definition: amorph.h:66
contents * borrow(int field)
Returns a pointer to the information at the index "field".
Definition: amorph.h:448
#define SETUP_COMBO_LOGGER
a macro that retasks the program-wide logger as a combo_logger.
Definition: combo_logger.h:49
#define non_continuable_error(c, f, i)
an extra piece of information used, if available, in bounds_halt below.
#define NULL_POINTER
The value representing a pointer to nothing.
Definition: definitions.h:32
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:45
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
#define BASE_LOG(s)
const int MAX_FILE_SIZE
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
const int MEGABYTE
Number of bytes in a megabyte.
Definition: definitions.h:135
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition: functions.h:43
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
A dynamic container class that holds any kind of object via pointers.
Definition: amorph.h:55
#include <time.h>
Definition: earth_time.cpp:37