marks_maker_js.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : marks_maker_javascript                                            *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *  Purpose:                                                                   *
00007 *                                                                             *
00008 *    Turns a link database in HOOPLE format into a web page, when given a     *
00009 *  suitable template file.  The template file must have the phrase:           *
00010 *        $INSERT_LINKS_HERE                                                   *
00011 *  at the point where the generated links are supposed to be stored.          *
00012 *                                                                             *
00013 *******************************************************************************
00014 * Copyright (c) 2005-$now By Author.  This program is free software; you can  *
00015 * redistribute it and/or modify it under the terms of the GNU General Public  *
00016 * License as published by the Free Software Foundation; either version 2 of   *
00017 * the License or (at your option) any later version.  This is online at:      *
00018 *     http://www.fsf.org/copyleft/gpl.html                                    *
00019 * Please send any updates to: fred@gruntose.com                               *
00020 \*****************************************************************************/
00021 
00022 #include "bookmark_tree.cpp"
00023 
00024 #include <basis/function.h>
00025 #include <basis/guards.h>
00026 #include <basis/istring.h>
00027 #include <data_struct/stack.cpp>
00028 #include <opsystem/application_shell.h>
00029 #include <opsystem/byte_filer.h>
00030 #include <opsystem/command_line.h>
00031 #include <loggers/file_logger.h>
00032 #include <opsystem/filename.h>
00033 #include <data_struct/static_memory_gremlin.h>
00034 #include <textual/list_parsing.h>
00035 
00036 using namespace nodes;
00037 
00038 //#define DEBUG_MARKS
00039   // uncomment to have more debugging noise.
00040 
00041 #undef BASE_LOG
00042 #define BASE_LOG(s) program_wide_logger().log(s)
00043 #undef LOG
00044 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger(), \
00045    isprintf("line %d: ", _categories._line_number) + s)
00046 
00047 const int MAX_FILE_SIZE = 4 * MEGABYTE;
00048   // the largest file we'll read.
00049 
00051 
00052 class marks_maker_javascript : public application_shell
00053 {
00054 public:
00055   marks_maker_javascript()
00056       : application_shell(static_class_name()), _need_closure(false),
00057         _loader_count(0), _link_spool(0), _functions_pending(0) {}
00058   IMPLEMENT_CLASS_NAME("marks_maker_javascript");
00059   virtual int execute();
00060   int print_instructions(const filename &program_name);
00061 
00062   int write_marks_page(const istring &output_filename,
00063           const istring &template_filename);
00064     // given a tree of links, this writes out a web page to "output_filename"
00065     // using a template file "template_filename".
00066 
00067 private:
00068   bookmark_tree _categories;  // our tree of categories.
00069   bool _need_closure;  // true if our <div> needs a closure.
00070   int _loader_count;  // count of the loader functions.
00071   int _link_spool;  // count of which link we're writing.
00072   stack<istring> _functions_pending;  // used for javascript node functions.
00073 
00074 //this needs to gather any strings that would have gone into functions.
00075 //instead, they need to be written into the current node's string.
00076 //when a new function def would be seen, then we need to push a new node
00077 //for accumulating the text.
00078 
00079   // these handle outputting text for categories and links.
00080   void write_category(inner_mark_tree *node, istring &output);
00081   void write_link(inner_mark_tree *node, const link_record &linko,
00082          istring &output);
00083 };
00084 
00086 
00087 int marks_maker_javascript::print_instructions(const filename &program_name)
00088 {
00089   isprintf to_show("%s:\n\
00090 This program needs three filenames as command line parameters.  The -i flag\n\
00091 is used to specify the input filename, the -t flag specifies a template web\n\
00092 page which is used as the wrapper around the links, and the -o flag specifies\n\
00093 the web page to be created.  The input file is expected to be in the HOOPLE\n\
00094 link database format.  The output file will be created from the template file\n\
00095 by finding the phrase $INSERT_LINKS_HERE in it and replacing that with html\n\
00096 formatted link and categories from the input file.  Another tag of $TODAYS_DATE\n\
00097 will be replaced with the date when the output file is regenerated.\n\
00098 The HOOPLE link format is documented here:\n\
00099     http://hoople.org/guides/link_database/format_manifesto.txt\n\
00100 ", program_name.basename().raw().s(), program_name.basename().raw().s());
00101   program_wide_logger().log(to_show.s());
00102   return 12;
00103 }
00104 
00105 void marks_maker_javascript::write_category(inner_mark_tree *node, istring &output)
00106 {
00107   FUNCDEF("write_category");
00108   // output a javascript line for the category.
00109 
00110   int node_num = node->_uid;
00111   inner_mark_tree *parent = dynamic_cast<inner_mark_tree *>(node->parent());
00112   int parent_node = parent? parent->_uid : -1;
00113     // the parent node for root is a negative one.
00114   istring chewed_name = node->name();
00115   for (int i = chewed_name.end(); i >= 0; i--) {
00116     // escape any raw single quotes that we see.
00117     if (chewed_name[i] == '\'') {
00118       chewed_name.zap(i, i);
00119       chewed_name.insert(i, "\\'");
00120     }
00121   }
00122   output += isprintf("  b.add(%d, %d, '%s');\n", node_num, parent_node,
00123       chewed_name.s());
00124 }
00125 
00126 void marks_maker_javascript::write_link(inner_mark_tree *node, const link_record &linko, 
00127     istring &output)
00128 {
00129   FUNCDEF("write_link");
00130   // write a javascript link definition.
00131   int parent_node = node->_uid;
00132   istring chewed_name = linko._description;
00133   for (int i = chewed_name.end(); i >= 0; i--) {
00134     // escape any raw single quotes that we see.
00135     if (chewed_name[i] == '\'') {
00136       chewed_name.zap(i, i);
00137       chewed_name.insert(i, "\\'");
00138     }
00139   }
00140 
00141   if (!linko._url) {
00142     // this just appears to be a comment line.
00143     if (!linko._description) return;  // it's a nothing line.
00144 
00145 /*
00146 //hmmm: probably not what we want.
00147 //hmmm: why not, again?
00148     output += linko._description;
00149     output += "<br>";
00150     output += log_base::platform_ending();
00151 */
00152     return;
00153   }
00154 
00155   // generate a function header if the number of links is a multiple of 100.
00156   if (! (_link_spool % 100) ) {
00157     if (_link_spool) {
00158       // close out the previous function and set a timeout.
00159       output += "  setTimeout('run_tree_loaders()', 0);\n";
00160       output += "}\n";
00161     }
00162 
00163     output += isprintf("function tree_loader_%d() {\n", _loader_count++);
00164   }
00165   _link_spool++;
00166 
00167   output += isprintf("  b.add(%d, %d, '%s', '%s');\n",
00168       linko._uid, parent_node, chewed_name.s(), linko._url.s());
00169 }
00170 
00171 int marks_maker_javascript::execute()
00172 {
00173   FUNCDEF("execute");
00174   SET_DEFAULT_COMBO_LOGGER;
00175 
00176   command_line cmds(__argc, __argv);  // process the command line parameters.
00177   istring input_filename;  // we'll store our link database name here.
00178   istring output_filename;  // where the web page we're creating goes.
00179   istring template_filename;  // the wrapper html code that we'll stuff.
00180   if (!cmds.get_value('i', input_filename, false))
00181     return print_instructions(cmds.program_name());
00182   if (!cmds.get_value('o', output_filename, false))
00183     return print_instructions(cmds.program_name());
00184   if (!cmds.get_value('t', template_filename, false))
00185     return print_instructions(cmds.program_name());
00186 
00187   BASE_LOG(istring("input file: ") + input_filename);
00188   BASE_LOG(istring("output file: ") + output_filename);
00189   BASE_LOG(istring("template file: ") + template_filename);
00190 
00191   int ret = _categories.read_csv_file(input_filename);
00192   if (ret) return ret;
00193 
00194   ret = write_marks_page(output_filename, template_filename);
00195   if (ret) return ret;
00196   
00197   return 0;
00198 }
00199 
00200 int marks_maker_javascript::write_marks_page(const istring &output_filename,
00201     const istring &template_filename)
00202 {
00203   FUNCDEF("write_marks_page");
00204   istring long_string;
00205     // this is our accumulator of links.  it is the semi-final result that will
00206     // be injected into the template file.
00207 
00208   // add our target layer so that we can write to a useful place.
00209   long_string += "<div class=\"marks_target\" id=\"martarg\">Marks Target</div>\n";
00210 
00211   // add the tree style and creation of the tree object into the text.
00212   long_string += "\n<div class=\"dtree\">\n";
00213   long_string += "<script type=\"text/javascript\">\n";
00214   long_string += "<!--\n";
00215 
00216   long_string += "function open_mark(url) {\n";
00217   long_string += "  window.open(url, '', '');\n";
00218   long_string += "}\n";
00219 
00220   // the list of functions is used for calling into the tree loaders
00221   // without blocking.
00222   long_string += "  b = new dTree('b');\n";
00224   long_string += "  b.config.useCookies = false;\n";
00225   long_string += "  b.config.folderLinks = false;\n";
00226 
00227   // traverse the tree in prefix order.
00228   tree::iterator itty = _categories.access_root().start(tree::prefix);
00229   tree *curr = NIL;
00230   while ( (curr = itty.next()) ) {
00231     inner_mark_tree *nod = (inner_mark_tree *)curr;
00232     // print out the category on this node.
00233     write_category(nod, long_string);
00234 
00235     // print the link for all of the ones stored at this node.
00236     for (int i = 0; i < nod->_links.elements(); i++) {
00237       link_record *lin = nod->_links.borrow(i);
00238       write_link(nod, *lin, long_string);
00239     }
00240   }
00241 
00242   // close the block of script in the output.
00243   long_string += "  setTimeout('run_tree_loaders()', 0);\n";
00244   long_string += "}\n\n";
00245 
00246   long_string += isprintf("function tree_loader_%d()"
00247       "{ setTimeout('run_tree_loaders()', 0); }\n", _loader_count++);
00248 
00249   long_string += "\nconst max_funcs = 1000;\n";
00250   long_string += "var loader_functions = new Array(max_funcs);\n";
00251   long_string += "var curr_func = 0;\n";
00252   long_string += "var done_rendering = false;\n\n";
00253 
00254   long_string += isprintf("for (var i = 0; i < %d; i++) {\n", _loader_count);
00255   long_string += "  loader_functions[curr_func++] "
00256         "= 'tree_loader_' + i + '()';\n";
00257   long_string += "}\n";
00258 
00259   long_string += "var run_index = 0;\n";
00260   long_string += "function run_tree_loaders() {\n";
00261   long_string += "  if (done_rendering) return;\n";
00262   long_string += "  if (run_index >= curr_func) {\n";
00263 
00264   long_string += "    if (document.getElementById) {\n";
00265   long_string += "      x = document.getElementById('martarg');\n";
00266   long_string += "      x.innerHTML = '';\n";
00267   long_string += "      x.innerHTML = b;\n";
00268   long_string += "    } else { document.write(b); }\n";
00269 //not a very graceful degradation.  we should use the other options from:
00270 // http://www.quirksmode.org/js/layerwrite.html
00271   long_string += "    done_rendering = true;\n";
00272   long_string += "    return;\n";
00273   long_string += "  }\n";
00274   long_string += "  var next_func = loader_functions[run_index++];\n";
00275   long_string += "  setTimeout(next_func, 0);\n";
00276   long_string += "}\n";
00277 
00278   long_string += isprintf("  run_tree_loaders();\n", _loader_count);
00279 
00280   long_string += "//-->\n";
00281   long_string += "</script>\n";
00282   long_string += "<p><a href=\"javascript: b.openAll();\">open all</a> | "
00283       "<a href=\"javascript: b.closeAll();\">close all</a></p>\n";
00284   long_string += "</div>\n";
00285 
00286   byte_filer template_file(template_filename, "r");
00287   istring full_template;
00288   if (!template_file.good())
00289     non_continuable_error(class_name(), func, "the template file could not be opened");
00290   template_file.read(full_template, MAX_FILE_SIZE);
00291   template_file.close();
00292 
00293   // javascript output needs some extra junk added to the header section.
00294   int indy = full_template.ifind("</title>");
00295   if (negative(indy))
00296     non_continuable_error(class_name(), func, "the template file is missing "
00297         "a <head> declaration");
00298 //hmmm: the path here must be configurable!
00299   full_template.insert(indy + 8, "\n\n"
00300       "<link rel=\"StyleSheet\" href=\"/yeti/javascript/dtree/dtree.css\" "
00301           "type=\"text/css\" />\n"
00302       "<script type=\"text/javascript\" src=\"/yeti/javascript/"
00303           "dtree/dtree.js\"></script>\n");
00304 
00305   // replace the tag with the long string we created.
00306   bool found_it = full_template.replace("$INSERT_LINKS_HERE", long_string);
00307   if (!found_it)
00308     non_continuable_error(class_name(), func, "the template file is missing "
00309         "the insertion point");
00310   full_template.replace("$TODAYS_DATE", timestamp(false, true));
00311 
00312   filename outname(output_filename);
00313   if (outname.exists()) {
00314     non_continuable_error(class_name(), func, istring("the output file ")
00315         + output_filename + " already exists.  It would be over-written if "
00316         "we continued.");
00317   }
00318 
00319   byte_filer output_file(output_filename, "w");
00320   if (!output_file.good())
00321     non_continuable_error(class_name(), func, "the output file could not be opened");
00322   // write the newly generated web page out now.
00323   output_file.write(full_template);
00324   output_file.close();
00325 
00326 // show the tree.
00327 //  BASE_LOG("");
00328 //  BASE_LOG(_categories.access_root().text_form());
00329 
00330   BASE_LOG(isprintf("wrote %d links in %d categories.",
00331       _categories.link_count(), _categories.category_count()));
00332   BASE_LOG("");
00333 
00334   return 0;
00335 }
00336 
00338 
00339 HOOPLE_MAIN(marks_maker_javascript, )
00340 
00341 #ifdef __BUILD_STATIC_APPLICATION__
00342   // static dependencies found by buildor_gen_deps.sh:
00343   #include <basis/array.cpp>
00344   #include <basis/byte_array.cpp>
00345   #include <basis/callstack_tracker.cpp>
00346   #include <basis/chaos.cpp>
00347   #include <basis/convert_utf.cpp>
00348   #include <basis/definitions.cpp>
00349   #include <basis/earth_time.cpp>
00350   #include <basis/guards.cpp>
00351   #include <basis/istring.cpp>
00352   #include <basis/log_base.cpp>
00353   #include <basis/memory_checker.cpp>
00354   #include <basis/mutex.cpp>
00355   #include <basis/object_base.cpp>
00356   #include <basis/outcome.cpp>
00357   #include <basis/packable.cpp>
00358   #include <basis/portable.cpp>
00359   #include <basis/sequence.cpp>
00360   #include <basis/set.cpp>
00361   #include <basis/utility.cpp>
00362   #include <basis/version_record.cpp>
00363   #include <data_struct/amorph.cpp>
00364   #include <data_struct/bit_vector.cpp>
00365   #include <data_struct/byte_hasher.cpp>
00366   #include <data_struct/configurator.cpp>
00367   #include <data_struct/hash_table.cpp>
00368   #include <data_struct/pointer_hash.cpp>
00369   #include <data_struct/stack.cpp>
00370   #include <data_struct/static_memory_gremlin.cpp>
00371   #include <data_struct/string_hash.cpp>
00372   #include <data_struct/string_hasher.cpp>
00373   #include <data_struct/string_table.cpp>
00374   #include <data_struct/symbol_table.cpp>
00375   #include <data_struct/table_configurator.cpp>
00376   #include <loggers/console_logger.cpp>
00377   #include <loggers/file_logger.cpp>
00378   #include <loggers/locked_logger.cpp>
00379   #include <loggers/null_logger.cpp>
00380   #include <loggers/program_wide_logger.cpp>
00381   #include <nodes/node.cpp>
00382   #include <nodes/path.cpp>
00383   #include <nodes/symbol_tree.cpp>
00384   #include <nodes/tree.cpp>
00385   #include <opsystem/application_base.cpp>
00386   #include <opsystem/application_shell.cpp>
00387   #include <opsystem/byte_filer.cpp>
00388   #include <opsystem/command_line.cpp>
00389   #include <opsystem/critical_events.cpp>
00390   #include <opsystem/directory.cpp>
00391   #include <opsystem/filename.cpp>
00392   #include <opsystem/ini_config.cpp>
00393   #include <opsystem/ini_parser.cpp>
00394   #include <opsystem/path_configuration.cpp>
00395   #include <opsystem/rendezvous.cpp>
00396   #include <textual/byte_format.cpp>
00397   #include <textual/list_parsing.cpp>
00398   #include <textual/parser_bits.cpp>
00399   #include <textual/string_manipulation.cpp>
00400   #include <textual/tokenizer.cpp>
00401 #endif // __BUILD_STATIC_APPLICATION__
00402 

Generated on Fri Nov 28 04:28:49 2008 for HOOPLE Libraries by  doxygen 1.5.1