So, this thread is about experimental export script additions. On the upside this approach gives you features you wished for at the cost of them maybe breaking down the line on new Hype releases. It boils down to continued maintenance if such patches are considered for prolonged projects. Although, that risk in general applies to any third-party export script (plugin or extension). It is currently and most likely will always be experimental in nature as I release this in my spare time. Hence, this thread is marked as USE AT YOUR OWN RISK.
To repeat it in the words of Tumult:
Here are the reasons why this is cool!
- On a project basis you can add these through an export script and the rendered Hype Runtime is bundled
- The patching takes place while your previewing and using advanced exports and retains the modification in code
- No more manual patching of the runtime as that was the reason I wouldn't use this method until now even though it was discussed before
- If you never redeploy and the exported Hype document (meaning open in open much later or without the proper exportscript) or the app is for only a specified timeframe or event most of the follow risks don't apply.
That said, what are the reasons it might break on Hype updates?
- The substitution method (patching) is run on the minified JS (converted into Symbols). These likely change on new versions of the Hype Runtime and would require reevaluationin such cases. That said, I try to "hook" against the most likely stable patch anchors (substitution strings).
- They could also be run against the full versions in a more stable fashion but that would make the runtime bigger, and we like small.
Exportscript Boilerplate:
#!/usr/bin/python
# HypeExportPlayground.hype-export.py
# Sample on implementing patches
#
# MIT License
# Copyright (c) 2020 Max Ziebell
#
import argparse
import json
import sys
import os
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--hype_version')
parser.add_argument('--hype_build')
parser.add_argument('--get_options', action='store_true')
parser.add_argument('--modify_staging_path')
parser.add_argument('--destination_path')
parser.add_argument('--export_info_json_path')
args, unknown = parser.parse_known_args()
if args.get_options:
def save_options():
return {
"allows_export" : True,
"allows_preview" : True,
}
options = {
"save_options" : save_options(),
"min_hype_build_version" : "596",
# "max_hype_build_version" : "670",
}
exit_with_result(options)
elif args.modify_staging_path != None:
import os
import string
import fnmatch
import re
export_info_file = open(args.export_info_json_path)
export_info = json.loads(export_info_file.read())
export_info_file.close()
# common file globs
glob_hype_runtime_minified = 'HYPE-'+args.hype_build+'*.min.js'
# common hooks
hook_api = '.API={'
hook_props = 'top:{HYP_r'
# patch helper
def read_content(filepath):
with open(filepath) as f:
return f.read()
def save_content(filepath, content):
with open(filepath, "w") as f:
f.write(content)
def patch_pre_hook(hook, insert, filePattern):
patch(hook, hook+insert, filePattern)
def patch_post_hook(hook, insert, filePattern):
patch(hook, insert+hook, filePattern)
def patch(find, replace, filePattern):
for path, dirs, files in os.walk(os.path.abspath(args.modify_staging_path)):
for filename in fnmatch.filter(files, filePattern):
filepath = os.path.join(path, filename)
s = read_content(filepath)
s = s.replace(find, replace)
save_content(filepath,s)
def read_runtime_content():
for path, dirs, files in os.walk(os.path.abspath(args.modify_staging_path)):
for filename in fnmatch.filter(files, glob_hype_runtime_minified):
return read_content(os.path.join(path, filename))
runtime = read_runtime_content()
# ADD PATCHES BELOW HERE
import shutil
shutil.rmtree(args.destination_path, ignore_errors=True)
shutil.move(args.modify_staging_path, args.destination_path)
exit_with_result(True)
# UTILITIES
# communicate info back to Hype
def exit_with_result(result):
import sys
print "===================="
print json.dumps({"result" : result})
sys.exit(0)
if __name__ == "__main__":
main()
Here are some nice little snippets for the ADD PATCHES BELOW HERE
spot:
showSceneByIndex
Extend the API with showSceneByIndex
. It's like showSceneNamed
but allows adressing the scene by index (starting at 0).
# add showSceneByIndex
showSceneContainer_minified = re.search('showNextScene:function\(\w,\w\){(\w+)', runtime).group(1)
patch_pre_hook(hook_api, 'showSceneByIndex:'+showSceneContainer_minified+',', glob_hype_runtime_minified)
Enable calls like the following example:
hypeDocument.showSceneByIndex(2);
rotateY and rotateX properties
Adds these two properties to the properties you can then address using the API with setElementProperty
.
# add missing rotation properties to API
patch_post_hook(hook_props, 'rotateX:{HYP_r:"bR",HYP_s:0},rotateY:{HYP_r:"aY",HYP_s:0},', glob_hype_runtime_minified)
Enable calls like the following examples:
hypeDocument.setElementProperty(testElm, 'rotateY', 45, 1.0, 'easeinout');
hypeDocument.setElementProperty(testElm, 'rotateX', 45, 1.0, 'easeinout');
rotatePath property
Adds a getter property so you can get the current rotation when using a motion path using the API with getElementProperty
. This is useful because the runtime manages rotationZ
and the motionPathRotation we are exposing here as rotatePath
separately and only the combined result is found in the outputted CSS.
# add missing path rotation property to API (motionPathRotation)
patch_post_hook(hook_props, 'rotatePath:{HYP_r:"bO",HYP_s:0},', glob_hype_runtime_minified)
Enable calls like the following example (read-only property):
hypeDocument.getElementProperty(testElm, 'rotatePath');
You can download the Python file and a test here: