example/ivy/ios: enable demo functionality

And bring iOS app up to Android capabilities.

Fixes golang/go#48694

Change-Id: I0e853cab102e34053ecdb17edb99644aa2f8651a
Reviewed-on: https://go-review.googlesource.com/c/mobile/+/357977
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Peter Weinberger <pjw@google.com>
This commit is contained in:
robaho
2021-10-30 03:09:00 +00:00
committed by Hyang-Ah Hana Kim
parent 7b8bfe0993
commit 170e11ba23
8 changed files with 292 additions and 136 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ mkdir work; cd work
go mod init work
go get -d golang.org/x/mobile/bind@latest
go get -d robpike.io/ivy/mobile
gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile
gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile robpike.io/ivy/demo
```
Place the Mobile.xcframework directory in this directory, and
+11 -4
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@@ -316,12 +316,16 @@
);
INFOPLIST_FILE = ivy/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = ivy;
STRIP_STYLE = debugging;
SUPPORTS_MACCATALYST = YES;
@@ -342,13 +346,16 @@
);
INFOPLIST_FILE = ivy/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)",
);
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.google.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = ivy;
STRIP_STYLE = debugging;
SUPPORTS_MACCATALYST = YES;
+59 -18
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19158" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<accessibilityOverrides isEnabled="YES"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19141"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -55,15 +55,38 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9SS-TP-C7c" userLabel="InputField">
<rect key="frame" x="20" y="817" width="374" height="34"/>
<color key="backgroundColor" red="0.98823529409999999" green="0.98039215690000003" blue="0.81176470590000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<rect key="contentStretch" x="1" y="1" width="1" height="1"/>
<fontDescription key="fontDescription" name="Menlo-Bold" family="Menlo" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert" enablesReturnKeyAutomatically="YES"/>
</textField>
<stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Cn1-rU-W3R">
<rect key="frame" x="20" y="831" width="374" height="31"/>
<subviews>
<textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9SS-TP-C7c" userLabel="InputField">
<rect key="frame" x="0.0" y="0.0" width="301.5" height="31"/>
<color key="backgroundColor" red="0.98823529409999999" green="0.98039215690000003" blue="0.81176470590000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<rect key="contentStretch" x="1" y="1" width="1" height="1"/>
<fontDescription key="fontDescription" name="Menlo-Bold" family="Menlo" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert" enablesReturnKeyAutomatically="YES" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eO6-bM-CwW">
<rect key="frame" x="301.5" y="0.0" width="72.5" height="31"/>
<accessibility key="accessibilityConfiguration" label="OK"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="1nb-CV-bfg"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Button" image="return" catalog="system"/>
<connections>
<action selector="okPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="258-zN-fwW"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="9SS-TP-C7c" firstAttribute="trailing" secondItem="eO6-bM-CwW" secondAttribute="leading" id="9Fe-Sf-U5p"/>
<constraint firstItem="eO6-bM-CwW" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="top" id="9hv-aL-4z2"/>
<constraint firstItem="eO6-bM-CwW" firstAttribute="trailing" secondItem="Cn1-rU-W3R" secondAttribute="trailing" id="A4G-YY-4Gn"/>
<constraint firstItem="9SS-TP-C7c" firstAttribute="leading" secondItem="Cn1-rU-W3R" secondAttribute="leading" id="i2m-BU-Q4a"/>
</constraints>
</stackView>
<wkWebView opaque="NO" tag="2" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" layoutMarginsFollowReadableWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ga1-Py-9re">
<rect key="frame" x="20" y="88" width="374" height="721"/>
<rect key="frame" x="20" y="88" width="374" height="737"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
@@ -73,29 +96,44 @@
</subviews>
<color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="9SS-TP-C7c" secondAttribute="trailing" id="3b6-rZ-W4R"/>
<constraint firstItem="9SS-TP-C7c" firstAttribute="top" secondItem="ga1-Py-9re" secondAttribute="bottom" constant="8" id="B3O-hi-PRD"/>
<constraint firstItem="Cn1-rU-W3R" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="7DU-Hc-uk7"/>
<constraint firstItem="Cn1-rU-W3R" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="8mr-vY-R1v"/>
<constraint firstItem="ga1-Py-9re" firstAttribute="bottom" secondItem="Cn1-rU-W3R" secondAttribute="top" constant="-6" id="BX0-OH-IJ4"/>
<constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="Fxh-TE-BtG"/>
<constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="9SS-TP-C7c" secondAttribute="leading" id="MhN-JS-MpC"/>
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="bottom" constant="11" id="Yvi-br-VHk"/>
<constraint firstItem="Cn1-rU-W3R" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="LPE-LO-nct"/>
<constraint firstItem="ga1-Py-9re" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="topMargin" id="p9M-sp-ZwC"/>
<constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="pOi-3s-hrr"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Ivy" id="9fD-A5-WTg">
<barButtonItem key="rightBarButtonItem" title="Help" id="aDa-GJ-VpX">
<barButtonItem key="leftBarButtonItem" title="Clear" id="70x-xp-aiX">
<connections>
<segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/>
<action selector="clear:" destination="BYZ-38-t0r" id="n7k-fg-dil"/>
</connections>
</barButtonItem>
<rightBarButtonItems>
<barButtonItem title="Help" id="aDa-GJ-VpX">
<connections>
<segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/>
</connections>
</barButtonItem>
<barButtonItem title="Demo" id="ZN2-PQ-aPW">
<connections>
<action selector="demo:" destination="BYZ-38-t0r" id="NpG-yh-wMO"/>
</connections>
</barButtonItem>
</rightBarButtonItems>
</navigationItem>
<connections>
<outlet property="bottomConstraint" destination="Yvi-br-VHk" id="EKm-WV-Y1w"/>
<outlet property="bottomConstraint" destination="8mr-vY-R1v" id="BqX-Do-vtq"/>
<outlet property="input" destination="9SS-TP-C7c" id="jUA-25-4WI"/>
<outlet property="okButton" destination="eO6-bM-CwW" id="o9U-hD-NDr"/>
<outlet property="tape" destination="ga1-Py-9re" id="00M-9S-MAl"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="990.40178571428567" y="382.60869565217394"/>
<point key="canvasLocation" x="989.85507246376824" y="382.36607142857139"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="ZgV-45-Pf8">
@@ -130,4 +168,7 @@
<point key="canvasLocation" x="271" y="386"/>
</scene>
</scenes>
<resources>
<image name="return" catalog="system" width="128" height="101"/>
</resources>
</document>
@@ -150,6 +150,13 @@
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"filename" : "icon-80.png",
"idiom" : "watch",
@@ -166,6 +173,13 @@
"size" : "44x44",
"subtype" : "40mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"filename" : "icon-100.png",
"idiom" : "watch",
@@ -174,6 +188,13 @@
"size" : "50x50",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"filename" : "icon-172.png",
"idiom" : "watch",
@@ -198,6 +219,13 @@
"size" : "108x108",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"filename" : "icon-1024.png",
"idiom" : "watch-marketing",
+1 -1
View File
@@ -11,7 +11,7 @@
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>com.google.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
+7 -2
View File
@@ -14,8 +14,13 @@
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
// A text input field coupled to an output "tape", rendered with a WKWebView.
@property (strong, nonatomic) UITextField *input;
@property (weak, nonatomic) IBOutlet UITextField *input;
@property (strong, nonatomic) Suggestion *suggestionView;
@property (strong, nonatomic) WKWebView *tape;
@property (weak, nonatomic) IBOutlet WKWebView *tape;
@property (weak, nonatomic) IBOutlet UIButton *okButton;
- (IBAction)clear:(id)sender;
- (IBAction)demo:(id)sender;
- (IBAction)okPressed:(id)sender;
@end
+139 -80
View File
@@ -9,50 +9,46 @@
@end
@implementation IvyController
@implementation IvyController {
NSArray *demo_lines;
int demo_index;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.input = (UITextField *)[self.view viewWithTag:1];
self.input.delegate = self;
self.input.autocorrectionType = UITextAutocorrectionTypeNo;
self.input.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
self.suggestionView = [[Suggestion alloc] init];
self.suggestionView.delegate = self;
self.tape = [self.view viewWithTag:2];
self.tape.UIDelegate = self;
self->demo_lines=NULL;
[self.okButton setTitle:@"" forState:UIControlStateNormal];
[self.okButton setHidden:TRUE];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(textDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.input];
addObserver:self
selector:@selector(textDidChange:)
name:UITextFieldTextDidChangeNotification
object:self.input];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
NSURL *bundleURL =
[[NSBundle mainBundle] URLForResource:@"tape" withExtension:@"html"];
NSURLRequest *request = [NSURLRequest requestWithURL:bundleURL];
[self.tape loadRequest:request];
self.tape.UIDelegate = self;
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
[self.input becomeFirstResponder];
}
- (void)viewDidAppear:(BOOL)animated
{
[self.view endEditing:YES];
[self clear:NULL];
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
@@ -75,33 +71,14 @@
}
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)str
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)str
{
if ([str isEqualToString:@"\n"]) {
[self
appendTape:[NSString stringWithFormat:@"<b>%@</b>", [self.input text]]];
NSString *expr = [self.input.text stringByAppendingString:@"\n"];
NSError *err;
NSString *result = MobileEval(expr, &err);
if (err != nil) {
result = err.description;
}
result = [result
stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
result =
[result stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
result =
[result stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
NSMutableArray *lines =
(NSMutableArray *)[result componentsSeparatedByString:@"\n"];
for (NSMutableString *line in lines) {
[self appendTape:line];
}
self.input.text = @"";
[self enterPressed];
return NO;
}
return YES;
}
@@ -121,51 +98,98 @@
// Move the input text field up, as the keyboard has taken some of the screen.
NSDictionary *info = [aNotification userInfo];
CGRect kbFrame =
[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
[[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
NSNumber *duration =
[info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
UIViewAnimationCurve keyboardTransitionAnimationCurve;
[[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
getValue:&keyboardTransitionAnimationCurve];
getValue:&keyboardTransitionAnimationCurve];
UIViewAnimationOptions options =
keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
[UIView animateWithDuration:duration.floatValue
delay:0
options:options
animations:^{
self.bottomConstraint.constant = kbFrame.size.height;
delay:0
options:options
animations:^{
self.bottomConstraint.constant = 0 - kbFrame.size.height;
[self.view layoutIfNeeded];
}
completion:^(BOOL finished) {
}
completion:^(BOOL finished) {
[self scrollTapeToBottom];
}];
}];
}
- (void)keyboardWillHide:(NSNotification *)aNotification
{
// Move the input text field back down.
NSDictionary *info = [aNotification userInfo];
NSNumber *duration =
[info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
[info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
UIViewAnimationCurve keyboardTransitionAnimationCurve;
[[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
getValue:&keyboardTransitionAnimationCurve];
getValue:&keyboardTransitionAnimationCurve];
UIViewAnimationOptions options =
keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
int offset = self.input.inputAccessoryView != NULL ? self.suggestionView.frame.size.height : 0;
[UIView animateWithDuration:duration.floatValue
delay:0
options:options
animations:^{
self.bottomConstraint.constant = 32;
delay:0
options:options
animations:^{
self.bottomConstraint.constant = 0 - offset;
[self.view layoutIfNeeded];
}
completion:^(BOOL finished) {
}
completion:^(BOOL finished) {
[self scrollTapeToBottom];
}];
}];
}
- (void)enterPressed
{
NSString *text = self.input.text;
if ([text isEqual:@""]){
if(self->demo_lines==NULL){
return;
}
while (demo_index < self->demo_lines.count) {
NSString *line = self->demo_lines[self->demo_index++];
if([line hasPrefix:@"#"]) {
[self appendTape:line tag:@"comment"];
} else {
self.input.text = line;
break;
}
}
} else if (self->demo_lines!=NULL && [text isEqual:@"quit"]) {
[self unloadDemo];
} else {
[self appendTape:text tag:@"expr"];
NSString *expr = [text stringByAppendingString:@"\n"];
NSError *err;
NSString *result = MobileEval(expr, &err);
if (err != nil) {
result = err.description;
}
result = [result
stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
result =
[result stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
result =
[result stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
NSMutableArray *lines =
(NSMutableArray *)[result componentsSeparatedByString:@"\n"];
for (NSMutableString *line in lines) {
if ([line hasPrefix:@"#"])
[self appendTape:line tag:@"comment"];
else
[self appendTape:line tag:@"result"];
}
self.input.text = @"";
}
}
- (void)scrollTapeToBottom
@@ -174,11 +198,46 @@
[self.tape evaluateJavaScript:scroll completionHandler:nil];
}
- (void)appendTape:(NSString *)text
- (void)appendTape:(NSString *)text tag:(NSString *)tag
{
NSString *injectSrc = @"appendDiv('%@');";
NSString *runToInject = [NSString stringWithFormat:injectSrc, text];
NSString *injectSrc = @"appendDiv('%@','%@');";
NSString *runToInject = [NSString stringWithFormat:injectSrc, text, tag];
[self.tape evaluateJavaScript:runToInject completionHandler:nil];
[self scrollTapeToBottom];
}
- (void)loadDemo
{
[self.okButton setHidden:FALSE];
NSString *text = DemoText();
self->demo_lines = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
self->demo_index = 0;
self.input.text = @"";
[self enterPressed];
}
- (void)unloadDemo
{
[self.okButton setHidden:TRUE];
self->demo_lines=NULL;
self.input.text = @"";
}
- (IBAction)okPressed:(id)sender {
[self enterPressed];
}
- (IBAction)demo:(id)sender {
if (self->demo_lines) { // demo already running
[self enterPressed];
} else {
[self loadDemo];
}
}
- (IBAction)clear:(id)sender {
[self unloadDemo];
NSString *string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:@"tape" ofType:@"html"] encoding:NSUTF8StringEncoding error:NULL];
[self.tape loadHTMLString:string baseURL:NULL];
}
@end
+46 -30
View File
@@ -5,40 +5,56 @@ license that can be found in the LICENSE file.
-->
<html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'>
<style>
body {
padding: 0;
margin: 0;
margin-top: 20px;
font-family: Menlo, monospace;
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" charset="UTF-8">
<style>
body {
font-family: sans-serif;
}
div {
padding: 1;
white-space: pre-wrap;
font-family: "Roboto Mono", monospace;
}
.comment {
color: grey;
}
.expr {
font-weight: bold;
}
.flow-hide {
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
overflow: hidden;
}
.flow-show {
word-break: break-all;
}
</style>
<script>
function flowClick(el) {
el.classList.toggle("flow-hide");
el.classList.toggle("flow-show");
}
.flowhide {
text-overflow: ellipsis;
word-break: break-word;
overflow-wrap: break-word;
function appendDiv(txt, tag) {
var el = document.createElement("div");
el.innerHTML = txt;
if (tag == "comment") {
el.classList.add("comment");
} else if (tag == "expr") {
el.classList.add("expr");
} else {
el.classList.add("flow-show");
el.onclick = function() {
flowClick(el);
};
}
document.body.appendChild(el);
}
.flowshow {
word-break: break-all;
}
</style>
<script>
function flowclick(el) {
el.classList.toggle("flowhide");
el.classList.toggle("flowshow");
}
function appendDiv(text) {
var el = document.createElement("div");
el.classList.add("flowhide");
el.innerHTML = text;
el.onclick = function() {
flowclick(el);
};
document.body.appendChild(el);
window.scrollBy(0, document.body.offsetHeight);
}
</script>
<body>
</body>