FIX: Fixed performanc bug in paragraph support.

* apparently, if you do an ngFor on the result of function call that
    returns a new array each time its called, it will screw up the template
    watching code such that every action will necessitate a rerendering
    of the information even if it hasn't changed.
This commit is contained in:
walljm 2018-03-03 21:13:51 -05:00
parent 5a54a1a803
commit 7a0bc2f2b2
17 changed files with 1700 additions and 744 deletions

File diff suppressed because one or more lines are too long

View File

@ -67,35 +67,35 @@
<feature name="SplashScreen"> <feature name="SplashScreen">
<param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" /> <param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" />
</feature> </feature>
<plugin name="cordova-plugin-console" spec="^1.0.5" />
<plugin name="cordova-plugin-crosswalk-webview" spec="^2.3.0">
<variable name="XWALK_VERSION" value="23+" />
<variable name="XWALK_LITEVERSION" value="xwalk_core_library_canary:17+" />
<variable name="XWALK_COMMANDLINE" value="--disable-pull-to-refresh-effect" />
<variable name="XWALK_MODE" value="embedded" />
<variable name="XWALK_MULTIPLEAPK" value="true" />
</plugin>
<plugin name="cordova-plugin-device" spec="^1.1.4" />
<plugin name="cordova-plugin-splashscreen" spec="^4.0.3" />
<plugin name="cordova-plugin-statusbar" spec="^2.2.3" />
<plugin name="cordova-plugin-whitelist" spec="^1.3.1" />
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
<universal-links> <universal-links>
<host name="bhgx5.app.goo.gl/XktS" scheme="https" /> <host name="bhgx5.app.goo.gl/XktS" scheme="https" />
<host name="dynamicbible-7c6cf.firebaseapp.com" scheme="https"> <host name="dynamicbible-7c6cf.firebaseapp.com" scheme="https">
<path url="/__/auth/callback" /> <path url="/__/auth/callback" />
</host> </host>
</universal-links> </universal-links>
<preference name="AndroidLaunchMode" value="singleTask" />
<plugin name="cordova-plugin-console" spec="^1.1.0" />
<plugin name="cordova-plugin-crosswalk-webview" spec="^2.4.0">
<variable name="XWALK_VERSION" value="23+" />
<variable name="XWALK_LITEVERSION" value="xwalk_core_library_canary:17+" />
<variable name="XWALK_COMMANDLINE" value="--disable-pull-to-refresh-effect" />
<variable name="XWALK_MODE" value="embedded" />
<variable name="XWALK_MULTIPLEAPK" value="true" />
</plugin>
<plugin name="cordova-plugin-device" spec="^1.1.7" />
<plugin name="cordova-plugin-splashscreen" spec="^4.1.0" />
<plugin name="cordova-plugin-statusbar" spec="^2.4.1" />
<plugin name="cordova-plugin-whitelist" spec="^1.3.3" />
<plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
<plugin name="cordova-universal-links-plugin" spec="^1.2.1" /> <plugin name="cordova-universal-links-plugin" spec="^1.2.1" />
<plugin name="cordova-plugin-buildinfo" spec="^2.0.1" /> <plugin name="cordova-plugin-buildinfo" spec="^2.0.1" />
<plugin name="cordova-plugin-browsertab" spec="^0.2.0" /> <plugin name="cordova-plugin-browsertab" spec="^0.2.0" />
<plugin name="cordova-plugin-inappbrowser" spec="^2.0.1" /> <plugin name="cordova-plugin-inappbrowser" spec="^2.0.2" />
<plugin name="cordova-plugin-customurlscheme" spec="^4.3.0"> <plugin name="cordova-plugin-customurlscheme" spec="^4.3.0">
<variable name="URL_SCHEME" value="com.firebase.cordova" /> <variable name="URL_SCHEME" value="com.firebase.cordova" />
<variable name="ANDROID_SCHEME" value=" " /> <variable name="ANDROID_SCHEME" value=" " />
<variable name="ANDROID_HOST" value=" " /> <variable name="ANDROID_HOST" value=" " />
<variable name="ANDROID_PATHPREFIX" value="/" /> <variable name="ANDROID_PATHPREFIX" value="/" />
</plugin> </plugin>
<preference name="AndroidLaunchMode" value="singleTask" />
<engine name="android" spec="6.2.3" /> <engine name="android" spec="6.2.3" />
</widget> </widget>

View File

