Skip to content

Instantly share code, notes, and snippets.

@daimor
Last active November 4, 2018 10:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daimor/ece6ec04866f76e18efa8d94dd7e8e0c to your computer and use it in GitHub Desktop.
Save daimor/ece6ec04866f76e18efa8d94dd7e8e0c to your computer and use it in GitHub Desktop.
Cache Javascript
/// Studio Template:<br>
/// Create a new Cache JavaScript Routine.
Class %CJS.RoutineWizard Extends %ZEN.Template.studioTemplate [ StorageStrategy = "" ]
{
Parameter TEMPLATENAME = "Cache JavaScript";
Parameter TEMPLATETITLE = "Cache JavaScript";
Parameter TEMPLATEDESCRIPTION = "Create a new Cache JavaScript routine.";
Parameter TEMPLATETYPE = "CJS";
/// What type of template.
Parameter TEMPLATEMODE = "new";
/// If this is a TEMPLATEMODE="new" then this is the name of the tab
/// in Studio this template is dispayed on. If none specified then
/// it displays on 'Custom' tab.
Parameter TEMPLATEGROUP As STRING;
/// This XML block defines the contents of the body pane of this Studio Template.
XData templateBody [ XMLNamespace = "http://www.intersystems.com/zen" ]
{
<pane id="body">
<vgroup labelPosition="left" cellStyle="padding: 2px; padding-left: 5px; padding-right: 5px;">
<html id="desc" OnDrawContent="%GetDescHTML"/>
<text label="Routine Name:"
id="ctrlRoutineName"
name="RoutineName"
size="40"
required="true"
labelClass="zenRequired"
title="Cache JavaScript Routine name"
onchange="zenPage.updateState();"
/>
</vgroup>
</pane>
}
/// Provide contents of description component.
Method %GetDescHTML(pSeed As %String) As %Status
{
Quit $$$OK
}
/// This is called when the template is first displayed;
/// This provides a chance to set focus etc.
ClientMethod onstartHandler() [ Language = javascript ]
{
// give focus to name
var ctrl = zenPage.getComponentById('ctrlRoutineName');
if (ctrl) {
ctrl.focus();
ctrl.select();
}
}
/// Validation handler for form built-into template.
ClientMethod formValidationHandler() [ Language = javascript ]
{
var rtnName = zenPage.getComponentById('ctrlRoutineName').getValue();
if ('' == rtnName) {
return false;
}
return true;
}
/// This method is called when the template is complete. Any
/// output to the principal device is returned to the Studio.
Method %OnTemplateAction() As %Status
{
Set tRoutineName = ..%GetValueByName("RoutineName")
Set %session.Data("Template","NAME") = tRoutineName_".CJS"
Write "// "_tRoutineName,!
Quit $$$OK
}
}
Class %CJS.StudioRoutines Extends %Studio.AbstractDocument [ StorageStrategy = "" ]
{
/// The extension name, this can be a comma separated list of extensions if this class supports more than one
Projection RegisterExtension As %Projection.StudioDocument(DocumentDescription = "CachéJavaScript Routine", DocumentExtension = "cjs", DocumentIcon = 1, DocumentType = "JS");
Property ShortName As %String;
/// Set the name property
Method %OnNew(name As %String) As %Status [ Private ]
{
set ..ShortName=$piece(name,".",1,$length(name,".")-1)
set tSC=##super(name)
if $$$ISERR(tSC) Quit tSC
quit $$$OK
}
/// Load the routine in Name into the stream Code
Method Load() As %Status
{
set source=..Code
do source.Clear()
set pCodeGN=$name(^rCJS(..ShortName,0))
for pLine=1:1:$get(@pCodeGN@(0),0) {
do source.WriteLine(@pCodeGN@(pLine))
}
do source.Rewind()
Quit $$$OK
}
/// CompileDocument is called when the document is to be compiled
/// It has already called the source control hooks at this point
Method CompileDocument(ByRef qstruct As %String) As %Status
{
Write !,"Compile: ",..Name
Set compiledCode=##class(%Routine).%OpenId(..ShortName_".INT")
Set compiledCode.Generated=1
do compiledCode.Clear()
do compiledCode.WriteLine(" ;generated at "_$zdatetime($ztimestamp,3))
do ..GenerateIntCode(compiledCode)
do compiledCode.%Save()
do compiledCode.Compile()
Quit $$$OK
}
Method GenerateIntCode(aCode) [ Internal ]
{
set varMatcher=##class(%Regex.Matcher).%New("[ \t]*(var[ \t]+)?(\w[\w\d]*)[ \t]*(\=[ \t]*(.*))?")
set printlnMatcher=##class(%Regex.Matcher).%New("[ \t]*(?:console\.log|println)\(([^\)]+)\)?")
set readMatcher=##class(%Regex.Matcher).%New("[ \t]*read\((.*)\,(.*)\)")
set source=..Code
do source.Rewind()
while 'source.AtEnd {
set tLine=source.ReadLine()
set pos=1
while $locate(tLine,"(([^\'\""\;\r\n]|[\'\""][^\'\""]*[\'\""])+)",pos,pos,tCode) {
set tPos=1
if $zstrip(tCode,"*W")="" {
do aCode.WriteLine(tCode)
continue
}
if varMatcher.Match(tCode) {
set varName=varMatcher.Group(2)
if varMatcher.Group(1)'="" {
do aCode.WriteLine($char(9)_"new "_varName)
}
if varMatcher.Group(3)'="" {
set expr=varMatcher.Group(4)
set expr=..Expression(expr)
do:expr'="" aCode.WriteLine($char(9)_"set "_varName_" = "_expr)
}
continue
} elseif printlnMatcher.Match(tCode) {
set expr=printlnMatcher.Group(1)
set expr=..Expression(expr)
do:expr'="" aCode.WriteLine($char(9)_"Write "_expr_",!")
} elseif readMatcher.Match(tCode) {
set expr=readMatcher.Group(1)
set expr=..Expression(expr)
set var=readMatcher.Group(2)
do:expr'="" aCode.WriteLine($char(9)_"read "_expr_","_var_",!")
}
}
}
}
ClassMethod Expression(tExpr) As %String
{
set matchers($increment(matchers),"matcher")="(?sm)([^\'\""]*)\+[ \t]*(?:\""([^\""]*)\""|\'([^\']*)\')([^\'\""]*)"
set matchers(matchers,"replacement")="$1_""$2$3""$4"
set matchers($increment(matchers),"matcher")="(?sm)([^\'\""]*)(?:\""([^\""]*)\""|\'([^\']*)\')[ \t]*\+([^\'\""]*)"
set matchers(matchers,"replacement")="$1""$2$3""_$4"
set matchers($increment(matchers),"matcher")="(?sm)([^\'\""]*)(?:\""([^\""]*)\""|\'([^\']*)\')([^\'\""]*)"
set matchers(matchers,"replacement")="$1""$2$3""$4"
set tResult=tExpr
for i=1:1:matchers {
set matcher=##class(%Regex.Matcher).%New(matchers(i,"matcher"))
set replacement=$get(matchers(i,"replacement"))
set matcher.Text=tResult
set tResult=matcher.ReplaceAll(replacement)
}
quit tResult
}
/// Delete the routine 'name' which includes the routine extension
ClassMethod Delete(name As %String) As %Status
{
Set rtnName = $piece(name,".",1,$length(name,".")-1)
Kill ^rCJS(rtnName)
Quit $$$OK
}
/// Lock the current routine, default method just unlocks the ^rCJS global with the name of the routine.
/// If it fails then return a status code of the error, otherise return $$$OK
Method Lock(flags As %String) As %Status
{
Lock +^rCJS(..Name):0 Else Quit $$$ERROR($$$CanNotLockRoutine,..Name)
Quit $$$OK
}
/// Unlock the current routine, default method just unlocks the ^rCJS global with the name of the routine
Method Unlock(flags As %String) As %Status
{
Lock -^rCJS(..Name)
Quit $$$OK
}
/// Save the routine stored in Code
Method Save() As %Status
{
set ^rCJS(..ShortName,"LANG")="CJS"
set pCodeGN=$name(^rCJS(..ShortName,0))
kill @pCodeGN
set @pCodeGN=$ztimestamp
Set ..Code.LineTerminator=$char(13,10)
set source=..Code
do source.Rewind()
WHILE '(source.AtEnd) {
set pCodeLine=source.ReadLine()
set @pCodeGN@($increment(@pCodeGN@(0)))=pCodeLine
}
set @pCodeGN@("SIZE")=..Code.Size
Quit $$$OK
}
/// Return the timestamp of routine 'name' in %TimeStamp format. This is used to determine if the routine has
/// been updated on the server and so needs reloading from Studio. So the format should be $zdatetime($horolog,3),
/// or "" if the routine does not exist.
ClassMethod TimeStamp(name As %String) As %TimeStamp
{
Set rtnName = $piece(name,".",1,$length(name,".")-1)
Set timeStamp=$zdatetime($get(^rCJS(rtnName,0)),3)
Quit timeStamp
}
/// Return 1 if the routine 'name' exists and 0 if it does not.
ClassMethod Exists(name As %String) As %Boolean
{
Set rtnName = $piece(name,".",1,$length(name,".")-1)
Set rtnNameExt = $piece(name,".",$length(name,"."))
Quit $data(^rCJS(rtnName))&&($get(^rCJS(rtnName,"LANG"))=$zconvert(rtnNameExt,"U"))
}
ClassMethod ListExecute(ByRef qHandle As %Binary, Directory As %String, Flat As %Boolean, System As %Boolean) As %Status
{
Set qHandle=$listbuild(Directory,Flat,System,"")
Quit $$$OK
}
/// This should return a Row in the form:<br>
/// $listbuild(name,date/time modified,size,directory delimitor)<ul>
/// <li>name - The name to display in the open dialog</li>
/// <li>date/time modified - In %TimeStamp format the date/time this item was last modified</li>
/// <li>size - The size of this item</li>
/// <li>directory delimitor - If this is not an directory then return "" here, if it is a directory then
/// return the type of delimitor to use when they double click on this directory</li>
ClassMethod ListFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = ListExecute ]
{
Set Row="",AtEnd=0
If qHandle="" Set AtEnd=1 Quit $$$OK
If $list(qHandle)'=""||($list(qHandle,4)=1) Set AtEnd=1 Quit $$$OK
set AtEnd=1
Set rtnName=$listget(qHandle,5)
For {
Set rtnName=$order(^rCJS(rtnName)) Quit:rtnName=""
continue:$get(^rCJS(rtnName,"LANG"))'="CJS"
set timeStamp=$zdatetime($get(^rCJS(rtnName,0)),3)
set size=+$get(^rCJS(rtnName,0,"SIZE"))
Set Row=$listbuild(rtnName_".cjs",timeStamp,size,"")
set AtEnd=0
set $list(qHandle,5)=rtnName
Quit
}
Quit $$$OK
}
/// Return other document types that this is related to.
/// Passed a name and you return a comma separated list of the other documents it is related to
/// or "" if it is not related to anything. Note that this can be passed a document of another type
/// for example if your 'test.XXX' document creates a 'test.INT' routine then it will also be called
/// with 'test.INT' so you can return 'test.XXX' to complete the cycle.
ClassMethod GetOther(Name As %String) As %String
{
Set rtnName = $piece(Name,".",1,$length(Name,".")-1)_".INT"
Quit:##class(%Routine).%ExistsId(rtnName) rtnName
Quit ""
}
/// This query scans over the documents for the Studio open dialog.
/// The arguments are defined in <method>ItemList</method>.<p>
/// This should return a row form by:<br>
/// $listbuild(name,date/time modified,size,directory delimitor)
/// <ul>
/// <li>name - The name to display in the open dialog</li>
/// <li>date/time modified - In %TimeStamp format the date/time this item was last modified</li>
/// <li>size - The size of this item</li>
/// <li>directory delimitor - If this is not an directory then return "" here, if it is a directory then
/// return the type of delimitor to use when they double click on this directory</li>
/// </ul>
Query List(Directory As %String, Flat As %Boolean, System As %Boolean) As %Query(ROWSPEC = "name:%String,modified:%TimeStamp,size:%Integer,directory:%String") [ SqlProc ]
{
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment