• Solutions
    • FERC XBRL Reporting
    • FDTA Financial Reporting
    • SEC Compliance
    • Windows Clipboard Management
    • Legato Scripting
  • Products
    • GoFiler Suite
    • XBRLworks
    • SEC Exhibit Explorer
    • SEC Extractor
    • Clipboard Scout
    • Legato
  • Education
    • Training
    • SEC and EDGAR Compliance
    • Legato Developers
  • Blog
  • Support
  • Skip to blog entries
  • Skip to archive page
  • Skip to right sidebar

Friday, December 22. 2017

LDC #64: Stripe tables, not candy canes!

Maybe you can stripe candy canes too, given it’s the season. However, this blog post is talking about striping tables! We had another feature request for GoFiler last week that we thought would make a good blog post. This request concerned developing a way for GoFiler to start striping the table after the first dollar sign in the table instead of at the normal body position that GoFiler detects. There are two ways we could do this: we could spend a lot of time modifying a core function of GoFiler, the Polish Table function, or we could make a Legato script that fires on post-process and re-does the striping of the table. The latter is preferable because it doesn’t affect other uses who perhaps don’t want or need this feature. Anyone who does can simply add this script to the extensions folder and that user can get the benefits of a new features.


 Here is our sample script: 




#define                 CURRENCY_SYMBOLS                "$\r€\r£\r¥"

    boolean             ppp_stripe_nq;
    boolean             do_ppp_stripe;
    
    void                run_ppp_stripe                  (int f_id, string mode);
    void                clear_table_row_colors          (handle sgml, handle edit_object, int sx, int sy);
    string              sgml_to_stripe_pos              (handle sgml);    


                                                                        /****************************************/
void setup() {                                                          /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* path to our script file              */
                                                                        /*                                      */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuSetHook("TABLE_CLEANUP_POLISH",fnScript,"run_ppp_stripe");      /* Set the Test Hook                    */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
void main(){                                                            /* main                                 */
                                                                        /****************************************/
    setup();                                                            /* run setup                            */
    }                                                                   /*                                      */
                                                                        /****************************************/
void run_ppp_stripe(int f_id, string mode){                             /* run_ppp_stripe                       */
                                                                        /****************************************/
    int                 c_x, c_y;                                       /* cursor x, y position                 */
    int                 t_ex,t_ey;                                      /* endpoints of table tag               */
    int                 rc;                                             /* return code                          */
    dword               remember;                                       /* remember checkbox result             */
    int                 token;                                          /* token                                */
    string              msg;                                            /* message to user                      */
    string              stripe_pos;                                     /* stiper position                      */
    string              element;                                        /* current sgml element                 */
    handle              sgml;                                           /* sgml parser                          */
    handle              edit_window;                                    /* handle to edit window                */
    handle              edit_object;                                    /* handle to edit object                */
                                                                        /*                                      */
    if(mode!="postprocess"){                                            /* if not running in post proces        */
      return;                                                           /* quit the script                      */
      }                                                                 /*                                      */
    if (ppp_stripe_nq != true){                                         /* if we're not in noquery mode         */
      msg = "Begin table striping on row with first currency symbol?";  /* set user msg                         */
      rc = YesNoRememberBox(msg);                                       /* query user                           */
      remember = GetLastError();                                        /* get checkbox result from user        */
      if (rc == IDYES){                                                 /* if user pressed yes                  */
        do_ppp_stripe = true;                                           /* set ppp stripe to true               */
        }                                                               /*                                      */
      else {                                                            /* if user didn't press yes             */
        do_ppp_stripe = false;                                          /* set ppp stripe to false              */
        }                                                               /*                                      */
      if (remember != ERROR_NONE){                                      /* if the user checked remember         */
        ppp_stripe_nq = true;                                           /* do not query next time run           */
        }                                                               /*                                      */
      }                                                                 /*                                      */
    if (do_ppp_stripe != true){                                         /* if user doesn't want this function   */
      return;                                                           /* quit                                 */
      }                                                                 /*                                      */
    edit_window = GetActiveEditWindow();                                /* get the active edit window           */
    edit_object = GetEditObject(edit_window);                           /* get the active edit object           */
    c_x = GetCaretXPosition(edit_object);                               /* get caret x position                 */
    c_y = GetCaretYPosition(edit_object);                               /* get caret y position                 */
    sgml = SGMLCreate(edit_object);                                     /* create SGML object                   */
    element = SGMLPreviousElement(sgml, c_x, c_y);                      /* get the previous element             */
    while (element!="" && token != HT_TABLE){                           /* while not at the table tag           */
      element = SGMLPreviousElement(sgml);                              /* get the previous element             */
      token = SGMLGetElementToken(sgml);                                /* get the token                        */
      }                                                                 /*                                      */
    t_ex = SGMLGetItemPosEX(sgml);                                      /* end pos of table                     */
    t_ey = SGMLGetItemPosEY(sgml);                                      /* end pos of table                     */
    stripe_pos = sgml_to_stripe_pos(sgml);                              /* get stripe start pos                 */
    if (stripe_pos!=""){                                                /* get SGML parser striping pos         */
      clear_table_row_colors(sgml,edit_object,t_ex,t_ey);               /* clear all background colors of rows  */
      c_x = GetParameter(stripe_pos,"sx");                              /* get caret x position                 */
      c_y = GetParameter(stripe_pos,"sy");                              /* get caret y position                 */
      CloseHandle(sgml);                                                /* close the sgml parser                */
      CloseHandle(edit_object);                                         /* close the handle                     */
      SetCaretPosition(edit_window, c_x, c_y);                          /* set caret position                   */
      CloseHandle(edit_window);                                         /* close the handle                     */
      RunMenuFunction("TABLE_STRIPING","NoQuery:true");                 /* run the table stripe function        */
      }                                                                 /*                                      */
    }                                                                   /*                                      */
                                                                        /****************************************/
string sgml_to_stripe_pos(handle sgml){                                 /* move SGML to row to stripe on        */
                                                                        /****************************************/
    string              symbols[];                                      /* symbols to stop on                   */
    string              stripe_pos;                                     /* striper position                     */
    string              item;                                           /* an item from the parser              */
    dword               token;                                          /* the token of the curent parse pos    */
                                                                        /*                                      */
    symbols = ExplodeString(CURRENCY_SYMBOLS);                          /* explode symbols to array             */
    item = SGMLNextItem(sgml);                                          /* get next item in parser              */
    while(item!="" && token!=HT__TABLE){                                /* while not at end of table            */
      token = SGMLGetElementToken(sgml);                                /* get token of item                    */
      if (SGMLGetItemType(sgml) == SPI_TYPE_TEXT ||                     /* if item is text or                   */
          SGMLGetItemType(sgml) == SPI_TYPE_CHAR){                      /* if item is char entity               */
        if (FindInList(symbols, item)>(-1)){                            /* if it's in the symbol list           */
          stripe_pos = FormatString("sx:%d;sy:%d",                      /* get position for striper             */
                SGMLGetItemPosEX(sgml), SGMLGetItemPosEY(sgml));        /* get position for striper             */
          return stripe_pos;                                            /* end here                             */
          }                                                             /*                                      */
        }                                                               /*                                      */
      item = SGMLNextItem(sgml);                                        /* next                                 */
      }                                                                 /*                                      */
    return "";                                                          /* return false                         */
    }
                                                                        /****************************************/
void clear_table_row_colors(handle sgml, handle edit_object,            /* clear all table row backgrounds      */
                                int s_sx, int s_sy){                    /****************************************/
    dword               token;                                          /* token of table item                  */
    int                 sx, sy, ex, ey;                                 /* points of table                      */
    string              element;                                        /* element text                         */
    string              style[];                                        /* style of element                     */
                                                                        /*                                      */
    element = SGMLNextElement(sgml,s_sx,s_sy);                          /* get next element                     */
    while (element!="" && token!=HT__TABLE){                            /* while we have another element        */
      token = SGMLGetElementToken(sgml);                                /* get token                            */
      if (token==HT_TR){                                                /* if we're on a table row              */
        sx = SGMLGetItemPosSX(sgml);                                    /* get positions of tag                 */
        ex = SGMLGetItemPosEX(sgml);                                    /* get positions of tag                 */
        sy = SGMLGetItemPosSY(sgml);                                    /* get positions of tag                 */
        ey = SGMLGetItemPosEY(sgml);                                    /* get positions of tag                 */
        style = CSSGetProperties(sgml);                                 /* get CSS properties                   */
        style["background-color"] = "";                                 /* set background to blank              */
        CSSSetProperties(sgml,style);                                   /* set style of tag                     */
        element = SGMLToString(sgml);                                   /* get text of current tag              */
        WriteSegment(edit_object,element,sx,sy,ex,ey);                  /* write out new row tag                */
        SGMLSetPosition(sgml,ex,ey);                                    /* set parser position                  */
        }                                                               /*                                      */
      element = SGMLNextElement(sgml);                                  /* get next element                     */
      }                                                                 /*                                      */
    SGMLSetPosition(sgml,s_sx,s_sy);                                    /* put it back where we found it        */
    }                                                                   /*                                      */


Let’s start with our defines and global variables. CURRENCY_SYMBOLS is a string that contains all of our recognized symbols for currency. The contents can be changed to put any other currency types you want in it or remove those you don’t. We separated the symbols with the \r new line character, but you can use any delimiter you wish. The ExplodeString function is going to take it and turn it into an array so we can use the FindInList function to search through it. The global variables ppp_stripe_nq and do_ppp_stripe are used to keep track of our user input. The function will ask the user if he or she wants to stripe at the currency symbol and if the response should be remembered. ppp_stripe_nq (named for Polish Post Process No Query; often descriptive variable names help visualize what’s going on) remembers if the user checked the remember box and do_ppp_stripe is set to true if the user clicks “yes” on that dialog. The setup function is barebones this time; we only need to set the hook to the TABLE_CLEANUP_POLISH function. It also runs our run_ppp_stripe function afterwards. The main function simply runs setup so we can hook it manually from the IDE if we want.



#define                 CURRENCY_SYMBOLS                "$\r€\r£\r¥"

    boolean             ppp_stripe_nq;
    boolean             do_ppp_stripe;
    
    void                run_ppp_stripe                  (int f_id, string mode);
    void                clear_table_row_colors          (handle sgml, handle edit_object, int sx, int sy);
    string              sgml_to_stripe_pos              (handle sgml);    


                                                                        /****************************************/
void setup() {                                                          /* Called from Application Startup      */
                                                                        /****************************************/
    string              fnScript;                                       /* path to our script file              */
                                                                        /*                                      */
    fnScript = GetScriptFilename();                                     /* Get the script filename              */
    MenuSetHook("TABLE_CLEANUP_POLISH",fnScript,"run_ppp_stripe");      /* Set the Test Hook                    */
    }                                                                   /* end setup                            */
                                                                        /****************************************/
void main(){                                                            /* main                                 */
                                                                        /****************************************/
    setup();                                                            /* run setup                            */
    }                                                                   /*                                      */



The primary function is run_ppp_stripe. This function is run when our hooked function, polish table, is run. We only want it to run post process, though, to clean up our striping, so we need to check if the mode is anything other than postprocess first and return if it is. Next, we check if the user has enabled no query mode by checking the variable ppp_stripe_nq. If not, we can ask the user for input. The YesNoRemember box sets last error dword to whatever the response is from the user, so we can check that using the GetLastError function. If the response is IDYES, we set do_ppp_stripe to true. Otherwise, it’s false. If the YesNoRemember box is anything other than ERROR_NONE, it means they checked the box, so we should set no query mode to true.



                                                                        /****************************************/
void run_ppp_stripe(int f_id, string mode){                             /* run_ppp_stripe                       */
                                                                        /****************************************/
    int                 c_x, c_y;                                       /* cursor x, y position                 */
    int                 t_ex,t_ey;                                      /* endpoints of table tag               */
    int                 rc;                                             /* return code                          */
    dword               remember;                                       /* remember checkbox result             */
    int                 token;                                          /* token                                */
    string              msg;                                            /* message to user                      */
    string              stripe_pos;                                     /* stiper position                      */
    string              element;                                        /* current sgml element                 */
    handle              sgml;                                           /* sgml parser                          */
    handle              edit_window;                                    /* handle to edit window                */
    handle              edit_object;                                    /* handle to edit object                */
                                                                        /*                                      */
    if(mode!="postprocess"){                                            /* if not running in post proces        */
      return;                                                           /* quit the script                      */
      }                                                                 /*                                      */
    if (ppp_stripe_nq != true){                                         /* if we're not in noquery mode         */
      msg = "Begin table striping on row with first currency symbol?";  /* set user msg                         */
      rc = YesNoRememberBox(msg);                                       /* query user                           */
      remember = GetLastError();                                        /* get checkbox result from user        */
      if (rc == IDYES){                                                 /* if user pressed yes                  */
        do_ppp_stripe = true;                                           /* set ppp stripe to true               */
        }                                                               /*                                      */
      else {                                                            /* if user didn't press yes             */
        do_ppp_stripe = false;                                          /* set ppp stripe to false              */
        }                                                               /*                                      */
      if (remember != ERROR_NONE){                                      /* if the user checked remember         */
        ppp_stripe_nq = true;                                           /* do not query next time run           */
        }                                                               /*                                      */
      }                                                                 /*                                      */


If the user picked “yes” the last time they were queried, we can continue on and retrieve handles to the edit window and edit objects. Using the edit object, we can get the current caret position to find where the cursor is, create an SGML object, and parse backwards until we hit a table tag or run out of elements to parse. Once we find a table, we need to get to its start position. Once there, we can store the end positions of our table tag for future reference as t_ex and t_ey. Next we can run our sgml_to_stripe_pos function to get the location in the table where our striping should begin. If it’s not blank, we found a currency symbol, so we can redo our table striping.


First we need to run clear_table_row_colors to set all table striping back to default values. Then we can get the X and Y positions that sgml_to_stripe_pos defined for us with the GetParameter function. Importantly, we must use the CloseHandle function on the SGML parser and on the edit object. If those handles are open, we cannot use the RunMenuFunction function because until Legato releases those handles there’s an open transaction still happening and a new one cannot be started. Once they are closed, we can use the SetCaretPosition function to set the location of the caret to the end of the currency symbol. The Table Stripe function can then pick this position up, and we use the RunMenuFunction function to actually call the table stripe function. It’s being used in NoQuery mode, so it will simply stripe starting from our cursor position with default options.


    
    if (do_ppp_stripe != true){                                         /* if user doesn't want this function   */
      return;                                                           /* quit                                 */
      }                                                                 /*                                      */
    edit_window = GetActiveEditWindow();                                /* get the active edit window           */
    edit_object = GetEditObject(edit_window);                           /* get the active edit object           */
    c_x = GetCaretXPosition(edit_object);                               /* get caret x position                 */
    c_y = GetCaretYPosition(edit_object);                               /* get caret y position                 */
    sgml = SGMLCreate(edit_object);                                     /* create SGML object                   */
    element = SGMLPreviousElement(sgml, c_x, c_y);                      /* get the previous element             */
    while (element!="" && token != HT_TABLE){                           /* while not at the table tag           */
      element = SGMLPreviousElement(sgml);                              /* get the previous element             */
      token = SGMLGetElementToken(sgml);                                /* get the token                        */
      }                                                                 /*                                      */
    t_ex = SGMLGetItemPosEX(sgml);                                      /* end pos of table                     */
    t_ey = SGMLGetItemPosEY(sgml);                                      /* end pos of table                     */
    stripe_pos = sgml_to_stripe_pos(sgml);                              /* get stripe start pos                 */
    if (stripe_pos!=""){                                                /* get SGML parser striping pos         */
      clear_table_row_colors(sgml,edit_object,t_ex,t_ey);               /* clear all background colors of rows  */
      c_x = GetParameter(stripe_pos,"sx");                              /* get caret x position                 */
      c_y = GetParameter(stripe_pos,"sy");                              /* get caret y position                 */
      CloseHandle(sgml);                                                /* close the sgml parser                */
      CloseHandle(edit_object);                                         /* close the handle                     */
      SetCaretPosition(edit_window, c_x, c_y);                          /* set caret position                   */
      CloseHandle(edit_window);                                         /* close the handle                     */
      RunMenuFunction("TABLE_STRIPING","NoQuery:true");                 /* run the table stripe function        */
      }                                                                 /*                                      */
    }                                                                   /*                                      */


The sgml_to_stripe_pos function is pretty simple. It returns a parameter string of the coordinates of the first currency symbol that it finds in the table that matches one of our defined symbols. If it can’t find any of the symbols, the function returns an empty string. The first thing this function does is use the ExplodeString function on our defined currency symbols to get an array of symbols against which we can check. Then our function gets the next item in the SGML parser. We use the SGMLNextItem function instead of the SGMLNextElement function as we did last week. This is because we’re looking for characters, not SGML tags. While our item isn’t blank and we haven’t hit the end of our table, we can get the token of our item and check if the type is a textual word or a character entity. If it’s either, we can use the FindInList function to see if the item we found is indeed a currency symbol. If so, we can return our parameter string containing the end positions of our currency symbol, which will become the start positions for the table striper to run on. Otherwise, we can keep on parsing until we hit the end of our table and return nothing.



                                                                        /****************************************/
string sgml_to_stripe_pos(handle sgml){                                 /* move SGML to row to stripe on        */
                                                                        /****************************************/
    string              symbols[];                                      /* symbols to stop on                   */
    string              stripe_pos;                                     /* striper position                     */
    string              item;                                           /* an item from the parser              */
    dword               token;                                          /* the token of the curent parse pos    */
                                                                        /*                                      */
    symbols = ExplodeString(CURRENCY_SYMBOLS);                          /* explode symbols to array             */
    item = SGMLNextItem(sgml);                                          /* get next item in parser              */
    while(item!="" && token!=HT__TABLE){                                /* while not at end of table            */
      token = SGMLGetElementToken(sgml);                                /* get token of item                    */
      if (SGMLGetItemType(sgml) == SPI_TYPE_TEXT ||                     /* if item is text or                   */
          SGMLGetItemType(sgml) == SPI_TYPE_CHAR){                      /* if item is char entity               */
        if (FindInList(symbols, item)>(-1)){                            /* if it's in the symbol list           */
          stripe_pos = FormatString("sx:%d;sy:%d",                      /* get position for striper             */
                SGMLGetItemPosEX(sgml), SGMLGetItemPosEY(sgml));        /* get position for striper             */
          return stripe_pos;                                            /* end here                             */
          }                                                             /*                                      */
        }                                                               /*                                      */
      item = SGMLNextItem(sgml);                                        /* next                                 */
      }                                                                 /*                                      */
    return "";                                                          /* return false                         */
    }


The clear_table_row_colors function does exactly what it sounds like: it iterates over every row of a table from the coordinates given to it and sets the background color to nothing. This function then can use the SGMLNextElement function to parse through instead of the SGMLNextItem function, because we really only care about table row tags. While we’re not at the end of our table then, we can use the SGMLGetElementToken function to check if we’re on a table row. If so, we can get the tag positions, use our CSS functions to get and modify the style. The WriteSegment function can write our changes back out. Finally we can use the SGMLSetPosition function to move the parser back to the start point.



                                                                        /****************************************/
void clear_table_row_colors(handle sgml, handle edit_object,            /* clear all table row backgrounds      */
                                int s_sx, int s_sy){                    /****************************************/
    dword               token;                                          /* token of table item                  */
    int                 sx, sy, ex, ey;                                 /* points of table                      */
    string              element;                                        /* element text                         */
    string              style[];                                        /* style of element                     */
                                                                        /*                                      */
    element = SGMLNextElement(sgml,s_sx,s_sy);                          /* get next element                     */
    while (element!="" && token!=HT__TABLE){                            /* while we have another element        */
      token = SGMLGetElementToken(sgml);                                /* get token                            */
      if (token==HT_TR){                                                /* if we're on a table row              */
        sx = SGMLGetItemPosSX(sgml);                                    /* get positions of tag                 */
        ex = SGMLGetItemPosEX(sgml);                                    /* get positions of tag                 */
        sy = SGMLGetItemPosSY(sgml);                                    /* get positions of tag                 */
        ey = SGMLGetItemPosEY(sgml);                                    /* get positions of tag                 */
        style = CSSGetProperties(sgml);                                 /* get CSS properties                   */
        style["background-color"] = "";                                 /* set background to blank              */
        CSSSetProperties(sgml,style);                                   /* set style of tag                     */
        element = SGMLToString(sgml);                                   /* get text of current tag              */
        WriteSegment(edit_object,element,sx,sy,ex,ey);                  /* write out new row tag                */
        SGMLSetPosition(sgml,ex,ey);                                    /* set parser position                  */
        }                                                               /*                                      */
      element = SGMLNextElement(sgml);                                  /* get next element                     */
      }                                                                 /*                                      */
    SGMLSetPosition(sgml,s_sx,s_sy);                                    /* put it back where we found it        */
    }                                                                   /*                                      */


Legato is very useful for creating entirely new functions, but this script shows how it can also be useful to modify existing ones to do new things. Instead of reinventing the wheel, we can simply hook a post process script onto our existing polish table function and give it a brand new level of functionality. We could go further with this, of course, by adding a pre-process options menu that overrides the existing options and only presents the user with choices we want to give them. Then we could pass those settings to the actual polish function and have a completely customized polish table. This would be a significant amount of work though, because the UI for the table polish tool is pretty complex, but it definitely could be done if needed. This is just another example of how you can customize your GoFiler with Legato to get the best results possible.


 


Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC.

Additional Resources

Novaworks’ Legato Resources

Legato Script Developers LinkedIn Group

Primer: An Introduction to Legato 



Posted by
Steven Horowitz
in Development at 10:47
Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)
No comments
Add Comment
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
   
 

Quicksearch

Categories

  • XML Accounting
  • XML AICPA News
  • XML FASB News
  • XML GASB News
  • XML IASB News
  • XML Development
  • XML Events
  • XML FERC
  • XML eForms News
  • XML FERC Filing Help
  • XML Filing Technology
  • XML Information Technology
  • XML Investor Education
  • XML MSRB
  • XML EMMA News
  • XML FDTA
  • XML MSRB Filing Help
  • XML Novaworks News
  • XML GoFiler Online Updates
  • XML GoFiler Updates
  • XML XBRLworks Updates
  • XML SEC
  • XML Corporation Finance
  • XML DERA
  • XML EDGAR News
  • XML Investment Management
  • XML SEC Filing Help
  • XML XBRL
  • XML Data Quality Committee
  • XML GRIP Taxonomy
  • XML IFRS Taxonomy
  • XML US GAAP Taxonomy

Calendar

Back May '25 Forward
Mo Tu We Th Fr Sa Su
Sunday, May 18. 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Feeds

  • XML
Sign Up Now
Get SEC news articles and blog posts delivered monthly to your inbox!
Based on the s9y Bulletproof template framework

Compliance

  • FERC
  • EDGAR
  • EMMA

Software

  • GoFiler Suite
  • SEC Exhibit Explorer
  • SEC Extractor
  • XBRLworks
  • Legato Scripting

Company

  • About Novaworks
  • News
  • Site Map
  • Support

Follow Us:

  • LinkedIn
  • YouTube
  • RSS
  • Newsletter
  • © 2024 Novaworks, LLC
  • Privacy
  • Terms of Use
  • Trademarks and Patents
  • Contact Us