@ -1,48 +0,0 @@
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var rootdir = process.argv[2];
if (rootdir)
{
console.log("Root: " + rootdir);
// go through each of the platform directories that have been prepared
var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []);
for (var x = 0; x < platforms.length; x++)
{
// // open up the index.html file at the www root
// try
// {
// var platform = platforms[x].trim().toLowerCase();
// var testBuildPath;
// if (platform === 'android')
// {
// testBuildPath = path.join(rootdir, 'platforms', platform, 'app', 'assets', 'www', 'build');
// }
// else
// {
// testBuildPath = path.join(rootdir, 'platforms', platform, 'app', 'www', 'build');
// }
// if (fs.existsSync(testBuildPath))
// {
// console.log('Removing map files from assets after prepare: ' + testBuildPath);
// fs.unlinkSync(testBuildPath + '/vendor.js.map');
// fs.unlinkSync(testBuildPath + '/main.js.map');
// fs.unlinkSync(testBuildPath + '/main.css.map');
// }
// else
// {
// console.log('Build dir @ ' + testBuildPath + ' does not exist for removal');
// }
// } catch (e)
// {
// process.stdout.write(e);
// }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,6 @@
"test-coverage": "ng test --code-coverage" "test-coverage": "ng test --code-coverage"
}, },
"dependencies": { "dependencies": {
"@angular/cli": "1.6.3",
"@angular/common": "5.0.0", "@angular/common": "5.0.0",
"@angular/compiler": "5.0.0", "@angular/compiler": "5.0.0",
"@angular/compiler-cli": "5.0.0", "@angular/compiler-cli": "5.0.0",
@ -38,10 +37,10 @@
"cordova-plugin-buildinfo": "^2.0.1", "cordova-plugin-buildinfo": "^2.0.1",
"cordova-plugin-compat": "^1.2.0", "cordova-plugin-compat": "^1.2.0",
"cordova-plugin-console": "^1.1.0", "cordova-plugin-console": "^1.1.0",
"cordova-plugin-crosswalk-webview": "^2.3.0", "cordova-plugin-crosswalk-webview": "^2.4.0",
"cordova-plugin-customurlscheme": "^4.3.0", "cordova-plugin-customurlscheme": "^4.3.0",
"cordova-plugin-device": "^1.1.7", "cordova-plugin-device": "^1.1.7",
"cordova-plugin-inappbrowser": "^2.0.1", "cordova-plugin-inappbrowser": "^2.0.2",
"cordova-plugin-splashscreen": "^4.1.0", "cordova-plugin-splashscreen": "^4.1.0",
"cordova-plugin-statusbar": "^2.4.1", "cordova-plugin-statusbar": "^2.4.1",
"cordova-plugin-whitelist": "^1.3.3", "cordova-plugin-whitelist": "^1.3.3",
@ -56,7 +55,7 @@
"zone.js": "0.8.18" "zone.js": "0.8.18"
}, },
"devDependencies": { "devDependencies": {
"@angular/cli": "1.1.2", "@angular/cli": "1.6.3",
"@ionic/app-scripts": "3.1.0", "@ionic/app-scripts": "3.1.0",
"@types/jasmine": "2.5.41", "@types/jasmine": "2.5.41",
"@types/node": "7.0.4", "@types/node": "7.0.4",

View File

@ -237,10 +237,10 @@
"cordova-plugin-browsertab": "0.2.0", "cordova-plugin-browsertab": "0.2.0",
"cordova-plugin-buildinfo": "2.0.1", "cordova-plugin-buildinfo": "2.0.1",
"cordova-plugin-console": "1.1.0", "cordova-plugin-console": "1.1.0",
"cordova-plugin-crosswalk-webview": "2.3.0", "cordova-plugin-crosswalk-webview": "2.4.0",
"cordova-plugin-customurlscheme": "4.3.0", "cordova-plugin-customurlscheme": "4.3.0",
"cordova-plugin-device": "1.1.7", "cordova-plugin-device": "1.1.7",
"cordova-plugin-inappbrowser": "2.0.1", "cordova-plugin-inappbrowser": "2.0.2",
"cordova-plugin-splashscreen": "4.1.0", "cordova-plugin-splashscreen": "4.1.0",
"cordova-plugin-statusbar": "2.4.1", "cordova-plugin-statusbar": "2.4.1",
"cordova-plugin-whitelist": "1.3.3", "cordova-plugin-whitelist": "1.3.3",

View File

@ -29,7 +29,17 @@ def DEFAULT_MIN_SDK_VERSION = 14
def getConfigPreference(name) { def getConfigPreference(name) {
name = name.toLowerCase() name = name.toLowerCase()
def xml = file("res/xml/config.xml").getText()
def xml
if (file("src/main/res/xml/config.xml").exists()) {
// cordova-android >= 7.0.0
xml = file("src/main/res/xml/config.xml").getText()
} else {
// cordova-android < 7.0.0
xml = file("res/xml/config.xml").getText()
}
// Disable namespace awareness since Cordova doesn't use them properly // Disable namespace awareness since Cordova doesn't use them properly
def root = new XmlParser(false, false).parseText(xml) def root = new XmlParser(false, false).parseText(xml)

View File

@ -82,10 +82,10 @@ module.exports.metadata =
"cordova-plugin-browsertab": "0.2.0", "cordova-plugin-browsertab": "0.2.0",
"cordova-plugin-buildinfo": "2.0.1", "cordova-plugin-buildinfo": "2.0.1",
"cordova-plugin-console": "1.1.0", "cordova-plugin-console": "1.1.0",
"cordova-plugin-crosswalk-webview": "2.3.0", "cordova-plugin-crosswalk-webview": "2.4.0",
"cordova-plugin-customurlscheme": "4.3.0", "cordova-plugin-customurlscheme": "4.3.0",
"cordova-plugin-device": "1.1.7", "cordova-plugin-device": "1.1.7",
"cordova-plugin-inappbrowser": "2.0.1", "cordova-plugin-inappbrowser": "2.0.2",
"cordova-plugin-splashscreen": "4.1.0", "cordova-plugin-splashscreen": "4.1.0",
"cordova-plugin-statusbar": "2.4.1", "cordova-plugin-statusbar": "2.4.1",
"cordova-plugin-whitelist": "1.3.3", "cordova-plugin-whitelist": "1.3.3",

View File

@ -25,6 +25,9 @@ import android.provider.Browser;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -51,6 +54,7 @@ import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView;
import org.apache.cordova.CallbackContext; import org.apache.cordova.CallbackContext;
import org.apache.cordova.Config; import org.apache.cordova.Config;
@ -67,6 +71,8 @@ import org.json.JSONObject;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap; import java.util.HashMap;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -91,6 +97,16 @@ public class InAppBrowser extends CordovaPlugin {
private static final String SHOULD_PAUSE = "shouldPauseOnSuspend"; private static final String SHOULD_PAUSE = "shouldPauseOnSuspend";
private static final Boolean DEFAULT_HARDWARE_BACK = true; private static final Boolean DEFAULT_HARDWARE_BACK = true;
private static final String USER_WIDE_VIEW_PORT = "useWideViewPort"; private static final String USER_WIDE_VIEW_PORT = "useWideViewPort";
private static final String TOOLBAR_COLOR = "toolbarcolor";
private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
private static final String CLOSE_BUTTON_COLOR = "closebuttoncolor";
private static final String HIDE_NAVIGATION = "hidenavigationbuttons";
private static final String NAVIGATION_COLOR = "navigationbuttoncolor";
private static final String HIDE_URL = "hideurlbar";
private static final String FOOTER = "footer";
private static final String FOOTER_COLOR = "footercolor";
private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR);
private InAppBrowserDialog dialog; private InAppBrowserDialog dialog;
private WebView inAppWebView; private WebView inAppWebView;
@ -109,6 +125,14 @@ public class InAppBrowser extends CordovaPlugin {
private ValueCallback<Uri[]> mUploadCallbackLollipop; private ValueCallback<Uri[]> mUploadCallbackLollipop;
private final static int FILECHOOSER_REQUESTCODE = 1; private final static int FILECHOOSER_REQUESTCODE = 1;
private final static int FILECHOOSER_REQUESTCODE_LOLLIPOP = 2; private final static int FILECHOOSER_REQUESTCODE_LOLLIPOP = 2;
private String closeButtonCaption = "";
private String closeButtonColor = "";
private int toolbarColor = android.graphics.Color.LTGRAY;
private boolean hideNavigationButtons = false;
private String navigationButtonColor = "";
private boolean hideUrlBar = false;
private boolean showFooter = false;
private String footerColor = "";
/** /**
* Executes the request and returns PluginResult. * Executes the request and returns PluginResult.
@ -127,7 +151,7 @@ public class InAppBrowser extends CordovaPlugin {
t = SELF; t = SELF;
} }
final String target = t; final String target = t;
final HashMap<String, Boolean> features = parseFeature(args.optString(2)); final HashMap<String, String> features = parseFeature(args.optString(2));
LOG.d(LOG_TAG, "target = " + target); LOG.d(LOG_TAG, "target = " + target);
@ -366,18 +390,21 @@ public class InAppBrowser extends CordovaPlugin {
* @param optString * @param optString
* @return * @return
*/ */
private HashMap<String, Boolean> parseFeature(String optString) { private HashMap<String, String> parseFeature(String optString) {
if (optString.equals(NULL)) { if (optString.equals(NULL)) {
return null; return null;
} else { } else {
HashMap<String, Boolean> map = new HashMap<String, Boolean>(); HashMap<String, String> map = new HashMap<String, String>();
StringTokenizer features = new StringTokenizer(optString, ","); StringTokenizer features = new StringTokenizer(optString, ",");
StringTokenizer option; StringTokenizer option;
while(features.hasMoreElements()) { while(features.hasMoreElements()) {
option = new StringTokenizer(features.nextToken(), "="); option = new StringTokenizer(features.nextToken(), "=");
if (option.hasMoreElements()) { if (option.hasMoreElements()) {
String key = option.nextToken(); String key = option.nextToken();
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE; String value = option.nextToken();
if (!customizableOptions.contains(key)){
value = value.equals("yes") || value.equals("no") ? value : "yes";
}
map.put(key, value); map.put(key, value);
} }
} }
@ -406,7 +433,7 @@ public class InAppBrowser extends CordovaPlugin {
intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName()); intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
this.cordova.getActivity().startActivity(intent); this.cordova.getActivity().startActivity(intent);
return ""; return "";
// not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
} catch (java.lang.RuntimeException e) { } catch (java.lang.RuntimeException e) {
LOG.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString()); LOG.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
return e.toString(); return e.toString();
@ -523,7 +550,7 @@ public class InAppBrowser extends CordovaPlugin {
* @param url the url to load. * @param url the url to load.
* @param features jsonObject * @param features jsonObject
*/ */
public String showWebPage(final String url, HashMap<String, Boolean> features) { public String showWebPage(final String url, HashMap<String, String> features) {
// Determine if we should hide the location bar. // Determine if we should hide the location bar.
showLocationBar = true; showLocationBar = true;
showZoomControls = true; showZoomControls = true;
@ -531,44 +558,74 @@ public class InAppBrowser extends CordovaPlugin {
mediaPlaybackRequiresUserGesture = false; mediaPlaybackRequiresUserGesture = false;
if (features != null) { if (features != null) {
Boolean show = features.get(LOCATION); String show = features.get(LOCATION);
if (show != null) { if (show != null) {
showLocationBar = show.booleanValue(); showLocationBar = show.equals("yes") ? true : false;
} }
Boolean zoom = features.get(ZOOM); if(showLocationBar) {
String hideNavigation = features.get(HIDE_NAVIGATION);
String hideUrl = features.get(HIDE_URL);
if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes") ? true : false;
if(hideUrl != null) hideUrlBar = hideUrl.equals("yes") ? true : false;
}
String zoom = features.get(ZOOM);
if (zoom != null) { if (zoom != null) {
showZoomControls = zoom.booleanValue(); showZoomControls = zoom.equals("yes") ? true : false;
} }
Boolean hidden = features.get(HIDDEN); String hidden = features.get(HIDDEN);
if (hidden != null) { if (hidden != null) {
openWindowHidden = hidden.booleanValue(); openWindowHidden = hidden.equals("yes") ? true : false;
} }
Boolean hardwareBack = features.get(HARDWARE_BACK_BUTTON); String hardwareBack = features.get(HARDWARE_BACK_BUTTON);
if (hardwareBack != null) { if (hardwareBack != null) {
hadwareBackButton = hardwareBack.booleanValue(); hadwareBackButton = hardwareBack.equals("yes") ? true : false;
} else { } else {
hadwareBackButton = DEFAULT_HARDWARE_BACK; hadwareBackButton = DEFAULT_HARDWARE_BACK;
} }
Boolean mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION); String mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION);
if (mediaPlayback != null) { if (mediaPlayback != null) {
mediaPlaybackRequiresUserGesture = mediaPlayback.booleanValue(); mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false;
} }
Boolean cache = features.get(CLEAR_ALL_CACHE); String cache = features.get(CLEAR_ALL_CACHE);
if (cache != null) { if (cache != null) {
clearAllCache = cache.booleanValue(); clearAllCache = cache.equals("yes") ? true : false;
} else { } else {
cache = features.get(CLEAR_SESSION_CACHE); cache = features.get(CLEAR_SESSION_CACHE);
if (cache != null) { if (cache != null) {
clearSessionCache = cache.booleanValue(); clearSessionCache = cache.equals("yes") ? true : false;
} }
} }
Boolean shouldPause = features.get(SHOULD_PAUSE); String shouldPause = features.get(SHOULD_PAUSE);
if (shouldPause != null) { if (shouldPause != null) {
shouldPauseInAppBrowser = shouldPause.booleanValue(); shouldPauseInAppBrowser = shouldPause.equals("yes") ? true : false;
} }
Boolean wideViewPort = features.get(USER_WIDE_VIEW_PORT); String wideViewPort = features.get(USER_WIDE_VIEW_PORT);
if (wideViewPort != null ) { if (wideViewPort != null ) {
useWideViewPort = wideViewPort.booleanValue(); useWideViewPort = wideViewPort.equals("yes") ? true : false;
}
String closeButtonCaptionSet = features.get(CLOSE_BUTTON_CAPTION);
if (closeButtonCaptionSet != null) {
closeButtonCaption = closeButtonCaptionSet;
}
String closeButtonColorSet = features.get(CLOSE_BUTTON_COLOR);
if (closeButtonColorSet != null) {
closeButtonColor = closeButtonColorSet;
}
String toolbarColorSet = features.get(TOOLBAR_COLOR);
if (toolbarColorSet != null) {
toolbarColor = android.graphics.Color.parseColor(toolbarColorSet);
}
String navigationButtonColorSet = features.get(NAVIGATION_COLOR);
if (navigationButtonColorSet != null) {
navigationButtonColor = navigationButtonColorSet;
}
String showFooterSet = features.get(FOOTER);
if (showFooterSet != null) {
showFooter = showFooterSet.equals("yes") ? true : false;
}
String footerColorSet = features.get(FOOTER_COLOR);
if (footerColorSet != null) {
footerColor = footerColorSet;
} }
} }
@ -583,13 +640,60 @@ public class InAppBrowser extends CordovaPlugin {
*/ */
private int dpToPixels(int dipValue) { private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
(float) dipValue, (float) dipValue,
cordova.getActivity().getResources().getDisplayMetrics() cordova.getActivity().getResources().getDisplayMetrics()
); );
return value; return value;
} }
private View createCloseButton(int id){
View _close;
Resources activityRes = cordova.getActivity().getResources();
if (closeButtonCaption != "") {
// Use TextView for text
TextView close = new TextView(cordova.getActivity());
close.setText(closeButtonCaption);
close.setTextSize(20);
if (closeButtonColor != "") close.setTextColor(android.graphics.Color.parseColor(closeButtonColor));
close.setGravity(android.view.Gravity.CENTER_VERTICAL);
close.setPadding(this.dpToPixels(10), 0, this.dpToPixels(10), 0);
_close = close;
}
else {
ImageButton close = new ImageButton(cordova.getActivity());
int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
Drawable closeIcon = activityRes.getDrawable(closeResId);
if (closeButtonColor != "") close.setColorFilter(android.graphics.Color.parseColor(closeButtonColor));
close.setImageDrawable(closeIcon);
close.setScaleType(ImageView.ScaleType.FIT_CENTER);
if (Build.VERSION.SDK_INT >= 16)
close.getAdjustViewBounds();
_close = close;
}
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
_close.setLayoutParams(closeLayoutParams);
if (Build.VERSION.SDK_INT >= 16)
_close.setBackground(null);
else
_close.setBackgroundDrawable(null);
_close.setContentDescription("Close Button");
_close.setId(Integer.valueOf(id));
_close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
return _close;
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
public void run() { public void run() {
@ -612,7 +716,7 @@ public class InAppBrowser extends CordovaPlugin {
// Toolbar layout // Toolbar layout
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
//Please, no more black! //Please, no more black!
toolbar.setBackgroundColor(android.graphics.Color.LTGRAY); toolbar.setBackgroundColor(toolbarColor);
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44))); toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2)); toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
toolbar.setHorizontalGravity(Gravity.LEFT); toolbar.setHorizontalGravity(Gravity.LEFT);
@ -635,6 +739,7 @@ public class InAppBrowser extends CordovaPlugin {
Resources activityRes = cordova.getActivity().getResources(); Resources activityRes = cordova.getActivity().getResources();
int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName()); int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName());
Drawable backIcon = activityRes.getDrawable(backResId); Drawable backIcon = activityRes.getDrawable(backResId);
if (navigationButtonColor != "") back.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16) if (Build.VERSION.SDK_INT >= 16)
back.setBackground(null); back.setBackground(null);
else else
@ -660,6 +765,7 @@ public class InAppBrowser extends CordovaPlugin {
forward.setId(Integer.valueOf(3)); forward.setId(Integer.valueOf(3));
int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName()); int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName());
Drawable fwdIcon = activityRes.getDrawable(fwdResId); Drawable fwdIcon = activityRes.getDrawable(fwdResId);
if (navigationButtonColor != "") forward.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16) if (Build.VERSION.SDK_INT >= 16)
forward.setBackground(null); forward.setBackground(null);
else else
@ -692,37 +798,37 @@ public class InAppBrowser extends CordovaPlugin {
public boolean onKey(View v, int keyCode, KeyEvent event) { public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button // If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
navigate(edittext.getText().toString()); navigate(edittext.getText().toString());
return true; return true;
} }
return false; return false;
} }
}); });
// Close/Done button
ImageButton close = new ImageButton(cordova.getActivity());
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
close.setLayoutParams(closeLayoutParams);
close.setContentDescription("Close Button");
close.setId(Integer.valueOf(5));
int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
Drawable closeIcon = activityRes.getDrawable(closeResId);
if (Build.VERSION.SDK_INT >= 16)
close.setBackground(null);
else
close.setBackgroundDrawable(null);
close.setImageDrawable(closeIcon);
close.setScaleType(ImageView.ScaleType.FIT_CENTER);
back.setPadding(0, this.dpToPixels(10), 0, this.dpToPixels(10));
if (Build.VERSION.SDK_INT >= 16)
close.getAdjustViewBounds();
close.setOnClickListener(new View.OnClickListener() { // Header Close/Done button
public void onClick(View v) { View close = createCloseButton(5);
closeDialog(); toolbar.addView(close);
}
}); // Footer
RelativeLayout footer = new RelativeLayout(cordova.getActivity());
int _footerColor;
if(footerColor != ""){
_footerColor = Color.parseColor(footerColor);
}else{
_footerColor = android.graphics.Color.LTGRAY;
}
footer.setBackgroundColor(_footerColor);
RelativeLayout.LayoutParams footerLayout = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44));
footerLayout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
footer.setLayoutParams(footerLayout);
if (closeButtonCaption != "") footer.setPadding(this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8));
footer.setHorizontalGravity(Gravity.LEFT);
footer.setVerticalGravity(Gravity.BOTTOM);
View footerClose = createCloseButton(7);
footer.addView(footerClose);
// WebView // WebView
inAppWebView = new WebView(cordova.getActivity()); inAppWebView = new WebView(cordova.getActivity());
@ -825,10 +931,9 @@ public class InAppBrowser extends CordovaPlugin {
actionButtonContainer.addView(back); actionButtonContainer.addView(back);
actionButtonContainer.addView(forward); actionButtonContainer.addView(forward);
// Add the views to our toolbar // Add the views to our toolbar if they haven't been disabled
toolbar.addView(actionButtonContainer); if (!hideNavigationButtons) toolbar.addView(actionButtonContainer);
toolbar.addView(edittext); if (!hideUrlBar) toolbar.addView(edittext);
toolbar.addView(close);
// Don't add the toolbar if its been disabled // Don't add the toolbar if its been disabled
if (getShowLocationBar()) { if (getShowLocationBar()) {
@ -837,7 +942,14 @@ public class InAppBrowser extends CordovaPlugin {
} }
// Add our webview to our main view/layout // Add our webview to our main view/layout
main.addView(inAppWebView); RelativeLayout webViewLayout = new RelativeLayout(cordova.getActivity());
webViewLayout.addView(inAppWebView);
main.addView(webViewLayout);
// Don't add the footer unless it's been enabled
if (showFooter) {
webViewLayout.addView(footer);
}
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes()); lp.copyFrom(dialog.getWindow().getAttributes());
@ -1027,7 +1139,7 @@ public class InAppBrowser extends CordovaPlugin {
// Update the UI if we haven't already // Update the UI if we haven't already
if (!newloc.equals(edittext.getText().toString())) { if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc); edittext.setText(newloc);
} }
try { try {
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();

View File

@ -6,22 +6,33 @@
</ion-item> </ion-item>
<ion-card-content *ngIf="data !== undefined && data.status === 0"> <ion-card-content *ngIf="data !== undefined && data.status === 0">
<br> <br>
<div class="passage-text" *ngFor="let ch of data.cs"> <ng-template [ngIf]="this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas">
<h2 *ngIf="data.cs.length > 1"> <h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b> <b>Chapter {{ch.ch}}</b>
</h2> </h2>
<div *ngFor="let para of getParas(ch)"> <div *ngFor="let para of ch.paras">
<h3 *ngIf="hasHeader(para.p)">{{para.p.h}}</h3> <h3 *ngIf="hasHeader(para.p)">{{para.p.h}}</h3>
<p> <p>
<span *ngFor="let vs of para.vss"> <ng-template ngFor let-vs [ngForOf]="para.vss">
<b *ngIf="showVerseNumbers">{{vs.v}}.</b> <span *ngFor="let w of vs.w"> <b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a> <ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template> <ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</span><br *ngIf="versesOnNewLine"> </ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</span> </ng-template>
</p> </p>
</div> </div>
</div> </div></ng-template>
<ng-template [ngIf]="!this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs">
<h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b>
</h2>
<ng-template ngFor let-vs [ngForOf]="ch.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w">
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line">
</ng-template>
</div></ng-template>
</ion-card-content> </ion-card-content>
<ion-card-content *ngIf="data !== undefined && data.status === -1"> <ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message> <error-message [msg]="data.msg"></error-message>

View File

@ -19,13 +19,8 @@ export class Passage implements OnInit
@Input() @Input()
cardItem: CardItem; cardItem: CardItem;
@Input()
versesOnNewLine: boolean;
@Input()
showVerseNumbers: boolean;
data: BiblePassageResult; data: BiblePassageResult;
withParas: BibleParaPassage[];
ref: Reference; ref: Reference;
constructor(private bibleService: BibleService, private elementRef: ElementRef, private profileService: ProfileService) constructor(private bibleService: BibleService, private elementRef: ElementRef, private profileService: ProfileService)
@ -35,7 +30,17 @@ export class Passage implements OnInit
ngOnInit(): void ngOnInit(): void
{ {
this.ref = new Reference(this.cardItem.qry); this.ref = new Reference(this.cardItem.qry);
this.bibleService.getResultAsPromise(this.ref.Section).then(data => this.data = data); this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{
this.setData(data);
});
}
setData(data: BiblePassageResult)
{
this.data = data;
this.withParas = this.getParaPassages(data.cs);
} }
close() close()
@ -70,7 +75,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data => this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{ {
this.data = data; this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);
}); });
@ -89,7 +94,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data => this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{ {
this.data = data; this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);
}); });
@ -136,7 +141,7 @@ export class Passage implements OnInit
this.bibleService.getResultAsPromise(this.ref.Section).then(data => this.bibleService.getResultAsPromise(this.ref.Section).then(data =>
{ {
this.data = data; this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);
}); });
@ -174,6 +179,21 @@ export class Passage implements OnInit
return this.ref.Section.start.book.book_number + ';' + this.ref.Section.start.chapter + ';' + vs.v; return this.ref.Section.start.book.book_number + ';' + this.ref.Section.start.chapter + ';' + vs.v;
} }
getParaPassages(chapters: BiblePassage[])
{
let passages: BibleParaPassage[] = [];
for (let ch of chapters)
{
let para = {
ch: ch.ch,
paras: this.getParas(ch)
};
passages.push(para);
}
return passages;
}
getParas(ch: BiblePassage) getParas(ch: BiblePassage)
{ {
// group the verses into paragraphs. // group the verses into paragraphs.
@ -182,14 +202,6 @@ export class Passage implements OnInit
let para: BiblePara = { p: { h: '', p: 0 }, vss: [] }; let para: BiblePara = { p: { h: '', p: 0 }, vss: [] };
let paras: BiblePara[] = []; let paras: BiblePara[] = [];
// if you aren't showing paragraphs, stick em all in the same paragraph.
if (!this.profileService.profile().show_paragraphs)
{
para.vss = ch.vss;
paras.push(para);
return paras;
}
// for each verse in the chapter, break them into paragraphs. // for each verse in the chapter, break them into paragraphs.
for (let v of ch.vss) for (let v of ch.vss)
{ {
@ -218,3 +230,8 @@ type BiblePara = {
p: Paragraph, p: Paragraph,
vss: BibleVerse[], vss: BibleVerse[],
} }
type BibleParaPassage = {
ch: number;
paras: BiblePara[];
}

View File

@ -1,4 +1,5 @@
verse-picker { verse-picker {
.button { .button {
color: #fff; color: #fff;
font-size: 1em; font-size: 1em;
@ -7,7 +8,7 @@ verse-picker {
background-color: #1c2e4c; background-color: #1c2e4c;
margin: .3em; margin: .3em;
text-align: center; text-align: center;
width: 75px; width: 95px;
} }
.backbutton { .backbutton {

View File

@ -22,35 +22,35 @@
<ion-list-header>Search Settings</ion-list-header> <ion-list-header>Search Settings</ion-list-header>
<ion-item> <ion-item>
<ion-label>Show Strongs as Modal</ion-label> <ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Clear Search after Query</ion-label> <ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Append Results Below</ion-label> <ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Insert Result Next to Item</ion-label> <ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Each Verse on New Line</ion-label> <ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Show Verse #'s</ion-label> <ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Show Paragraphs</ion-label> <ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Show Paragraph Headings</ion-label> <ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="save()"></ion-toggle> <ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item> </ion-item>
<ion-list-header>Adjust Text</ion-list-header> <ion-list-header>Adjust Text</ion-list-header>
@ -97,8 +97,7 @@
<ion-content #searchcontent padding class="search-card"> <ion-content #searchcontent padding class="search-card">
<ion-card *ngFor="let item of this.profileService.profile().items"> <ion-card *ngFor="let item of this.profileService.profile().items">
<passage *ngIf="isPassage(item.type)" [cardItem]="item" [versesOnNewLine]="this.profileService.profile().verses_on_new_line" <passage *ngIf="isPassage(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></passage>
[showVerseNumbers]="this.profileService.profile().show_verse_numbers" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></passage>
<strongs *ngIf="isStrongs(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></strongs> <strongs *ngIf="isStrongs(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></strongs>
<words *ngIf="isWords(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></words> <words *ngIf="isWords(item.type)" [cardItem]="item" (onClose)="removeItem($event)" (onItemClicked)="getItemsNextToCard($event)"></words>
<error *ngIf="isError(item.type)" [cardItem]="item" (onClose)="removeItem($event)"></error> <error *ngIf="isError(item.type)" [cardItem]="item" (onClose)="removeItem($event)"></error>

View File

@ -12,8 +12,7 @@ import { VersePickerModal } from '../../components/verse-picker/verse-picker';
@Component({ @Component({
templateUrl: 'search.html' templateUrl: 'search.html'
}) })
export class SearchPage implements OnInit export class SearchPage implements OnInit {
{
searchQuery = ''; searchQuery = '';
last: CardItem; last: CardItem;
loader: Loading; loader: Loading;
@ -27,34 +26,43 @@ export class SearchPage implements OnInit
, public modalCtrl: ModalController , public modalCtrl: ModalController
, public profileService: ProfileService , public profileService: ProfileService
, public params: NavParams , public params: NavParams
) ) {
{
} }
ngOnInit(): void ngOnInit(): void {
{
let t = this.profileService.profile();
// Check if there is a profile saved in local storage
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() =>
{
this.initializeItems(t);
if (this.profileService.localIsLoaded) {
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() => {
let t = this.profileService.profile();
this.initializeItems(t);
this.loader.dismiss(); this.loader.dismiss();
}); });
}
else
{
this.profileService.onLocalStorageLoaded.subscribe(t => {
// Check if there is a profile saved in local storage
this.loader = this.loadingCtrl.create({ content: 'Loading Page...' });
this.loader.present().then(() => {
this.initializeItems(t);
this.loader.dismiss();
});
});
this.profileService.onSavedPagesChanged.subscribe(sp => {
this.pagesService.initializePages(sp);
});
}
} }
initializeItems(u: User) initializeItems(u: User) {
{
// migrate old way of storing card items to the new. // migrate old way of storing card items to the new.
let has_migrated = false; let has_migrated = false;
for (let i in u.items) for (let i in u.items) {
{ if (u.items.hasOwnProperty(i)) {
if (u.items.hasOwnProperty(i))
{
let ci = u.items[i]; let ci = u.items[i];
if (ci['data'] !== undefined) if (ci['data'] !== undefined) {
{
if (ci['data'].qry !== undefined) if (ci['data'].qry !== undefined)
u.items[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type }; u.items[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type };
else if (ci['data'].ref !== undefined) else if (ci['data'].ref !== undefined)
@ -73,15 +81,11 @@ export class SearchPage implements OnInit
} }
} }
for (let pg of u.saved_pages) for (let pg of u.saved_pages) {
{ for (let i in pg.queries) {
for (let i in pg.queries) if (pg.queries.hasOwnProperty(i)) {
{
if (pg.queries.hasOwnProperty(i))
{
let ci = pg.queries[i]; let ci = pg.queries[i];
if (ci['data'] !== undefined) if (ci['data'] !== undefined) {
{
if (ci['data'].qry !== undefined) if (ci['data'].qry !== undefined)
pg.queries[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type }; pg.queries[i] = { qry: ci['data'].qry, dict: ci.dict, type: ci.type };
else if (ci['data'].ref !== undefined) else if (ci['data'].ref !== undefined)
@ -105,7 +109,7 @@ export class SearchPage implements OnInit
this.pagesService.initializePages(u.saved_pages); this.pagesService.initializePages(u.saved_pages);
if (this.params.data.queries !== undefined) if (this.params.data.queries !== undefined)
this.profileService.profile().items = this.params.data.queries.slice(); this.profileService.profile().items = JSON.parse(JSON.stringify(this.params.data.queries));
if (this.params.data.title === undefined) if (this.params.data.title === undefined)
this.title = 'Search'; this.title = 'Search';
@ -114,25 +118,19 @@ export class SearchPage implements OnInit
if (has_migrated) if (has_migrated)
this.profileService.save(); this.profileService.save();
} }
save() textSizeChanged() {
{
this.profileService.save();
}
textSizeChanged()
{
this.profileService.textSizeChanged(); this.profileService.textSizeChanged();
this.save(); this.profileService.localSave();
} }
actionsMenu() actionsMenu() {
{
this.menu.open('actions'); this.menu.open('actions');
} }
addPage() addPage() {
{
const alert = this.alertCtrl.create({ const alert = this.alertCtrl.create({
title: 'Save Search as Page', title: 'Save Search as Page',
inputs: [ inputs: [
@ -145,15 +143,13 @@ export class SearchPage implements OnInit
{ {
text: 'Cancel', text: 'Cancel',
role: 'cancel', role: 'cancel',
handler: (): void => handler: (): void => {
{
console.log('Cancel clicked'); console.log('Cancel clicked');
} }
}, },
{ {
text: 'Save', text: 'Save',
handler: data => handler: data => {
{
const p = { queries: this.profileService.profile().items.slice(), title: data.title }; const p = { queries: this.profileService.profile().items.slice(), title: data.title };
this.profileService.profile().saved_pages.push(p); this.profileService.profile().saved_pages.push(p);
this.profileService.save(); this.profileService.save();
@ -165,71 +161,58 @@ export class SearchPage implements OnInit
alert.present(); alert.present();
} }
updatePage() updatePage() {
{
const page = this.profileService.profile().saved_pages.find( const page = this.profileService.profile().saved_pages.find(
i => i =>
i.title === this.params.data.title i.title === this.params.data.title
); );
page.queries = this.profileService.profile().items.slice(); page.queries = this.profileService.profile().items.slice();
this.profileService.save(); this.profileService.save();
} }
setQuery(searchbar) setQuery(searchbar) {
{
this.searchQuery = searchbar.target.value; this.searchQuery = searchbar.target.value;
} }
getQuery(searchbar) getQuery(searchbar) {
{
this.updateUIwithItems(this.searchQuery, true); this.updateUIwithItems(this.searchQuery, true);
} }
isError(t: string) isError(t: string) {
{
return t === 'Error'; return t === 'Error';
} }
isPassage(t: string) isPassage(t: string) {
{
return t === 'Passage'; return t === 'Passage';
} }
isStrongs(t: string) isStrongs(t: string) {
{
return t === 'Strongs'; return t === 'Strongs';
} }
isWords(t: string) isWords(t: string) {
{
return t === 'Words'; return t === 'Words';
} }
versePicker() versePicker() {
{
const modal = this.modalCtrl.create(VersePickerModal, { onItemClicked: this }); const modal = this.modalCtrl.create(VersePickerModal, { onItemClicked: this });
modal.present(); modal.present();
} }
removeItem(item) removeItem(item) {
{
const idx = this.profileService.profile().items.indexOf(item); const idx = this.profileService.profile().items.indexOf(item);
this.profileService.profile().items.splice(idx, 1); this.profileService.profile().items.splice(idx, 1);
// save the users settings. // save the users settings.
this.profileService.save(); this.profileService.localSave();
} }
addItemToList(item: CardItem)
{ addItemToList(item: CardItem) {
if (this.profileService.profile().append_to_bottom) if (this.profileService.profile().append_to_bottom) {
{ if (this.last != null && this.profileService.profile().insert_next_to_item) {
if (this.last != null && this.profileService.profile().insert_next_to_item)
{
const idx = this.profileService.profile().items.indexOf(this.last); const idx = this.profileService.profile().items.indexOf(this.last);
this.profileService.profile().items.splice(idx + 1, 0, item); this.profileService.profile().items.splice(idx + 1, 0, item);
} else } else
this.profileService.profile().items.push(item); this.profileService.profile().items.push(item);
} }
else else {
{ if (this.last != null && this.profileService.profile().insert_next_to_item) {
if (this.last != null && this.profileService.profile().insert_next_to_item)
{
const idx = this.profileService.profile().items.indexOf(this.last); const idx = this.profileService.profile().items.indexOf(this.last);
this.profileService.profile().items.splice(idx, 0, item); this.profileService.profile().items.splice(idx, 0, item);
} else } else
@ -237,33 +220,25 @@ export class SearchPage implements OnInit
} }
this.last = null; this.last = null;
} }
getItemsNextToCard(data: OpenData) getItemsNextToCard(data: OpenData) {
{
this.last = data.card; this.last = data.card;
this.updateUIwithItems(data.qry, data.from_search_bar); this.updateUIwithItems(data.qry, data.from_search_bar);
} }
getItemList(search: string): Promise<CardItem[]> getItemList(search: string): Promise<CardItem[]> {
{ return new Promise((resolve) => {
return new Promise((resolve) =>
{
const list: CardItem[] = []; const list: CardItem[] = [];
try try {
{
const qs = search.split(';'); const qs = search.split(';');
for (let x in qs) for (let x in qs) {
{ if (qs.hasOwnProperty(x)) {
if (qs.hasOwnProperty(x))
{
let q = qs[x].trim(); let q = qs[x].trim();
if (q !== '') if (q !== '') {
{
// its a search term. // its a search term.
if (q.search(/[0-9]/i) === -1) if (q.search(/[0-9]/i) === -1)
list.push({ qry: q, dict: 'na', type: 'Words' }); list.push({ qry: q, dict: 'na', type: 'Words' });
else if (q.search(/(H|G)[0-9]/i) !== -1) else if (q.search(/(H|G)[0-9]/i) !== -1) {
{
// its a strongs lookup // its a strongs lookup
let dict = q.substring(0, 1); let dict = q.substring(0, 1);
@ -275,11 +250,9 @@ export class SearchPage implements OnInit
q = q.substring(1, q.length); q = q.substring(1, q.length);
list.push({ qry: q, dict: dict, type: 'Strongs' }); list.push({ qry: q, dict: dict, type: 'Strongs' });
} }
else else {
{
// its a verse reference. // its a verse reference.
if (q.trim() !== '') if (q.trim() !== '') {
{
const myref = new Reference(q.trim()); const myref = new Reference(q.trim());
list.push({ qry: myref.toString(), dict: myref.Section.start.book.book_number > 39 ? 'G' : 'H', type: 'Passage' }); list.push({ qry: myref.toString(), dict: myref.Section.start.book.book_number > 39 ? 'G' : 'H', type: 'Passage' });
} }
@ -292,8 +265,7 @@ export class SearchPage implements OnInit
this.profileService.save(); this.profileService.save();
} }
catch (error) catch (error) {
{
list.push({ qry: error, type: 'Error', dict: 'na' }); list.push({ qry: error, type: 'Error', dict: 'na' });
console.log(error); console.log(error);
} }
@ -302,18 +274,13 @@ export class SearchPage implements OnInit
}); });
} }
updateUIwithItems(search: string, from_search_bar: boolean) updateUIwithItems(search: string, from_search_bar: boolean) {
{ this.getItemList(search).then(lst => {
this.getItemList(search).then(lst =>
{
this.loader = this.loadingCtrl.create({ content: 'Looking up Query...' }); this.loader = this.loadingCtrl.create({ content: 'Looking up Query...' });
this.loader.present().then( this.loader.present().then(
() => () => {
{ for (let item of lst) {
for (let item of lst) if (item.type === 'Strongs' && this.profileService.profile().strongs_modal && !from_search_bar) {
{
if (item.type === 'Strongs' && this.profileService.profile().strongs_modal && !from_search_bar)
{
const modal = this.modalCtrl.create(StrongsModal, { sn: parseInt(item.qry), dict: item.dict, onItemClicked: this }); const modal = this.modalCtrl.create(StrongsModal, { sn: parseInt(item.qry), dict: item.dict, onItemClicked: this });
modal.present(); modal.present();
} else } else
@ -330,8 +297,7 @@ export type OpenData = { card: CardItem, qry: string, from_search_bar: boolean }
export type CardItem = { qry: string, type: string, dict: string } export type CardItem = { qry: string, type: string, dict: string }
class Item class Item {
{
id: number; id: number;
data: any; data: any;
type: Type<any>; type: Type<any>;

View File

@ -9,7 +9,7 @@
<ion-content padding> <ion-content padding>
<ng-template [ngIf]="profileService.profile()"> <ng-template [ngIf]="profileService.profile()">
<h4>Search Settings</h4> <h4>Search Settings</h4>
{{profileService.isWeb}} : {{profileService.url}}
<ng-template [ngIf]="!profileService.currentUser()"> <ng-template [ngIf]="!profileService.currentUser()">
<ion-item> <ion-item>
<button ion-button (click)="profileService.authenticate()">Login With Google</button> <button ion-button (click)="profileService.authenticate()">Login With Google</button>

View File

@ -20,12 +20,7 @@ export class SettingsPage
textSizeChanged() textSizeChanged()
{ {
this.profileService.textSizeChanged(); this.profileService.textSizeChanged();
this.save(); this.profileService.localSave();
}
save()
{
this.profileService.save()
} }
reset() reset()

View File

@ -11,45 +11,36 @@ import { CardItem } from '../pages/search/search';
import { Promise } from 'q'; import { Promise } from 'q';
import { setTimeout } from 'timers'; import { setTimeout } from 'timers';
type fbObject<T> = { import { Output, EventEmitter } from '@angular/core';
ref: AngularFireObject<T>,
stream: Observable<T>,
};
export const DEFAULT_USER_NAME = 'john_doe'; export const DEFAULT_USER_NAME = 'john_doe';
export type User = {
username: string,
uid: string | null,
strongs_modal: boolean,
clear_search_after_query: boolean,
items: CardItem[],
append_to_bottom: boolean,
insert_next_to_item: boolean,
font_size: number,
saved_pages: SavedPage[],
verses_on_new_line: boolean,
show_verse_numbers: boolean,
show_paragraphs: boolean,
show_paragraph_headings: boolean,
}
export type SavedPage = {
queries: CardItem[],
title: string,
}
@Injectable() @Injectable()
export class ProfileService export class ProfileService
{ {
@Output()
onSavedPagesChanged = new EventEmitter<SavedPage[]>();
@Output()
onLocalStorageLoaded = new EventEmitter<User>();
localProfile: User; localProfile: User;
remoteProfile: fbObject<User> | null; remoteProfile: fbObject<User> | null;
remoteLoggedIn: boolean;
isWeb: boolean; isWeb: boolean;
url: string;
needsSync = false; needsSync = false;
firebaseUser: firebase.User;
remoteLoggedIn: boolean;
localIsLoaded: boolean;
constructor(private local: Storage, private db: AngularFireDatabase, public firebaseAuth: AngularFireAuth) constructor(
private local: Storage,
private db: AngularFireDatabase,
public firebaseAuth: AngularFireAuth
)
{ {
this.isWeb = (document.URL.startsWith('http') || !document.URL.startsWith('http://localhost:8080')); this.url = document.URL;
this.isWeb = (document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080'));
this.localIsLoaded = false;
// asyncrounosly kick off a poller that does the work of syncing remotely when the // asyncrounosly kick off a poller that does the work of syncing remotely when the
// profile needs to be synced. // profile needs to be synced.
@ -66,36 +57,29 @@ export class ProfileService
{ {
let st = new Date(); console.log('Saving the remote profile...'); let st = new Date(); console.log('Saving the remote profile...');
self.remoteProfile.ref.set(self.localProfile); self.remoteProfile.ref.set(self.localProfile);
console.log('Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms'); console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms');
} }
self.needsSync = false; self.needsSync = false;
} }
poll(self); poll(self);
}, 3000); }, 10000);
})(this); })(this);
let localObserver = this.userObserver().subscribe( this.local.get('profile').then(json_profile =>
user =>
{
this.update(user);
this.localProfile = user;
},
error => console.log(error)
);
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
}
userObserver(): Observable<User>
{
return Observable.fromPromise(this.local.get('profile')).map(json_profile =>
{ {
let t = this.profile(); let t = this.profile();
if (json_profile !== null) t = JSON.parse(json_profile); if (json_profile !== null)
return t; t = JSON.parse(json_profile);
this.localProfile = t;
this.localIsLoaded = true;
this.onLocalStorageLoaded.emit(this.localProfile);
}); });
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
} }
profile(): User profile(): User
@ -105,50 +89,141 @@ export class ProfileService
this.localProfile = ProfileService.createDefaultUser(); this.localProfile = ProfileService.createDefaultUser();
} }
return this.localProfile return this.localProfile;
} }
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User) subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User)
{ {
if (!user) return; console.log('subscribeToRemoteProfile');
if (!user || this.firebaseUser) return;
console.log('You got the firebase user.');
let obj = db.object('/settings/' + user.uid); let obj = db.object('/settings/' + user.uid);
this.remoteProfile = { this.remoteProfile = {
ref: obj as AngularFireObject<User>, ref: obj as AngularFireObject<User>,
stream: obj.valueChanges() as Observable<User>, stream: obj.valueChanges() as Observable<User>,
}; };
this.firebaseUser = user;
this.profile().username = user.displayName; this.profile().username = user.displayName;
this.profile().uid = user.uid; this.profile().uid = user.uid;
this.save();
this.remoteProfile.stream.subscribe( this.remoteProfile.stream.subscribe(
user => this.handleRemotePreferenceChange(user), user => this.handleRemotePreferenceChange(user),
error => console.log(error)); error => console.log(error));
} }
comparePage(a: SavedPage, b: SavedPage)
{
if (a.title > b.title)
return 1;
if (a.title === b.title)
return 0;
if (a.title < b.title)
return -1;
}
private returnYonly(a: SavedPage[], b: SavedPage[])
{
let r: SavedPage[] = [];
if (b === undefined)
return r;
if (a !== undefined && a.length === 0 && b !== undefined && b.length > 0)
return [...b];
let x = [...a];
let y = [...b];
/// <summary>
/// Takes two javascript arrays and returns an array
/// containing a set of values shared by arrays.
/// </summary>
// declare iterator
let i = 0;
// declare terminator
let t = (x.length < y.length) ? x.length : y.length;
// sort the arrays
x.sort(this.comparePage);
y.sort(this.comparePage);
// in this loop, we remove from the arrays, the
// values that aren't shared between them.
while (i < t)
{
if (x[i].title === y[i].title)
{
i++;
}
if (x.length > i && y.length > i && x[i].title < y[i].title)
x.splice(i, 1);
if (x.length > i && y.length > i && x[i].title > y[i].title)
{
r.unshift(y[i]);
y.splice(i, 1);
}
t = (x.length < y.length) ? x.length : y.length;
if (t === i && t < y.length)
{
r = r.concat(y);
}
}
// we could return y, because at this time, both arrays
// are identical.
return r;
}
handleRemotePreferenceChange(user: User) handleRemotePreferenceChange(user: User)
{ {
console.log('handleRemotePreferenceChange');
if (user) if (user)
{ {
if (!user.saved_pages) user.saved_pages = []; let changed = false;
if (!user.items) user.items = []; let local_was_empty = this.profile().saved_pages.length === 0;
// merge the saved pages so you don't loose those either // merge the saved pages so you don't loose those either
if (this.profile().saved_pages.length > 0) // don't add if they are the same.
user.saved_pages.concat(this.localProfile.saved_pages); let ys = this.returnYonly(this.profile().saved_pages, user.saved_pages);
if (ys.length > 0)
{
this.localProfile.saved_pages = this.localProfile.saved_pages.concat(ys);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
if (!local_was_empty)
{
changed = true;
this.needsSync = true;
}
}
if (this.profile().saved_pages.length > 0 && user.saved_pages === undefined)
{
changed = true;
this.needsSync = true;
}
// don't sync things that don't make sense. // don't sync things that don't make sense.
this.profile().uid = user.uid; if (this.profile().uid !== user.uid)
this.profile().username = user.username; {
this.profile().saved_pages = user.saved_pages; this.profile().uid = user.uid;
changed = true;
}
if (this.profile().username !== user.username)
{
this.profile().username = user.username;
changed = true;
}
// We only save the local change here since this is an update from our remote profile. // We only save the local change here since this is an update from our remote profile.
this.localSave(); if (changed)
{
this.localSave();
}
} }
else else
{ {
// No user is there so we should save our local to the remote.
this.save(); this.save();
} }
} }
@ -160,6 +235,8 @@ export class ProfileService
authenticate() authenticate()
{ {
console.log('Authenticating to remote...');
let self = this; let self = this;
let provider = new firebase.auth.GoogleAuthProvider(); let provider = new firebase.auth.GoogleAuthProvider();
if (this.isWeb) if (this.isWeb)
@ -184,12 +261,14 @@ export class ProfileService
refresh() refresh()
{ {
console.log('refresh');
this.logout(); this.logout();
this.authenticate(); this.authenticate();
} }
logout() logout()
{ {
console.log('logout');
this.firebaseAuth.auth.signOut(); // sign out this.firebaseAuth.auth.signOut(); // sign out
this.remoteProfile = null; // inform the profile service not to bother this.remoteProfile = null; // inform the profile service not to bother
this.remoteLoggedIn = false; this.remoteLoggedIn = false;
@ -201,8 +280,9 @@ export class ProfileService
this.needsSync = true; this.needsSync = true;
} }
private localSave() localSave()
{ {
console.log('saving local');
this.local.set('profile', JSON.stringify(this.profile())); this.local.set('profile', JSON.stringify(this.profile()));
} }
@ -267,6 +347,7 @@ export class ProfileService
{ {
let idx = this.profile().saved_pages.indexOf(page); let idx = this.profile().saved_pages.indexOf(page);
this.profile().saved_pages.splice(idx, 1); this.profile().saved_pages.splice(idx, 1);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
// Sync with remote // Sync with remote
this.save(); this.save();
@ -300,3 +381,30 @@ export class ProfileService
} }
} }
type fbObject<T> = {
ref: AngularFireObject<T>,
stream: Observable<T>,
};
export type User = {
username: string,
uid: string | null,
strongs_modal: boolean,
clear_search_after_query: boolean,
items: CardItem[],
append_to_bottom: boolean,
insert_next_to_item: boolean,
font_size: number,
saved_pages: SavedPage[],
verses_on_new_line: boolean,
show_verse_numbers: boolean,
show_paragraphs: boolean,
show_paragraph_headings: boolean,
}
export type SavedPage = {
queries: CardItem[],
title: string,
}