From 91a2cc15f7feefeb04b4cf545aab17e868a50dbd Mon Sep 17 00:00:00 2001 From: Kosh Date: Sat, 3 Jun 2017 13:16:08 +0800 Subject: [PATCH 01/14] this commit fixes #597 improves trending overall and some layouts improvements --- README.md | 2 + .../data/dao/FragmentPagerAdapterModel.java | 7 +- .../fastaccess/data/dao/TimelineModel.java | 5 + .../data/service/TrendingService.java | 1 + .../provider/colors/ColorsProvider.java | 5 + .../provider/rest/RestProvider.java | 2 +- .../AuthenticationInterceptor.java | 5 +- .../provider/timeline/HtmlHelper.java | 75 +-------- .../ui/adapter/IssuePullsTimelineAdapter.java | 6 +- .../viewholder/IssueDetailsViewHolder.java | 43 ++--- .../ui/modules/editor/EditorActivity.java | 69 ++++---- .../ui/modules/repos/RepoPagerActivity.java | 4 +- .../commit/details/CommitPagerActivity.java | 5 +- .../issues/create/CreateIssueActivity.java | 4 +- .../issues/create/CreateIssuePresenter.java | 3 + .../issue/details/IssuePagerActivity.java | 105 +++++++------ .../issues/issue/details/IssuePagerMvp.java | 9 +- .../issue/details/IssuePagerPresenter.java | 56 +++---- .../timeline/IssueTimelineFragment.java | 92 +++++++---- .../details/timeline/IssueTimelineMvp.java | 18 +-- .../timeline/IssueTimelinePresenter.java | 75 +++------ .../details/PullRequestPagerActivity.java | 147 ++++++++++-------- .../details/PullRequestPagerMvp.java | 10 +- .../details/PullRequestPagerPresenter.java | 86 +++++----- .../timeline/PullRequestTimelineFragment.java | 116 +++++++++----- .../timeline/PullRequestTimelineMvp.java | 20 +-- .../PullRequestTimelinePresenter.java | 92 ++++------- .../ui/modules/trending/TrendingActivity.kt | 6 +- .../ui/modules/trending/TrendingPresenter.kt | 6 +- .../fastaccess/ui/widgets/DiffLineSpan.java | 13 +- .../row_layouts/layout/comments_row_item.xml | 6 +- .../layout/issue_detail_header_row_item.xml | 6 +- .../layout/no_emojies_comments_row_item.xml | 6 +- .../layout/review_comments_row_item.xml | 6 +- app/src/main/res/values/strings.xml | 3 + debug_gradle.properties | 3 +- 36 files changed, 560 insertions(+), 557 deletions(-) diff --git a/README.md b/README.md index ebcf5523..46454a2a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ grab **FastHub** from [Here](https://github.com/thermatk/FastHub-Libre) maintain - Notifications overview and "Mark all as read" - Search users/orgs, repos, issues/prs & code. - Pinned Repos + - Trending - **Repositories** - Search Repos - Browse and search Repos @@ -81,6 +82,7 @@ Ads are no longer available until further notice # Specs / Open-source libraries: - Minimum **SDK 21**, _but AppCompat is used all the way ;-)_ +- **Kotlin** all new modules starting from 2.5.3 will be written in **#Kotlin**. - **MVP**-architecture: [**ThirtyInch**](https://github.com/grandcentrix/ThirtyInch) because its ThirtyInch. - [**RxJava2**](https://github.com/ReactiveX/RxJava) & [**RxAndroid**](https://github.com/ReactiveX/RxAndroid) for Retrofit & background threads - [**Retrofit**](https://github.com/square/retrofit) for constructing the REST API diff --git a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java index 32a1985a..4412f896 100644 --- a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java @@ -9,7 +9,6 @@ import com.annimon.stream.Stream; import com.fastaccess.R; import com.fastaccess.data.dao.model.Commit; import com.fastaccess.data.dao.model.Gist; -import com.fastaccess.data.dao.model.Issue; import com.fastaccess.data.dao.model.Login; import com.fastaccess.data.dao.model.PullRequest; import com.fastaccess.data.dao.types.IssueState; @@ -101,8 +100,8 @@ import lombok.Setter; .collect(Collectors.toList()); } - @NonNull public static List buildForIssues(@NonNull Context context, @NonNull Issue issueModel) { - return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.details), IssueTimelineFragment.newInstance(issueModel))) + @NonNull public static List buildForIssues(@NonNull Context context) { + return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.details), IssueTimelineFragment.newInstance())) .collect(Collectors.toList()); } @@ -111,7 +110,7 @@ import lombok.Setter; String login = pullRequest.getLogin(); String repoId = pullRequest.getRepoId(); int number = pullRequest.getNumber(); - return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.details), PullRequestTimelineFragment.newInstance(pullRequest)), + return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.details), PullRequestTimelineFragment.newInstance()), new FragmentPagerAdapterModel(context.getString(R.string.commits), PullRequestCommitsFragment.newInstance(repoId, login, number)), new FragmentPagerAdapterModel(context.getString(R.string.files), PullRequestFilesFragment.newInstance(repoId, login, number))) .collect(Collectors.toList()); diff --git a/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java b/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java index 9b9f2dfc..98b15d70 100644 --- a/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/TimelineModel.java @@ -280,6 +280,7 @@ import static com.annimon.stream.Collectors.toList; return comment != null ? (int) comment.getId() : 0; } + @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { @@ -290,6 +291,8 @@ import static com.annimon.stream.Collectors.toList; dest.writeParcelable(this.pullRequest, flags); dest.writeParcelable(this.status, flags); dest.writeParcelable(this.review, flags); + dest.writeParcelable(this.groupedReview, flags); + dest.writeParcelable(this.reviewComment, flags); dest.writeLong(this.sortedDate != null ? this.sortedDate.getTime() : -1); } @@ -301,6 +304,8 @@ import static com.annimon.stream.Collectors.toList; this.pullRequest = in.readParcelable(PullRequest.class.getClassLoader()); this.status = in.readParcelable(PullRequestStatusModel.class.getClassLoader()); this.review = in.readParcelable(ReviewModel.class.getClassLoader()); + this.groupedReview = in.readParcelable(GroupedReviewModel.class.getClassLoader()); + this.reviewComment = in.readParcelable(ReviewCommentModel.class.getClassLoader()); long tmpSortedDate = in.readLong(); this.sortedDate = tmpSortedDate == -1 ? null : new Date(tmpSortedDate); } diff --git a/app/src/main/java/com/fastaccess/data/service/TrendingService.java b/app/src/main/java/com/fastaccess/data/service/TrendingService.java index 5f39e54c..9e625e7a 100644 --- a/app/src/main/java/com/fastaccess/data/service/TrendingService.java +++ b/app/src/main/java/com/fastaccess/data/service/TrendingService.java @@ -1,5 +1,6 @@ package com.fastaccess.data.service; + import com.fastaccess.data.dao.TrendingResponse; import com.github.florent37.retrojsoup.annotations.Select; diff --git a/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java b/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java index 8490b5ea..d8127261 100644 --- a/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java +++ b/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java @@ -23,6 +23,7 @@ import java.io.InputStreamReader; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import io.reactivex.Observable; @@ -33,6 +34,9 @@ import io.reactivex.Observable; public class ColorsProvider { + private static List popularLanguages = Stream.of("Java", "Kotlin", "Javascript", "Python", "CSS", "PHP", + "Ruby", "C++", "C", "GO", "Swift").toList();//predefined languages. + private static Map colors = new LinkedHashMap<>(); public static void load() { @@ -60,6 +64,7 @@ public class ColorsProvider { return Stream.of(colors) .filter(value -> value != null && !InputHelper.isEmpty(value.getKey())) .map(Map.Entry::getKey) + .sortBy(s -> !popularLanguages.contains(s)) .collect(Collectors.toCollection(ArrayList::new)); } diff --git a/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java b/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java index 467b0e3d..13645339 100644 --- a/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java +++ b/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java @@ -60,7 +60,7 @@ public class RestProvider { .setPrettyPrinting() .create(); - public static OkHttpClient provideOkHttpClient(boolean isRawString) { + private static OkHttpClient provideOkHttpClient(boolean isRawString) { if (okHttpClient == null) { OkHttpClient.Builder client = new OkHttpClient.Builder(); if (BuildConfig.DEBUG) { diff --git a/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java b/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java index 273d6acb..70c15ffe 100644 --- a/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java +++ b/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java @@ -1,5 +1,7 @@ package com.fastaccess.provider.rest.interceptors; +import android.support.annotation.NonNull; + import com.fastaccess.data.service.NotificationService; import com.fastaccess.helper.InputHelper; @@ -18,7 +20,7 @@ public class AuthenticationInterceptor implements Interceptor { private String authToken; private String otp; - @Override public Response intercept(Chain chain) throws IOException { + @Override public Response intercept(@NonNull Chain chain) throws IOException { Request original = chain.request(); if (original.url() != HttpUrl.get(URI.create(NotificationService.SUBSCRIPTION_URL))) { Request.Builder builder = original.newBuilder(); @@ -28,6 +30,7 @@ public class AuthenticationInterceptor implements Interceptor { if (!InputHelper.isEmpty(otp)) { builder.addHeader("X-GitHub-OTP", otp.trim()); } + builder.addHeader("User-Agent", "FastHub"); Request request = builder.build(); return chain.proceed(request); } diff --git a/app/src/main/java/com/fastaccess/provider/timeline/HtmlHelper.java b/app/src/main/java/com/fastaccess/provider/timeline/HtmlHelper.java index ddc64214..a30d215b 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/HtmlHelper.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/HtmlHelper.java @@ -90,12 +90,6 @@ public class HtmlHelper { return mySpanner; } - private static final String TAG_ROOT = "githubroot"; - - private static final String ROOT_START = '<' + TAG_ROOT + '>'; - - private static final String ROOT_END = "'; - private static final String TOGGLE_START = ""; private static final String TOGGLE_END = ""; @@ -122,23 +116,16 @@ public class HtmlHelper { private static final String PARAGRAPH_END = "

"; - private static final String BLOCKQUOTE_START = "
"; - - private static final String BLOCKQUOTE_END = "
"; - //https://github.com/k0shk0sh/GitHubSdk/blob/master/library/src/main/java/com/meisolsson/githubsdk/core/HtmlUtils.java @NonNull public static CharSequence format(final String html) { if (html == null || html.length() == 0) return ""; StringBuilder formatted = new StringBuilder(html); strip(formatted, TOGGLE_START, TOGGLE_END); strip(formatted, SIGNATURE_START, SIGNATURE_END); - replace(formatted, REPLY_START, REPLY_END, BLOCKQUOTE_START, BLOCKQUOTE_END); + strip(formatted, REPLY_START, REPLY_END); strip(formatted, HIDDEN_REPLY_START, HIDDEN_REPLY_END); if (replace(formatted, PARAGRAPH_START, BREAK)) replace(formatted, PARAGRAPH_END, BREAK); - formatEmailFragments(formatted); trim(formatted); - formatted.insert(0, ROOT_START); - formatted.append(ROOT_END); return formatted; } @@ -165,65 +152,15 @@ public class HtmlHelper { return true; } - private static void replace(final StringBuilder input, final String fromStart, final String fromEnd, - final String toStart, final String toEnd) { - int start = input.indexOf(fromStart); - if (start == -1) - return; - final int fromStartLength = fromStart.length(); - final int fromEndLength = fromEnd.length(); - final int toStartLength = toStart.length(); - while (start != -1) { - input.replace(start, start + fromStartLength, toStart); - int end = input.indexOf(fromEnd, start + toStartLength); - if (end != -1) - input.replace(end, end + fromEndLength, toEnd); - - start = input.indexOf(fromStart); - } - } - - private static void formatEmailFragments(final StringBuilder input) { - int emailStart = input.indexOf(EMAIL_START); - int breakAdvance = BREAK.length() - 1; - while (emailStart != -1) { - int startLength = EMAIL_START.length(); - int emailEnd = input.indexOf(EMAIL_END, emailStart + startLength); - if (emailEnd == -1) - break; - - input.delete(emailEnd, emailEnd + EMAIL_END.length()); - input.delete(emailStart, emailStart + startLength); - - int fullEmail = emailEnd - startLength; - for (int i = emailStart; i < fullEmail; i++) - if (input.charAt(i) == '\n') { - input.deleteCharAt(i); - input.insert(i, BREAK); - i += breakAdvance; - fullEmail += breakAdvance; - } - - emailStart = input.indexOf(EMAIL_START, fullEmail); - } - } - private static void trim(final StringBuilder input) { int length = input.length(); int breakLength = BREAK.length(); - while (length > 0) { - if (input.indexOf(BREAK) == 0) - input.delete(0, breakLength); - else if (length >= breakLength - && input.lastIndexOf(BREAK) == length - breakLength) - input.delete(length - breakLength, length); - else if (Character.isWhitespace(input.charAt(0))) - input.deleteCharAt(0); - else if (Character.isWhitespace(input.charAt(length - 1))) - input.deleteCharAt(length - 1); - else - break; + if (input.indexOf(BREAK) == 0) input.delete(0, breakLength); + else if (length >= breakLength && input.lastIndexOf(BREAK) == length - breakLength) input.delete(length - breakLength, length); + else if (Character.isWhitespace(input.charAt(0))) input.deleteCharAt(0); + else if (Character.isWhitespace(input.charAt(length - 1))) input.deleteCharAt(length - 1); + else break; length = input.length(); } } diff --git a/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java index 69fae96a..6ba0b835 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/IssuePullsTimelineAdapter.java @@ -66,11 +66,7 @@ public class IssuePullsTimelineAdapter extends BaseRecyclerAdapter { } private void bind(@NonNull Issue issueModel) { - avatar.setUrl(issueModel.getUser().getAvatarUrl(), issueModel.getUser().getLogin()); - name.setText(issueModel.getUser().getLogin()); - date.setText(ParseDateFormat.getTimeAgo(issueModel.getCreatedAt())); - if (!InputHelper.isEmpty(issueModel.getBodyHtml())) { - HtmlHelper.htmlIntoTextView(comment, issueModel.getBodyHtml()); - } else { - comment.setText(R.string.no_description_provided); - } - if (issueModel.getReactions() != null) { - appendEmojies(issueModel.getReactions()); - } + setup(issueModel.getUser(), issueModel.getBodyHtml(), issueModel.getReactions()); + setupDate(issueModel.getCreatedAt(), issueModel.getUpdatedAt()); } private void bind(@NonNull PullRequest pullRequest) { - avatar.setUrl(pullRequest.getUser().getAvatarUrl(), pullRequest.getUser().getLogin()); - name.setText(pullRequest.getUser().getLogin()); - date.setText(ParseDateFormat.getTimeAgo(pullRequest.getCreatedAt())); - if (!InputHelper.isEmpty(pullRequest.getBodyHtml())) { - HtmlHelper.htmlIntoTextView(comment, pullRequest.getBodyHtml()); + setup(pullRequest.getUser(), pullRequest.getBodyHtml(), pullRequest.getReactions()); + setupDate(pullRequest.getCreatedAt(), pullRequest.getUpdatedAt()); + } + + private void setup(User user, String description, ReactionsModel reactionsModel) { + avatar.setUrl(user.getAvatarUrl(), user.getLogin(), user.isOrganizationType()); + name.setText(user.getLogin()); + if (reactionsModel != null) { + appendEmojies(reactionsModel); + } + if (!InputHelper.isEmpty(description)) { + HtmlHelper.htmlIntoTextView(comment, description); } else { comment.setText(R.string.no_description_provided); } - if (pullRequest.getReactions() != null) { - appendEmojies(pullRequest.getReactions()); + } + + private void setupDate(@NonNull Date createdDate, @NonNull Date updated) { + if (createdDate.before(updated)) { + date.setText(String.format("%s %s", ParseDateFormat.getTimeAgo(updated), + date.getResources().getString(R.string.edited))); + } else { + date.setText(ParseDateFormat.getTimeAgo(createdDate)); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/editor/EditorActivity.java b/app/src/main/java/com/fastaccess/ui/modules/editor/EditorActivity.java index 5c209f5e..ac0dfb02 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/editor/EditorActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/editor/EditorActivity.java @@ -18,6 +18,7 @@ import android.widget.CheckBox; import android.widget.LinearLayout; import android.widget.ListView; +import com.evernote.android.state.State; import com.fastaccess.BuildConfig; import com.fastaccess.R; import com.fastaccess.data.dao.EditReviewCommentModel; @@ -46,7 +47,6 @@ import butterknife.OnClick; import butterknife.OnItemClick; import butterknife.OnTextChanged; import es.dmoral.toasty.Toasty; -import com.evernote.android.state.State; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; import static android.view.View.GONE; @@ -70,16 +70,15 @@ public class EditorActivity extends BaseActivity mentions = new ArrayList<>(); diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java index 97211b97..dd44fcda 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/RepoPagerActivity.java @@ -20,6 +20,7 @@ import android.view.MotionEvent; import android.view.View; import android.widget.CheckBox; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.LicenseModel; import com.fastaccess.data.dao.NameParser; @@ -58,7 +59,6 @@ import butterknife.BindView; import butterknife.OnCheckedChanged; import butterknife.OnClick; import butterknife.OnLongClick; -import com.evernote.android.state.State; import it.sephiroth.android.library.bottomnavigation.BottomNavigation; import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt; @@ -313,7 +313,7 @@ public class RepoPagerActivity extends BaseActivity imp if (intent != null && intent.getExtras() != null) { CharSequence charSequence = intent.getExtras().getCharSequence(BundleConstant.EXTRA); if (!InputHelper.isEmpty(charSequence)) { + Logger.e(charSequence); sendToView(view -> view.onSetCode(charSequence)); } } @@ -61,6 +63,7 @@ public class CreateIssuePresenter extends BasePresenter imp makeRestCall(RestProvider.getIssueService().editIssue(login, repo, number, requestModel), issueModel -> { if (issueModel != null) { + Logger.e(issueModel.getBodyHtml()); sendToView(view -> view.onSuccessSubmission(issueModel)); } else { sendToView(view -> view.showMessage(R.string.error, R.string.error_creating_issue)); diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java index 5310ee01..b1bc6d07 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java @@ -13,6 +13,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.FragmentPagerAdapterModel; import com.fastaccess.data.dao.LabelModel; @@ -47,7 +48,6 @@ import java.util.List; import butterknife.BindView; import butterknife.OnClick; -import com.evernote.android.state.State; /** * Created by Kosh on 10 Dec 2016, 9:23 AM @@ -126,7 +126,7 @@ public class IssuePagerActivity extends BaseActivity { + void onSetupIssue(boolean isUpdate); void showSuccessIssueActionMsg(boolean isClose); @@ -39,6 +39,7 @@ public interface IssuePagerMvp { void onMileStoneSelected(@NonNull MilestoneModel milestoneModel); void onFinishActivity(); + } interface Presenter extends BaseMvp.FAPresenter { @@ -82,4 +83,8 @@ public interface IssuePagerMvp { void onSubscribeOrMute(boolean mute); } + interface IssuePrCallback { + @Nullable T getData(); + } + } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerPresenter.java index 13540a67..bcd1fd7c 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerPresenter.java @@ -70,31 +70,14 @@ class IssuePagerPresenter extends BasePresenter implements I showToRepoBtn = intent.getExtras().getBoolean(BundleConstant.EXTRA_THREE); if (issueModel != null) { issueNumber = issueModel.getNumber(); - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); return; } else if (issueNumber > 0 && !InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { getIssueFromApi(); return; } } - sendToView(IssuePagerMvp.View::onSetupIssue); - } - - private void getIssueFromApi() { - Observable observable = RestProvider.getIssueService().getIssue(login, repoId, issueNumber) - .flatMap(issue -> RestProvider.getRepoService().isCollaborator(login, repoId, Login.getUser().getLogin()), - (issue, booleanResponse) -> { - isCollaborator = booleanResponse.code() == 204; - return issue; - }); - makeRestCall(observable, this::setupIssue); - } - - private void setupIssue(Issue issue) { - issueModel = issue; - issueModel.setRepoId(repoId); - issueModel.setLogin(login); - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } @Override public void onWorkOffline(long issueNumber, @NonNull String repoId, @NonNull String login) { @@ -103,7 +86,7 @@ class IssuePagerPresenter extends BasePresenter implements I .subscribe(issueModel1 -> { if (issueModel1 != null) { issueModel = issueModel1; - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } })); } else { @@ -163,7 +146,7 @@ class IssuePagerPresenter extends BasePresenter implements I issue.setRepoId(issueModel.getRepoId()); issue.setLogin(issueModel.getLogin()); issueModel = issue; - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(true)); } }) .subscribe(issue -> {/**/}, @@ -184,7 +167,7 @@ class IssuePagerPresenter extends BasePresenter implements I int code = booleanResponse.code(); if (code == 204) { issueModel.setLocked(!isLocked()); - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(true)); } sendToView(IssuePagerMvp.View::hideProgress); }); @@ -217,7 +200,7 @@ class IssuePagerPresenter extends BasePresenter implements I issueModel.setLogin(login); issueModel.setRepoId(repoId); manageObservable(issue.save(issueModel).toObservable()); - sendToView(IssuePagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.labels_added_successfully)); }); } @@ -227,7 +210,7 @@ class IssuePagerPresenter extends BasePresenter implements I Stream.of(labels).filter(value -> value != null && value.getName() != null) .map(LabelModel::getName).collect(Collectors.toList())), labelModels -> { - sendToView(IssuePagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.labels_added_successfully)); LabelListModel listModel = new LabelListModel(); listModel.addAll(labels); issueModel.setLabels(listModel); @@ -249,7 +232,7 @@ class IssuePagerPresenter extends BasePresenter implements I assignee.addAll(users); issueModel.setAssignees(assignee); manageObservable(issueModel.save(issueModel).toObservable()); - sendToView(IssuePagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.assignee_added)); } ); } @@ -267,7 +250,7 @@ class IssuePagerPresenter extends BasePresenter implements I this.issueModel.setLogin(login); this.issueModel.setRepoId(repoId); manageObservable(issueModel.save(issueModel).toObservable()); - sendToView(IssuePagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(true)); } @Override public void onSubscribeOrMute(boolean mute) { @@ -288,4 +271,25 @@ class IssuePagerPresenter extends BasePresenter implements I } }); } + + private void getIssueFromApi() { + makeRestCall(RxHelper.getObserver(Observable.zip(RestProvider.getIssueService().getIssue(login, repoId, issueNumber), + RestProvider.getRepoService().isCollaborator(login, repoId, Login.getUser().getLogin()), + (issue, booleanResponse) -> { + isCollaborator = booleanResponse.code() == 204; + return issue; + })), this::setupIssue); + } + + private void setupIssue(Issue issue) { + issueModel = issue; + issueModel.setRepoId(repoId); + issueModel.setLogin(login); + sendToView(view -> view.onSetupIssue(false)); + } + + private void updateTimeline(IssuePagerMvp.View view, int assignee_added) { + view.showMessage(R.string.success, assignee_added); + view.onUpdateTimeline(); + } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java index 2a73a6d6..faa30889 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java @@ -1,6 +1,7 @@ package com.fastaccess.ui.modules.repos.issues.issue.details.timeline; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; @@ -8,6 +9,7 @@ import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.view.View; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.TimelineModel; import com.fastaccess.data.dao.model.Comment; @@ -24,6 +26,7 @@ import com.fastaccess.ui.adapter.IssuePullsTimelineAdapter; import com.fastaccess.ui.adapter.viewholder.TimelineCommentsViewHolder; import com.fastaccess.ui.base.BaseFragment; import com.fastaccess.ui.modules.editor.EditorActivity; +import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerMvp; import com.fastaccess.ui.modules.repos.reactions.ReactionsDialogFragment; import com.fastaccess.ui.widgets.AppbarRefreshLayout; import com.fastaccess.ui.widgets.StateLayout; @@ -36,7 +39,6 @@ import java.util.LinkedHashMap; import java.util.List; import butterknife.BindView; -import com.evernote.android.state.State; /** * Created by Kosh on 31 Mar 2017, 7:35 PM @@ -50,16 +52,32 @@ public class IssueTimelineFragment extends BaseFragment toggleMap = new LinkedHashMap<>(); private IssuePullsTimelineAdapter adapter; - private OnLoadMore onLoadMore; + private OnLoadMore onLoadMore; + private IssuePagerMvp.IssuePrCallback issueCallback; - public static IssueTimelineFragment newInstance(@NonNull Issue issueModel) { - IssueTimelineFragment view = new IssueTimelineFragment(); - view.setArguments(Bundler.start().put(BundleConstant.ITEM, issueModel).end());//TODO fix this - return view; + @NonNull public static IssueTimelineFragment newInstance() { + return new IssueTimelineFragment(); + } + + @SuppressWarnings("unchecked") @Override public void onAttach(Context context) { + super.onAttach(context); + if (getParentFragment() instanceof IssuePagerMvp.IssuePrCallback) { + issueCallback = (IssuePagerMvp.IssuePrCallback) getParentFragment(); + } else if (context instanceof IssuePagerMvp.IssuePrCallback) { + issueCallback = (IssuePagerMvp.IssuePrCallback) context; + } else { + throw new IllegalArgumentException(String.format("%s or parent fragment must implement IssuePagerMvp.IssuePrCallback", context.getClass() + .getSimpleName())); + } + } + + @Override public void onDetach() { + issueCallback = null; + super.onDetach(); } @Override public void onRefresh() { - getPresenter().onCallApi(1, null); + getPresenter().onCallApi(1, getIssue()); } @Override public void onNotifyAdapter(@Nullable List items, int page) { @@ -69,17 +87,16 @@ public class IssueTimelineFragment extends BaseFragment getLoadMore() { if (onLoadMore == null) { - onLoadMore = new OnLoadMore(getPresenter()); + onLoadMore = new OnLoadMore<>(getPresenter()); } + onLoadMore.setParameter(getIssue()); return onLoadMore; } @@ -88,12 +105,15 @@ public class IssueTimelineFragment extends BaseFragment items, int page); - @NonNull OnLoadMore getLoadMore(); + @NonNull OnLoadMore getLoadMore(); void onEditComment(@NonNull Comment item); @@ -50,32 +50,24 @@ public interface IssueTimelineMvp { void onSetHeader(@NonNull TimelineModel timelineModel); - void onRefresh(@NonNull Issue issue); + @Nullable Issue getIssue(); + + void onUpdateHeader(); } interface Presenter extends BaseMvp.FAPresenter, BaseViewHolder.OnItemClickListener, - BaseMvp.PaginationListener { + BaseMvp.PaginationListener { boolean isPreviouslyReacted(long commentId, int vId); @NonNull ArrayList getEvents(); - void onFragmentCreated(@Nullable Bundle bundle); - void onWorkOffline(); void onHandleDeletion(@Nullable Bundle bundle); - @Nullable String repoId(); - - @Nullable String login(); - - int number(); - void onHandleReaction(@IdRes int viewId, long id, @ReactionsProvider.ReactionType int reactionType); boolean isCallingApi(long id, int vId); - - void onUpdateIssue(@NonNull Issue issue); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java index f8c0e5da..513e73a7 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelinePresenter.java @@ -37,7 +37,6 @@ import lombok.Getter; */ @Getter public class IssueTimelinePresenter extends BasePresenter implements IssueTimelineMvp.Presenter { - @com.evernote.android.state.State Issue issue; private ArrayList timeline = new ArrayList<>(); private ReactionsProvider reactionsProvider; private int page; @@ -51,12 +50,13 @@ import lombok.Getter; @Override public void onItemClick(int position, View v, TimelineModel item) { if (getView() != null) { if (item.getType() == TimelineModel.COMMENT) { - if (getHeader() == null) return; + if (getView().getIssue() == null) return; + Issue issue = getView().getIssue(); if (v.getId() == R.id.commentMenu) { PopupMenu popupMenu = new PopupMenu(v.getContext(), v); popupMenu.inflate(R.menu.comments_menu); String username = Login.getUser().getLogin(); - boolean isOwner = CommentsHelper.isOwner(username, getHeader().getLogin(), item.getComment().getUser().getLogin()); + boolean isOwner = CommentsHelper.isOwner(username, issue.getLogin(), item.getComment().getUser().getLogin()); popupMenu.getMenu().findItem(R.id.delete).setVisible(isOwner); popupMenu.getMenu().findItem(R.id.edit).setVisible(isOwner); popupMenu.setOnMenuItemClickListener(item1 -> { @@ -113,8 +113,10 @@ import lombok.Getter; @Override public void onItemLongClick(int position, View v, TimelineModel item) { if (getView() == null) return; if (item.getType() == TimelineModel.COMMENT || item.getType() == TimelineModel.HEADER) { - String login = login(); - String repoId = repoId(); + if (getView().getIssue() == null) return; + Issue issue = getView().getIssue(); + String login = issue.getLogin(); + String repoId = issue.getRepoId(); if (!InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { ReactionTypes type = ReactionTypes.get(v.getId()); if (type != null) { @@ -136,23 +138,6 @@ import lombok.Getter; return timeline; } - @Override protected void onCreate() { - super.onCreate(); - if (issue != null && timeline.isEmpty()) { - sendToView(view -> view.onSetHeader(TimelineModel.constructHeader(issue))); - onCallApi(1, null); - } - } - - @Override public void onFragmentCreated(@Nullable Bundle bundle) { - if (bundle == null) throw new NullPointerException("Bundle is null?"); - issue = bundle.getParcelable(BundleConstant.ITEM); - if (timeline.isEmpty() && issue != null) { - sendToView(view -> view.onSetHeader(TimelineModel.constructHeader(issue))); - onCallApi(1, null); - } - } - @Override public void onWorkOffline() { //TODO } @@ -161,7 +146,9 @@ import lombok.Getter; if (bundle != null) { long commId = bundle.getLong(BundleConstant.EXTRA, 0); if (commId != 0) { - makeRestCall(RestProvider.getIssueService().deleteIssueComment(login(), repoId(), commId), + if (getView() == null || getView().getIssue() == null) return; + Issue issue = getView().getIssue(); + makeRestCall(RestProvider.getIssueService().deleteIssueComment(issue.getLogin(), issue.getRepoId(), commId), booleanResponse -> sendToView(view -> { if (booleanResponse.code() == 204) { Comment comment = new Comment(); @@ -175,25 +162,11 @@ import lombok.Getter; } } - @Nullable @Override public String repoId() { - return getHeader() != null ? getHeader().getRepoId() : null; - } - - @Nullable @Override public String login() { - return getHeader() != null ? getHeader().getLogin() : null; - } - - @Override public int number() { - return getHeader() != null ? getHeader().getNumber() : -1; - } - - @Nullable private Issue getHeader() { - return issue; - } - @Override public void onHandleReaction(int viewId, long id, @ReactionsProvider.ReactionType int reactionType) { - String login = login(); - String repoId = repoId(); + if (getView() == null || getView().getIssue() == null) return; + Issue issue = getView().getIssue(); + String login = issue.getLogin(); + String repoId = issue.getRepoId(); Observable observable = getReactionsProvider().onHandleReaction(viewId, id, login, repoId, reactionType); if (observable != null) manageObservable(observable); } @@ -202,10 +175,6 @@ import lombok.Getter; return getReactionsProvider().isCallingApi(id, vId); } - @Override public void onUpdateIssue(@NonNull Issue issue) { - this.issue = issue; - } - @NonNull private ReactionsProvider getReactionsProvider() { if (reactionsProvider == null) { reactionsProvider = new ReactionsProvider(); @@ -229,8 +198,8 @@ import lombok.Getter; this.previousTotal = previousTotal; } - @Override public void onCallApi(int page, @Nullable Object parameter) { - if (getHeader() == null) { + @Override public void onCallApi(int page, @Nullable Issue parameter) { + if (parameter == null) { sendToView(BaseMvp.FAView::hideProgress); return; } @@ -243,19 +212,19 @@ import lombok.Getter; return; } setCurrentPage(page); - String login = getHeader().getLogin(); - String repoID = getHeader().getRepoId(); - int number = getHeader().getNumber(); + String login = parameter.getLogin(); + String repoId = parameter.getRepoId(); + int number = parameter.getNumber(); Observable> observable; if (page > 1) { - observable = RestProvider.getIssueService().getIssueComments(login, repoID, number, page) + observable = RestProvider.getIssueService().getIssueComments(login, repoId, number, page) .map(comments -> { lastPage = comments != null ? comments.getLast() : 0; return TimelineModel.construct(comments != null ? comments.getItems() : null); }); } else { - observable = Observable.zip(RestProvider.getIssueService().getTimeline(login, repoID, number), - RestProvider.getIssueService().getIssueComments(login, repoID, number, page), + observable = Observable.zip(RestProvider.getIssueService().getTimeline(login, repoId, number), + RestProvider.getIssueService().getIssueComments(login, repoId, number, page), (issueEventPageable, commentPageable) -> { lastPage = commentPageable != null ? commentPageable.getLast() : 0; return TimelineModel.construct(commentPageable != null ? commentPageable.getItems() : null, diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerActivity.java index b49a550b..8d2cf11a 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerActivity.java @@ -13,6 +13,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.FragmentPagerAdapterModel; import com.fastaccess.data.dao.LabelModel; @@ -49,7 +50,6 @@ import java.util.List; import butterknife.BindView; import butterknife.OnClick; -import com.evernote.android.state.State; /** * Created by Kosh on 10 Dec 2016, 9:23 AM @@ -127,7 +127,7 @@ public class PullRequestPagerActivity extends BaseActivity labels) { Logger.e(labels, labels.size()); getPresenter().onPutLabels(labels); @@ -364,10 +336,9 @@ public class PullRequestPagerActivity extends BaseActivity { - void onSetupIssue(); + void onSetupIssue(boolean update); void onLabelsRetrieved(@NonNull List items); - void onUpdateMenu(); - void showSuccessIssueActionMsg(boolean isClose); void showErrorIssueActionMsg(boolean isClose); @@ -88,6 +88,8 @@ public interface PullRequestPagerMvp { boolean isCollaborator(); void onUpdatePullRequest(@NonNull PullRequest pullRequestModel); + + void onRefresh(); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerPresenter.java index 5fe17e59..696478de 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/PullRequestPagerPresenter.java @@ -67,33 +67,14 @@ class PullRequestPagerPresenter extends BasePresenter repoId = intent.getExtras().getString(BundleConstant.EXTRA_TWO); showToRepoBtn = intent.getExtras().getBoolean(BundleConstant.EXTRA_THREE); if (pullRequest != null) { - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); return; } else if (issueNumber > 0 && !InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { - makeRestCall(RestProvider.getPullRequestService() - .getPullRequest(login, repoId, issueNumber) - .flatMap(pullRequest1 -> RestProvider.getRepoService().isCollaborator(login, repoId, Login.getUser().getLogin()), - (pullRequest1, booleanResponse) -> { - isCollaborator = booleanResponse.code() == 204; - return pullRequest1; - }) - .flatMap(pullRequest1 -> RestProvider.getIssueService().getIssue(login, repoId, issueNumber), - (pullRequest1, issue) -> {//hack to get reactions from issue api - if (issue != null) { - pullRequest1.setReactions(issue.getReactions()); - } - return pullRequest1; - }), pullRequestModelResponse -> { - pullRequest = pullRequestModelResponse; - pullRequest.setRepoId(repoId); - pullRequest.setLogin(login); - sendToView(PullRequestPagerMvp.View::onSetupIssue); - manageObservable(pullRequest.save(pullRequest).toObservable()); - }); + callApi(); return; } } - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } @Override public void onWorkOffline() { @@ -102,7 +83,7 @@ class PullRequestPagerPresenter extends BasePresenter .subscribe(pullRequestModel -> { if (pullRequestModel != null) { pullRequest = pullRequestModel; - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } })); } @@ -158,7 +139,7 @@ class PullRequestPagerPresenter extends BasePresenter int code = booleanResponse.code(); if (code == 204) { pullRequest.setLocked(!isLocked()); - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } }); } @@ -175,7 +156,7 @@ class PullRequestPagerPresenter extends BasePresenter issue.setRepoId(getPullRequest().getRepoId()); issue.setLogin(getPullRequest().getLogin()); pullRequest = issue; - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(false)); } }, throwable -> sendToView(view -> view.showErrorIssueActionMsg(getPullRequest().getState() == IssueState.open)))); } @@ -195,9 +176,7 @@ class PullRequestPagerPresenter extends BasePresenter } else { sendToView(view -> view.showMessage(R.string.error, R.string.no_labels)); } - }, throwable -> { - sendToView(view -> view.showMessage(R.string.error, R.string.no_labels)); - }) + }, throwable -> sendToView(view -> view.showMessage(R.string.error, R.string.no_labels))) ); } @@ -206,7 +185,7 @@ class PullRequestPagerPresenter extends BasePresenter Stream.of(labels).filter(value -> value != null && value.getName() != null) .map(LabelModel::getName).collect(Collectors.toList())), labelModels -> { - sendToView(PullRequestPagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.labels_added_successfully)); LabelListModel listModel = new LabelListModel(); listModel.addAll(labels); pullRequest.setLabels(listModel); @@ -223,7 +202,7 @@ class PullRequestPagerPresenter extends BasePresenter pullRequest.setLogin(login); pullRequest.setRepoId(repoId); manageObservable(pr.save(pullRequest).toObservable()); - sendToView(PullRequestPagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.labels_added_successfully)); }); } @@ -244,13 +223,13 @@ class PullRequestPagerPresenter extends BasePresenter assignee.addAll(users); pullRequest.setAssignees(assignee); manageObservable(pullRequest.save(pullRequest).toObservable()); - sendToView(PullRequestPagerMvp.View::onUpdateTimeline); + sendToView(view -> updateTimeline(view, R.string.assignee_added)); } ); } else { assigneesRequestModel.setReviewers(assignees); makeRestCall(RestProvider.getPullRequestService().putReviewers(login, repoId, issueNumber, assigneesRequestModel), - pullRequestResponse -> sendToView(PullRequestPagerMvp.View::onUpdateTimeline) + pullRequestResponse -> sendToView(view -> updateTimeline(view, R.string.reviewer_added)) ); } } @@ -266,10 +245,7 @@ class PullRequestPagerPresenter extends BasePresenter .doOnSubscribe(disposable -> sendToView(view -> view.showProgress(0))) .subscribe(mergeResponseModel -> { if (mergeResponseModel.isMerged()) { - sendToView(view -> { - view.showMessage(R.string.success, R.string.success_merge); - view.onUpdateTimeline(); - }); + sendToView(view -> updateTimeline(view, R.string.success_merge)); } else { sendToView(view -> view.showErrorMessage(mergeResponseModel.getMessage())); } @@ -291,10 +267,44 @@ class PullRequestPagerPresenter extends BasePresenter } @Override public void onUpdatePullRequest(@NonNull PullRequest pullRequestModel) { - this.pullRequest = pullRequestModel; + this.pullRequest.setTitle(pullRequestModel.getTitle()); + this.pullRequest.setBody(pullRequestModel.getBody()); + this.pullRequest.setBodyHtml(pullRequestModel.getBodyHtml()); this.pullRequest.setLogin(login); this.pullRequest.setRepoId(repoId); manageObservable(pullRequest.save(pullRequest).toObservable()); - sendToView(PullRequestPagerMvp.View::onSetupIssue); + sendToView(view -> view.onSetupIssue(true)); + } + + @Override public void onRefresh() { + callApi(); + } + + private void callApi() { + makeRestCall(RxHelper.getObserver(Observable.zip(RestProvider.getPullRequestService() + .getPullRequest(login, repoId, issueNumber), + RestProvider.getRepoService().isCollaborator(login, repoId, Login.getUser().getLogin()), + RestProvider.getIssueService().getIssue(login, repoId, issueNumber), + (pullRequestModel, booleanResponse, issue) -> { + this.pullRequest = pullRequestModel; + if (issue != null) { + this.pullRequest.setReactions(issue.getReactions()); + this.pullRequest.setTitle(issue.getTitle()); + this.pullRequest.setBody(issue.getBody()); + this.pullRequest.setBodyHtml(issue.getBodyHtml()); + } + this.pullRequest.setLogin(login); + this.pullRequest.setRepoId(repoId); + isCollaborator = booleanResponse.code() == 204; + return pullRequest; + })), pullRequest -> { + sendToView(view -> view.onSetupIssue(false)); + manageObservable(pullRequest.save(pullRequest).toObservable()); + }); + } + + private void updateTimeline(PullRequestPagerMvp.View view, int assignee_added) { + view.showMessage(R.string.success, assignee_added); + view.onUpdateTimeline(); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java index cd27b7af..ce80b9ec 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelineFragment.java @@ -1,6 +1,7 @@ package com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.timeline.timeline; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; @@ -8,6 +9,7 @@ import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.view.View; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.EditReviewCommentModel; import com.fastaccess.data.dao.ReviewCommentModel; @@ -25,6 +27,7 @@ import com.fastaccess.ui.adapter.IssuePullsTimelineAdapter; import com.fastaccess.ui.adapter.viewholder.TimelineCommentsViewHolder; import com.fastaccess.ui.base.BaseFragment; import com.fastaccess.ui.modules.editor.EditorActivity; +import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerMvp; import com.fastaccess.ui.modules.repos.reactions.ReactionsDialogFragment; import com.fastaccess.ui.widgets.AppbarRefreshLayout; import com.fastaccess.ui.widgets.StateLayout; @@ -37,7 +40,6 @@ import java.util.LinkedHashMap; import java.util.List; import butterknife.BindView; -import com.evernote.android.state.State; /** * Created by Kosh on 31 Mar 2017, 7:35 PM @@ -52,16 +54,33 @@ public class PullRequestTimelineFragment extends BaseFragment toggleMap = new LinkedHashMap<>(); private IssuePullsTimelineAdapter adapter; - private OnLoadMore onLoadMore; + private OnLoadMore onLoadMore; - public static PullRequestTimelineFragment newInstance(@NonNull PullRequest pullRequest) { - PullRequestTimelineFragment view = new PullRequestTimelineFragment(); - view.setArguments(Bundler.start().put(BundleConstant.ITEM, pullRequest).end());//TODO fix this - return view; + private IssuePagerMvp.IssuePrCallback issueCallback; + + @NonNull public static PullRequestTimelineFragment newInstance() { + return new PullRequestTimelineFragment(); + } + + @SuppressWarnings("unchecked") @Override public void onAttach(Context context) { + super.onAttach(context); + if (getParentFragment() instanceof IssuePagerMvp.IssuePrCallback) { + issueCallback = (IssuePagerMvp.IssuePrCallback) getParentFragment(); + } else if (context instanceof IssuePagerMvp.IssuePrCallback) { + issueCallback = (IssuePagerMvp.IssuePrCallback) context; + } else { + throw new IllegalArgumentException(String.format("%s or parent fragment must implement IssuePagerMvp.IssuePrCallback", context.getClass() + .getSimpleName())); + } + } + + @Override public void onDetach() { + issueCallback = null; + super.onDetach(); } @Override public void onRefresh() { - getPresenter().onCallApi(1, null); + getPresenter().onCallApi(1, getPullRequest()); } @Override protected int fragmentLayout() { @@ -69,24 +88,27 @@ public class PullRequestTimelineFragment extends BaseFragment getLoadMore() { if (onLoadMore == null) { - onLoadMore = new OnLoadMore(getPresenter()); + onLoadMore = new OnLoadMore<>(getPresenter()); } + onLoadMore.setParameter(getPullRequest()); return onLoadMore; } @Override public void onEditComment(@NonNull Comment item) { Intent intent = new Intent(getContext(), EditorActivity.class); + if (getPullRequest() == null) return; intent.putExtras(Bundler .start() - .put(BundleConstant.ID, getPresenter().repoId()) - .put(BundleConstant.EXTRA_TWO, getPresenter().login()) - .put(BundleConstant.EXTRA_THREE, getPresenter().number()) + .put(BundleConstant.ID, getPullRequest().getRepoId()) + .put(BundleConstant.EXTRA_TWO, getPullRequest().getLogin()) + .put(BundleConstant.EXTRA_THREE, getPullRequest().getNumber()) .put(BundleConstant.EXTRA_FOUR, item.getId()) .put(BundleConstant.EXTRA, item.getBody()) .put(BundleConstant.EXTRA_TYPE, BundleConstant.ExtraTYpe.EDIT_ISSUE_COMMENT_EXTRA) @@ -170,11 +192,12 @@ public class PullRequestTimelineFragment extends BaseFragment items, int page); - @NonNull OnLoadMore getLoadMore(); + @NonNull OnLoadMore getLoadMore(); void onEditComment(@NonNull Comment item); @@ -66,34 +66,26 @@ public interface PullRequestTimelineMvp { void onSetHeader(@NonNull TimelineModel timelineModel); - void onRefresh(@NonNull PullRequest pullRequest); + @Nullable PullRequest getPullRequest(); + + void onUpdateHeader(); } interface Presenter extends BaseMvp.FAPresenter, BaseViewHolder.OnItemClickListener, - ReviewCommentCallback, BaseMvp.PaginationListener { + ReviewCommentCallback, BaseMvp.PaginationListener { @NonNull ArrayList getEvents(); - void onFragmentCreated(@Nullable Bundle bundle); - void onWorkOffline(); void onHandleDeletion(@Nullable Bundle bundle); - @Nullable String repoId(); - - @Nullable String login(); - - int number(); - boolean isPreviouslyReacted(long commentId, int vId); void onHandleReaction(@IdRes int vId, long idOrNumber, @ReactionsProvider.ReactionType int reactionType); - boolean isMerged(); + boolean isMerged(PullRequest pullRequest); boolean isCallingApi(long id, int vId); - - void onUpdatePullRequest(@NonNull PullRequest pullRequest); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java index 1a03c564..ca89fca3 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/pull_requests/pull_request/details/timeline/timeline/PullRequestTimelinePresenter.java @@ -41,20 +41,19 @@ import io.reactivex.Observable; public class PullRequestTimelinePresenter extends BasePresenter implements PullRequestTimelineMvp.Presenter { private ArrayList timeline = new ArrayList<>(); private ReactionsProvider reactionsProvider; - @com.evernote.android.state.State PullRequest pullRequest; private int page; private int previousTotal; private int lastPage = Integer.MAX_VALUE; @Override public void onItemClick(int position, View v, TimelineModel item) { - if (getView() != null) { + if (getView() != null && getView().getPullRequest() != null) { if (item.getType() == TimelineModel.COMMENT) { - if (getHeader() == null) return; + PullRequest pullRequest = getView().getPullRequest(); if (v.getId() == R.id.commentMenu) { PopupMenu popupMenu = new PopupMenu(v.getContext(), v); popupMenu.inflate(R.menu.comments_menu); String username = Login.getUser().getLogin(); - boolean isOwner = CommentsHelper.isOwner(username, getHeader().getLogin(), item.getComment().getUser().getLogin()); + boolean isOwner = CommentsHelper.isOwner(username, pullRequest.getLogin(), item.getComment().getUser().getLogin()); popupMenu.getMenu().findItem(R.id.delete).setVisible(isOwner); popupMenu.getMenu().findItem(R.id.edit).setVisible(isOwner); popupMenu.setOnMenuItemClickListener(item1 -> { @@ -119,10 +118,11 @@ public class PullRequestTimelinePresenter extends BasePresenter view.onSetHeader(TimelineModel.constructHeader(pullRequest))); - onCallApi(1, null); - } - } - - @Override public void onFragmentCreated(@Nullable Bundle bundle) { - if (bundle == null) throw new NullPointerException("Bundle is null?"); - pullRequest = bundle.getParcelable(BundleConstant.ITEM); - if (timeline.isEmpty() && pullRequest != null) { - sendToView(view -> view.onSetHeader(TimelineModel.constructHeader(pullRequest))); - onCallApi(1, null); - } - } - @Override public void onWorkOffline() { //TODO } - @Nullable private PullRequest getHeader() { - return pullRequest; - } - @Override public void onHandleDeletion(@Nullable Bundle bundle) { + if (getView() == null || getView().getPullRequest() == null) return; if (bundle != null) { + PullRequest pullRequest = getView().getPullRequest(); + String login = pullRequest.getLogin(); + String repoId = pullRequest.getRepoId(); long commId = bundle.getLong(BundleConstant.EXTRA, 0); boolean isReviewComment = bundle.getBoolean(BundleConstant.YES_NO_EXTRA); if (commId != 0 && !isReviewComment) { - makeRestCall(RestProvider.getIssueService().deleteIssueComment(login(), repoId(), commId), + makeRestCall(RestProvider.getIssueService().deleteIssueComment(login, repoId, commId), booleanResponse -> sendToView(view -> { if (booleanResponse.code() == 204) { Comment comment = new Comment(); @@ -187,7 +170,7 @@ public class PullRequestTimelinePresenter extends BasePresenter sendToView(view -> { if (booleanResponse.code() == 204) { view.onRemoveReviewComment(groupPosition, commentPosition); @@ -199,38 +182,24 @@ public class PullRequestTimelinePresenter extends BasePresenter { @@ -275,9 +244,10 @@ public class PullRequestTimelinePresenter extends BasePresenter view.getLoadMore().reset()); @@ -321,7 +291,7 @@ public class PullRequestTimelinePresenter extends BasePresenter(), Tr @State var selectedTitle: String = "" fun onDailyClicked() { - Toasty.info(applicationContext, "Hello").show() Logger.e() daily.isSelected = true weekly.isSelected = false @@ -40,7 +38,6 @@ class TrendingActivity : BaseActivity(), Tr } fun onWeeklyClicked() { - Toasty.info(applicationContext, "Hello").show() weekly.isSelected = true daily.isSelected = false monthly.isSelected = false @@ -48,7 +45,6 @@ class TrendingActivity : BaseActivity(), Tr } fun onMonthlyClicked() { - Toasty.info(applicationContext, "Hello").show() monthly.isSelected = true weekly.isSelected = false daily.isSelected = false @@ -137,4 +133,4 @@ class TrendingActivity : BaseActivity(), Tr else -> return "daily" } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt index c4d46a99..50759383 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt @@ -10,9 +10,7 @@ import io.reactivex.Observable class TrendingPresenter : BasePresenter(), TrendingMvp.Presenter { override fun onLoadLanguage() { - manageObservable(Observable.create { - ColorsProvider.languages().forEach({ t: String? -> it.onNext(t) }) - it.onComplete() - }.doOnNext({ t: String -> sendToView({ it.onAppend(t) }) })) + manageObservable(Observable.fromIterable(ColorsProvider.languages()) + .doOnNext({ t: String -> sendToView({ it.onAppend(t) }) })) } } diff --git a/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java b/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java index 0d654c91..5011658e 100644 --- a/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java +++ b/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java @@ -20,7 +20,6 @@ import android.text.style.TypefaceSpan; import com.fastaccess.App; import com.fastaccess.R; import com.fastaccess.helper.InputHelper; -import com.fastaccess.helper.Logger; public class DiffLineSpan extends MetricAffectingSpan implements LineBackgroundSpan { private Rect rect = new Rect(); @@ -68,8 +67,8 @@ public class DiffLineSpan extends MetricAffectingSpan implements LineBackgroundS String[] split = text.split("\\r?\\n|\\r"); if (split.length > 0) { int lines = split.length; + int index = -1; for (int i = 0; i < lines; i++) { - Logger.e(lines, i, lines + i, lines - i); if (truncate && (lines - i) > 3) continue; String token = split[i]; if (i < (lines - 1)) { @@ -85,7 +84,7 @@ public class DiffLineSpan extends MetricAffectingSpan implements LineBackgroundS color = patchRefColor; } else if (firstChar == '\\') { token = token.replace("\\ No newline at end of file", ""); - noNewlineRemoved = true; + index = i; } SpannableString spannableDiff = new SpannableString(token); if (color != Color.TRANSPARENT) { @@ -94,12 +93,12 @@ public class DiffLineSpan extends MetricAffectingSpan implements LineBackgroundS } builder.append(spannableDiff); } + if (index != -1) { + builder.insert(index, SpannableBuilder.builder() + .append(ContextCompat.getDrawable(App.getInstance(), R.drawable.ic_newline))); + } } } - if (noNewlineRemoved) { - builder.insert(builder.length() - 1, SpannableBuilder.builder() - .append(ContextCompat.getDrawable(App.getInstance(), R.drawable.ic_newline))); - } builder.setSpan(new TypefaceSpan("monospace"), 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return builder; } diff --git a/app/src/main/res/layouts/row_layouts/layout/comments_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/comments_row_item.xml index 932e4888..69b187a3 100644 --- a/app/src/main/res/layouts/row_layouts/layout/comments_row_item.xml +++ b/app/src/main/res/layouts/row_layouts/layout/comments_row_item.xml @@ -82,10 +82,10 @@ android:id="@+id/comment" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/spacing_normal" + android:layout_marginBottom="@dimen/spacing_micro" android:layout_marginEnd="@dimen/spacing_xs_large" - android:layout_marginStart="@dimen/avatar_margin" - android:layout_marginTop="@dimen/spacing_normal" + android:layout_marginStart="@dimen/spacing_xs_large" + android:layout_marginTop="@dimen/spacing_micro" android:textIsSelectable="true"/> diff --git a/app/src/main/res/layouts/row_layouts/layout/review_comments_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/review_comments_row_item.xml index f46318e2..1bb0da7a 100644 --- a/app/src/main/res/layouts/row_layouts/layout/review_comments_row_item.xml +++ b/app/src/main/res/layouts/row_layouts/layout/review_comments_row_item.xml @@ -80,10 +80,10 @@ android:id="@+id/comment" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="@dimen/spacing_normal" + android:layout_marginBottom="@dimen/spacing_micro" android:layout_marginEnd="@dimen/spacing_xs_large" - android:layout_marginStart="@dimen/avatar_margin" - android:layout_marginTop="@dimen/spacing_normal" + android:layout_marginStart="@dimen/spacing_xs_large" + android:layout_marginTop="@dimen/spacing_micro" android:textIsSelectable="true" tools:text="Hello World"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e49e7275..631892e6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -475,4 +475,7 @@ Type Sort Sort direction + Assignee added successfully + Reviewer added successfully + Milestone added successfully diff --git a/debug_gradle.properties b/debug_gradle.properties index 95f8cf7a..d0ba5760 100644 --- a/debug_gradle.properties +++ b/debug_gradle.properties @@ -5,4 +5,5 @@ android_key_alias=ALIAS github_client_id=GITHUB_CLIENT_ID github_secret=GITHUB_SECRET imgur_client_id=imgur_client_id -imgur_secret=imgur_secret \ No newline at end of file +imgur_secret=imgur_secret +redirect_url=fasthub://login \ No newline at end of file From 921ef05da2101ea97895a53cac7c781befae867f Mon Sep 17 00:00:00 2001 From: Kosh Date: Sat, 3 Jun 2017 14:22:52 +0800 Subject: [PATCH 02/14] this commit handles trending deeplinking --- .../provider/colors/ColorsProvider.java | 2 +- .../AuthenticationInterceptor.java | 16 +++++- .../rest/jsoup/RetroJsoupProvider.java | 5 +- .../provider/scheme/LinkParserHelper.java | 2 +- .../provider/scheme/SchemeParser.java | 23 +++++++- .../com/fastaccess/ui/base/BaseActivity.java | 7 +++ .../ui/modules/trending/TrendingActivity.kt | 56 ++++++++++++++++--- .../ui/modules/trending/TrendingPresenter.kt | 3 +- .../trending/fragment/TrendingFragment.kt | 4 +- .../layout/trending_activity_layout.xml | 6 ++ 10 files changed, 105 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java b/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java index d8127261..bfddffcb 100644 --- a/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java +++ b/app/src/main/java/com/fastaccess/provider/colors/ColorsProvider.java @@ -34,7 +34,7 @@ import io.reactivex.Observable; public class ColorsProvider { - private static List popularLanguages = Stream.of("Java", "Kotlin", "Javascript", "Python", "CSS", "PHP", + private static List popularLanguages = Stream.of("Java", "Kotlin", "JavaScript", "Python", "CSS", "PHP", "Ruby", "C++", "C", "GO", "Swift").toList();//predefined languages. private static Map colors = new LinkedHashMap<>(); diff --git a/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java b/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java index 70c15ffe..214f302c 100644 --- a/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java +++ b/app/src/main/java/com/fastaccess/provider/rest/interceptors/AuthenticationInterceptor.java @@ -8,17 +8,27 @@ import com.fastaccess.helper.InputHelper; import java.io.IOException; import java.net.URI; -import lombok.AllArgsConstructor; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; -@AllArgsConstructor public class AuthenticationInterceptor implements Interceptor { private String authToken; private String otp; + private boolean isScrapping; + + public AuthenticationInterceptor(String authToken, String otp) { + this.authToken = authToken; + this.otp = otp; + } + + public AuthenticationInterceptor(String authToken, String otp, boolean isScrapping) { + this.authToken = authToken; + this.otp = otp; + this.isScrapping = isScrapping; + } @Override public Response intercept(@NonNull Chain chain) throws IOException { Request original = chain.request(); @@ -30,7 +40,7 @@ public class AuthenticationInterceptor implements Interceptor { if (!InputHelper.isEmpty(otp)) { builder.addHeader("X-GitHub-OTP", otp.trim()); } - builder.addHeader("User-Agent", "FastHub"); + if (!isScrapping) builder.addHeader("User-Agent", "FastHub"); Request request = builder.build(); return chain.proceed(request); } diff --git a/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java b/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java index db899bb7..73e36901 100644 --- a/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java +++ b/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java @@ -28,7 +28,7 @@ public class RetroJsoupProvider { client.addInterceptor(new HttpLoggingInterceptor() .setLevel(HttpLoggingInterceptor.Level.BODY)); } - client.addInterceptor(new AuthenticationInterceptor(PrefGetter.getToken(), PrefGetter.getOtpCode())); + client.addInterceptor(new AuthenticationInterceptor(PrefGetter.getToken(), PrefGetter.getOtpCode(), true)); okHttpClient = client.build(); } return okHttpClient; @@ -36,7 +36,8 @@ public class RetroJsoupProvider { public static TrendingService getTrendingService(@NonNull String since, @Nullable String lang) { return new RetroJsoup.Builder() - .url("https://github.com/trending/" + (!InputHelper.isEmpty(lang) ? lang : "") + "?since=" + since) + .url("https://github.com/trending/" + (!InputHelper.isEmpty(lang) ? lang.replaceAll(" ", "-") : "").toLowerCase() + + "?since=" + since) .client(provideOkHttpClient()) .build() .create(TrendingService.class); diff --git a/app/src/main/java/com/fastaccess/provider/scheme/LinkParserHelper.java b/app/src/main/java/com/fastaccess/provider/scheme/LinkParserHelper.java index 37f7c8b3..d71193ac 100644 --- a/app/src/main/java/com/fastaccess/provider/scheme/LinkParserHelper.java +++ b/app/src/main/java/com/fastaccess/provider/scheme/LinkParserHelper.java @@ -23,7 +23,7 @@ class LinkParserHelper { static final String API_AUTHORITY = "api.github.com"; static final String PROTOCOL_HTTPS = "https"; static final ArrayList IGNORED_LIST = Stream.of("notifications", "settings", "blog", "explore", - "dashboard", "repositories", "logout", "sessions", "site", "security", "contact", "about", "logos", "login", "trending", "") + "dashboard", "repositories", "logout", "sessions", "site", "security", "contact", "about", "logos", "login", "") .collect(Collectors.toCollection(ArrayList::new)); diff --git a/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java b/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java index 0254213e..4cd47ee5 100644 --- a/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java +++ b/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java @@ -23,6 +23,7 @@ import com.fastaccess.ui.modules.repos.code.releases.ReleasesListActivity; import com.fastaccess.ui.modules.repos.issues.create.CreateIssueActivity; import com.fastaccess.ui.modules.repos.issues.issue.details.IssuePagerActivity; import com.fastaccess.ui.modules.repos.pull_requests.pull_request.details.PullRequestPagerActivity; +import com.fastaccess.ui.modules.trending.TrendingActivity; import com.fastaccess.ui.modules.user.UserPagerActivity; import java.util.List; @@ -108,6 +109,7 @@ public class SchemeParser { String authority = data.getAuthority(); if (TextUtils.equals(authority, HOST_DEFAULT) || TextUtils.equals(authority, RAW_AUTHORITY) || TextUtils.equals(authority, API_AUTHORITY)) { + Intent trending = getTrending(context, data); Intent userIntent = getUser(context, data); Intent repoIssues = getRepoIssueIntent(context, data); Intent repoPulls = getRepoPullRequestIntent(context, data); @@ -119,7 +121,7 @@ public class SchemeParser { Intent commit = getCommit(context, data, showRepoBtn); Intent commits = getCommits(context, data, showRepoBtn); Intent blob = getBlob(context, data); - Optional intentOptional = returnNonNull(userIntent, repoIssues, repoPulls, pullRequestIntent, commit, commits, + Optional intentOptional = returnNonNull(trending, userIntent, repoIssues, repoPulls, pullRequestIntent, commit, commits, createIssueIntent, issueIntent, releasesIntent, repoIntent, blob); Optional empty = Optional.empty(); if (intentOptional != null && intentOptional.isPresent() && intentOptional != empty) { @@ -324,6 +326,25 @@ public class SchemeParser { return null; } + @Nullable private static Intent getTrending(@NonNull Context context, @NonNull Uri uri) { + List segments = uri.getPathSegments(); + if (segments != null && !segments.isEmpty()) { + if (uri.getPathSegments().get(0).equals("trending")) { + String query = ""; + String lang = ""; + if (uri.getPathSegments().size() > 1) { + lang = uri.getPathSegments().get(1); + } + if (uri.getQueryParameterNames() != null && !uri.getQueryParameterNames().isEmpty()) { + query = uri.getQueryParameter("since"); + } + return TrendingActivity.TrendingIntent.getTrendingIntent(context, lang, query); + } + return null; + } + return null; + } + /** * https://github.com/owner/repo/issues/new */ diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java index 28b87b52..5e5e8b19 100644 --- a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java +++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java @@ -340,6 +340,13 @@ public abstract class BaseActivity(), Tr @State var selectedTitle: String = "" + companion object TrendingIntent { + fun getTrendingIntent(context: Context, lang: String?, query: String?): Intent { + val intent = Intent(context, TrendingActivity::class.java) + intent.putExtras(Bundler.start() + .put(BundleConstant.EXTRA, lang) + .put(BundleConstant.EXTRA_TWO, query) + .end()) + return intent + } + } + fun onDailyClicked() { Logger.e() daily.isSelected = true @@ -73,17 +88,14 @@ class TrendingActivity : BaseActivity(), Tr override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Logger.e(selectedTitle) trendingFragment = supportFragmentManager.findFragmentById(R.id.trendingFragment) as TrendingFragment? daily.setOnClickListener { onDailyClicked() } weekly.setOnClickListener { onWeeklyClicked() } monthly.setOnClickListener { onMonthlyClicked() } - presenter.onLoadLanguage() navMenu.setNavigationItemSelectedListener(this) - if (savedInstanceState == null) { - daily.isSelected = true - setValues() - } + setupIntent(savedInstanceState) + presenter.onLoadLanguage() + onSelectTrending() } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -102,7 +114,7 @@ class TrendingActivity : BaseActivity(), Tr override fun onAppend(title: String) { navMenu.menu.add(R.id.languageGroup, title.hashCode(), Menu.NONE, title) .setCheckable(true) - .isChecked = title == selectedTitle + .isChecked = title.toLowerCase() == selectedTitle.toLowerCase() } private fun onItemClicked(item: MenuItem?): Boolean { @@ -122,6 +134,7 @@ class TrendingActivity : BaseActivity(), Tr private fun setValues() { closeDrawerLayout() + Logger.e(selectedTitle, getSince()) trendingFragment?.onSetQuery(selectedTitle, getSince()) } @@ -133,4 +146,33 @@ class TrendingActivity : BaseActivity(), Tr else -> return "daily" } } + + private fun setupIntent(savedInstanceState: Bundle?) { + if (savedInstanceState == null) { + if (intent != null && intent.extras != null) { + val bundle = intent.extras + if (bundle != null) { + val lang: String = bundle.getString(BundleConstant.EXTRA) + val query: String = bundle.getString(BundleConstant.EXTRA_TWO) + if (!lang.isNullOrEmpty()) { + selectedTitle = lang + } + if (!query.isNullOrEmpty()) { + when (query.toLowerCase()) { + "daily" -> daily.isSelected = true + "weekly" -> weekly.isSelected = true + "monthly" -> monthly.isSelected = true + } + } else { + daily.isSelected = true + } + } else { + daily.isSelected = true + } + } else { + daily.isSelected = true + } + setValues() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt index 50759383..a400d57a 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingPresenter.kt @@ -1,5 +1,6 @@ package com.fastaccess.ui.modules.trending +import com.fastaccess.helper.RxHelper import com.fastaccess.provider.colors.ColorsProvider import com.fastaccess.ui.base.mvp.presenter.BasePresenter import io.reactivex.Observable @@ -10,7 +11,7 @@ import io.reactivex.Observable class TrendingPresenter : BasePresenter(), TrendingMvp.Presenter { override fun onLoadLanguage() { - manageObservable(Observable.fromIterable(ColorsProvider.languages()) + manageObservable(RxHelper.getObserver(Observable.fromIterable(ColorsProvider.languages())) .doOnNext({ t: String -> sendToView({ it.onAppend(t) }) })) } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt index 8385053c..f30709f3 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt @@ -62,9 +62,7 @@ class TrendingFragment : BaseFragment @@ -69,6 +70,7 @@ android:clickable="true" android:focusable="true" android:gravity="center" + android:maxLines="1" android:padding="@dimen/spacing_normal" android:text="@string/weekly" android:textColor="@color/search_tab_highlighter"/> @@ -83,6 +85,7 @@ android:clickable="true" android:focusable="true" android:gravity="center" + android:maxLines="1" android:padding="@dimen/spacing_normal" android:text="@string/monthly" android:textColor="@color/search_tab_highlighter"/> @@ -105,4 +108,7 @@ + + + \ No newline at end of file From 753ca85daccabccc5029229656955deb29e2db62 Mon Sep 17 00:00:00 2001 From: Kosh Date: Sat, 3 Jun 2017 17:35:17 +0800 Subject: [PATCH 03/14] this commit fixes #441 --- app/build.gradle | 4 + .../data/dao/FragmentPagerAdapterModel.java | 8 +- .../com/fastaccess/data/dao/Pageable.java | 12 +- .../com/fastaccess/data/dao/PayloadModel.java | 22 +-- .../fastaccess/data/dao/SimpleUrlsModel.java | 5 +- .../data/service/UserRestService.java | 16 ++- .../provider/scheme/SchemeParser.java | 2 +- .../modules/profile/events/ProfileEvents.kt | 32 +++++ .../profile/events/ProfileEventsFragment.kt | 132 ++++++++++++++++++ .../profile/events/ProfileEventsPresenter.kt | 127 +++++++++++++++++ .../issue/details/IssuePagerActivity.java | 4 +- .../issue/details/IssuePagerPresenter.java | 4 +- .../ui/modules/trending/TrendingActivity.kt | 2 +- .../ui/modules/user/UserPagerActivity.java | 48 ++++--- build.gradle | 2 +- 15 files changed, 365 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEvents.kt create mode 100644 app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsFragment.kt create mode 100644 app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsPresenter.kt diff --git a/app/build.gradle b/app/build.gradle index bbc68083..07473fe7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,6 +92,10 @@ android { compileOptions.incremental = false } +kapt { + generateStubs = true +} + repositories { maven { url "https://clojars.org/repo/" } maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } diff --git a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java index 4412f896..27392c78 100644 --- a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java @@ -20,6 +20,7 @@ import com.fastaccess.ui.modules.main.issues.MyIssuesFragment; import com.fastaccess.ui.modules.main.pullrequests.MyPullRequestFragment; import com.fastaccess.ui.modules.notification.all.AllNotificationsFragment; import com.fastaccess.ui.modules.notification.unread.UnreadNotificationsFragment; +import com.fastaccess.ui.modules.profile.events.ProfileEventsFragment; import com.fastaccess.ui.modules.profile.followers.ProfileFollowersFragment; import com.fastaccess.ui.modules.profile.following.ProfileFollowingFragment; import com.fastaccess.ui.modules.profile.gists.ProfileGistsFragment; @@ -73,6 +74,7 @@ import lombok.Setter; @NonNull public static List buildForProfile(@NonNull Context context, @NonNull String login) { return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.overview), ProfileOverviewFragment.newInstance(login)), + new FragmentPagerAdapterModel(context.getString(R.string.feeds), ProfileEventsFragment.Companion.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.repos), ProfileReposFragment.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.starred), ProfileStarredFragment.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.gists), ProfileGistsFragment.newInstance(login)), @@ -85,7 +87,8 @@ import lombok.Setter; @NonNull String login, @NonNull String url, @NonNull String defaultBranch) { return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.readme), ViewerFragment.newInstance(url, true)), - new FragmentPagerAdapterModel(context.getString(R.string.files), RepoFilePathFragment.newInstance(login, repoId, null, defaultBranch)), + new FragmentPagerAdapterModel(context.getString(R.string.files), RepoFilePathFragment.newInstance(login, repoId, null, + defaultBranch)), new FragmentPagerAdapterModel(context.getString(R.string.commits), RepoCommitsFragment.newInstance(repoId, login, defaultBranch)), new FragmentPagerAdapterModel(context.getString(R.string.releases), RepoReleasesFragment.newInstance(repoId, login)), new FragmentPagerAdapterModel(context.getString(R.string.contributors), RepoContributorsFragment.newInstance(repoId, login))) @@ -160,7 +163,8 @@ import lombok.Setter; public static List buildForGists(@NonNull Context context) { - return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.my_gists), ProfileGistsFragment.newInstance(Login.getUser().getLogin())), + return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.my_gists), ProfileGistsFragment.newInstance(Login.getUser() + .getLogin())), new FragmentPagerAdapterModel(context.getString(R.string.public_gists), GistsFragment.newInstance())) .collect(Collectors.toList()); } diff --git a/app/src/main/java/com/fastaccess/data/dao/Pageable.java b/app/src/main/java/com/fastaccess/data/dao/Pageable.java index e7c9233c..cbfafe6c 100644 --- a/app/src/main/java/com/fastaccess/data/dao/Pageable.java +++ b/app/src/main/java/com/fastaccess/data/dao/Pageable.java @@ -17,12 +17,12 @@ import lombok.Setter; @Getter @Setter @NoArgsConstructor public class Pageable implements Parcelable { - private int first; - private int next; - private int prev; - private int last; - private int totalCount; - private boolean incompleteResults; + public int first; + public int next; + public int prev; + public int last; + public int totalCount; + public boolean incompleteResults; public List items; @Override public int describeContents() { return 0; } diff --git a/app/src/main/java/com/fastaccess/data/dao/PayloadModel.java b/app/src/main/java/com/fastaccess/data/dao/PayloadModel.java index 7d6b39ac..0e5ac668 100644 --- a/app/src/main/java/com/fastaccess/data/dao/PayloadModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/PayloadModel.java @@ -22,17 +22,17 @@ import lombok.Setter; @Getter @Setter @NoArgsConstructor public class PayloadModel implements Parcelable { - private String action; - private Repo forkee; - private Issue issue; - private PullRequest pullRequest; - private String ref; - private String refType; - private Comment comment; - private User target; - private User member; - private String head; - private TeamsModel team; + public String action; + public Repo forkee; + public Issue issue; + public PullRequest pullRequest; + public String ref; + public String refType; + public Comment comment; + public User target; + public User member; + public String head; + public TeamsModel team; @Override public int describeContents() { return 0; } diff --git a/app/src/main/java/com/fastaccess/data/dao/SimpleUrlsModel.java b/app/src/main/java/com/fastaccess/data/dao/SimpleUrlsModel.java index cbbf08c7..ddff012b 100644 --- a/app/src/main/java/com/fastaccess/data/dao/SimpleUrlsModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/SimpleUrlsModel.java @@ -14,8 +14,9 @@ import lombok.Setter; @Getter @Setter public class SimpleUrlsModel implements Parcelable { - private String item; - private String url; + public String item; + public String url; + public SimpleUrlsModel(String item, String url) { this.item = item; this.url = url; diff --git a/app/src/main/java/com/fastaccess/data/service/UserRestService.java b/app/src/main/java/com/fastaccess/data/service/UserRestService.java index b935d97e..a5fd9fe9 100644 --- a/app/src/main/java/com/fastaccess/data/service/UserRestService.java +++ b/app/src/main/java/com/fastaccess/data/service/UserRestService.java @@ -34,11 +34,15 @@ public interface UserRestService { @GET("users/{username}/received_events") Observable> getReceivedEvents(@NonNull @Path("username") String userName, @Query("page") int page); - @GET("/users/{username}/repos") - Observable> getRepos(@Path("username") @NonNull String username, @QueryMap(encoded=true) Map filterParams, @Query("page") int page); + @GET("users/{username}/events") + Observable> getUserEvents(@NonNull @Path("username") String userName, @Query("page") int page); - @GET("/user/repos") - Observable> getRepos(@QueryMap(encoded=true) Map filterParams, @Query(value = "page") int page); + @GET("users/{username}/repos") + Observable> getRepos(@Path("username") @NonNull String username, @QueryMap(encoded = true) Map filterParams, + @Query("page") int page); + + @GET("user/repos") + Observable> getRepos(@QueryMap(encoded = true) Map filterParams, @Query(value = "page") int page); @GET("users/{username}/starred") Observable> getStarred(@Path("username") @NonNull String username, @Query("page") int page); @@ -52,10 +56,10 @@ public interface UserRestService { @GET("user/following/{username}") Observable> getFollowStatus(@Path("username") @NonNull String username); - @PUT("/user/following/{username}") + @PUT("user/following/{username}") Observable> followUser(@Path("username") @NonNull String username); - @DELETE("/user/following/{username}") + @DELETE("user/following/{username}") Observable> unfollowUser(@Path("username") @NonNull String username); @GET Observable getContributions(@Url String url); diff --git a/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java b/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java index 4cd47ee5..bc99fe2e 100644 --- a/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java +++ b/app/src/main/java/com/fastaccess/provider/scheme/SchemeParser.java @@ -338,7 +338,7 @@ public class SchemeParser { if (uri.getQueryParameterNames() != null && !uri.getQueryParameterNames().isEmpty()) { query = uri.getQueryParameter("since"); } - return TrendingActivity.TrendingIntent.getTrendingIntent(context, lang, query); + return TrendingActivity.Companion.getTrendingIntent(context, lang, query); } return null; } diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEvents.kt b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEvents.kt new file mode 100644 index 00000000..9e867ccb --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEvents.kt @@ -0,0 +1,32 @@ +package com.fastaccess.ui.modules.profile.events + +import android.support.v4.widget.SwipeRefreshLayout +import com.fastaccess.data.dao.SimpleUrlsModel +import com.fastaccess.data.dao.model.Event +import com.fastaccess.provider.rest.loadmore.OnLoadMore +import com.fastaccess.ui.base.mvp.BaseMvp +import com.fastaccess.ui.widgets.dialog.ListDialogView +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder + +/** + * Created by Kosh on 03 Jun 2017, 4:49 PM + */ +interface ProfileEvents { + + interface View : BaseMvp.FAView, SwipeRefreshLayout.OnRefreshListener, + android.view.View.OnClickListener, ListDialogView.onSimpleItemSelection { + fun onNotifyAdapter(events: List?, page: Int) + + fun onOpenRepoChooser(models: ArrayList) + + fun getLoadMore(): OnLoadMore + } + + interface Presenter : BaseMvp.FAPresenter, + BaseViewHolder.OnItemClickListener, + BaseMvp.PaginationListener { + fun getEvents(): ArrayList + + fun onWorkOffline() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsFragment.kt new file mode 100644 index 00000000..66e1a6f9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsFragment.kt @@ -0,0 +1,132 @@ +package com.fastaccess.ui.modules.profile.events + +import android.net.Uri +import android.os.Bundle +import android.support.annotation.StringRes +import android.support.v4.widget.SwipeRefreshLayout +import android.view.View +import com.fastaccess.R +import com.fastaccess.data.dao.SimpleUrlsModel +import com.fastaccess.data.dao.model.Event +import com.fastaccess.helper.BundleConstant +import com.fastaccess.helper.Bundler +import com.fastaccess.provider.rest.loadmore.OnLoadMore +import com.fastaccess.provider.scheme.SchemeParser +import com.fastaccess.ui.adapter.FeedsAdapter +import com.fastaccess.ui.base.BaseFragment +import com.fastaccess.ui.widgets.StateLayout +import com.fastaccess.ui.widgets.dialog.ListDialogView +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView +import java.util.* + +/** + * Created by Kosh on 11 Nov 2016, 12:36 PM + */ + +open class ProfileEventsFragment : BaseFragment(), ProfileEvents.View { + + val recycler by lazy { view!!.findViewById(R.id.recycler) as DynamicRecyclerView } + val refresh by lazy { view!!.findViewById(R.id.refresh) as SwipeRefreshLayout } + val stateLayout by lazy { view!!.findViewById(R.id.stateLayout) as StateLayout } + val adapter by lazy { FeedsAdapter(presenter.getEvents()) } + private var onLoadMore: OnLoadMore? = null + + override fun fragmentLayout(): Int { + return R.layout.micro_grid_refresh_list + } + + override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { + stateLayout.setEmptyText(R.string.no_feeds) + stateLayout.setOnReloadListener(this) + refresh.setOnRefreshListener(this) + recycler.setEmptyView(stateLayout, refresh) + adapter.listener = presenter + getLoadMore().setCurrent_page(presenter.currentPage, presenter.previousTotal) + recycler.adapter = adapter + recycler.addOnScrollListener(getLoadMore()) + if (presenter.getEvents().isEmpty() && !presenter.isApiCalled) { + onRefresh() + } + } + + override fun onRefresh() { + presenter.onCallApi(1, arguments.getString(BundleConstant.EXTRA)) + } + + override fun onNotifyAdapter(events: List?, page: Int) { + hideProgress() + if (events == null || events.isEmpty()) { + adapter.clear() + return + } + if (page <= 1) { + adapter.insertItems(events) + } else { + adapter.addItems(events) + } + } + + override fun showProgress(@StringRes resId: Int) { + refresh.isRefreshing = true + stateLayout.showProgress() + } + + override fun hideProgress() { + refresh.isRefreshing = false + stateLayout.hideProgress() + } + + override fun showErrorMessage(message: String) { + showReload() + super.showErrorMessage(message) + } + + override fun showMessage(titleRes: Int, msgRes: Int) { + showReload() + super.showMessage(titleRes, msgRes) + } + + override fun onOpenRepoChooser(models: ArrayList) { + val dialogView = ListDialogView() + dialogView.initArguments(getString(R.string.repo_chooser), models) + dialogView.show(childFragmentManager, "ListDialogView") + } + + override fun providePresenter(): ProfileEventsPresenter { + return ProfileEventsPresenter() + } + + override fun getLoadMore(): OnLoadMore { + if (onLoadMore == null) { + onLoadMore = OnLoadMore(presenter) + } + onLoadMore!!.parameter = arguments.getString(BundleConstant.EXTRA) + return onLoadMore as OnLoadMore + } + + override fun onDestroyView() { + recycler.removeOnScrollListener(getLoadMore()) + super.onDestroyView() + } + + override fun onClick(view: View) { + onRefresh() + } + + override fun onItemSelected(item: SimpleUrlsModel) { + SchemeParser.launchUri(context, Uri.parse(item.item)) + } + + private fun showReload() { + hideProgress() + stateLayout.showReload(adapter.itemCount) + } + + companion object { + fun newInstance(login: String): ProfileEventsFragment { + val fragment = ProfileEventsFragment() + fragment.arguments = Bundler.start().put(BundleConstant.EXTRA, login).end() + return fragment + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsPresenter.kt new file mode 100644 index 00000000..0f1db9ed --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/events/ProfileEventsPresenter.kt @@ -0,0 +1,127 @@ +package com.fastaccess.ui.modules.profile.events + +import android.net.Uri +import android.view.View +import com.annimon.stream.Collectors +import com.annimon.stream.Stream +import com.fastaccess.data.dao.NameParser +import com.fastaccess.data.dao.Pageable +import com.fastaccess.data.dao.SimpleUrlsModel +import com.fastaccess.data.dao.model.Event +import com.fastaccess.data.dao.types.EventsType +import com.fastaccess.helper.RxHelper +import com.fastaccess.provider.rest.RestProvider +import com.fastaccess.provider.scheme.SchemeParser +import com.fastaccess.ui.base.mvp.presenter.BasePresenter +import com.fastaccess.ui.modules.repos.RepoPagerActivity +import com.fastaccess.ui.modules.repos.code.commit.details.CommitPagerActivity +import java.util.* + +/** + * Created by Kosh on 11 Nov 2016, 12:36 PM + */ + +class ProfileEventsPresenter : BasePresenter(), ProfileEvents.Presenter { + private val eventsModels = ArrayList() + private var page: Int = 0 + private var previousTotal: Int = 0 + private var lastPage = Integer.MAX_VALUE + + override fun getCurrentPage(): Int { + return page + } + + override fun getPreviousTotal(): Int { + return previousTotal + } + + override fun setCurrentPage(page: Int) { + this.page = page + } + + override fun setPreviousTotal(previousTotal: Int) { + this.previousTotal = previousTotal + } + + override fun onCallApi(page: Int, parameter: String?) { + if (page == 1 || parameter.isNullOrEmpty()) { + lastPage = Integer.MAX_VALUE + sendToView { view -> view.getLoadMore().reset() } + } + if (page > lastPage || lastPage == 0) { + sendToView({ it.hideProgress() }) + return + } + currentPage = page + makeRestCall>(RestProvider.getUserService().getUserEvents(parameter!!, page)) { response -> + lastPage = response.last + sendToView { view -> view.onNotifyAdapter(response.items, page) } + } + } + + override fun onSubscribed() { + sendToView { view -> view.showProgress(0) } + } + + override fun onError(throwable: Throwable) { + onWorkOffline() + super.onError(throwable) + } + + override fun getEvents(): ArrayList { + return eventsModels + } + + override fun onWorkOffline() { + if (eventsModels.isEmpty()) { + manageDisposable(RxHelper.getObserver(Event.getEvents().toObservable()) + .subscribe({ modelList -> + if (modelList != null) { + sendToView { view -> view.onNotifyAdapter(modelList, 1) } + } + }, { it.printStackTrace() })) + } else { + sendToView({ it.hideProgress() }) + } + } + + override fun onItemClick(position: Int, v: View, item: Event) { + if (item.type == EventsType.ForkEvent) { + val parser = NameParser(item.payload.forkee.htmlUrl) + RepoPagerActivity.startRepoPager(v.context, parser) + } else { + val payloadModel = item.payload + if (payloadModel != null) { + if (payloadModel.head != null) { + val repoModel = item.repo + val uri = Uri.parse(repoModel.name) + if (uri == null || uri.pathSegments.size < 1) return + val intent = CommitPagerActivity.createIntent(v.context, uri.lastPathSegment, uri.pathSegments[0], + payloadModel.head, true) + v.context.startActivity(intent) + } else if (item.payload.issue != null) { + SchemeParser.launchUri(v.context, Uri.parse(item.payload.issue.htmlUrl), true) + } else if (item.payload.pullRequest != null) { + SchemeParser.launchUri(v.context, Uri.parse(item.payload.pullRequest.htmlUrl), true) + } else if (item.payload.comment != null) { + SchemeParser.launchUri(v.context, Uri.parse(item.payload.comment.htmlUrl), true) + } else { + val repoModel = item.repo + if (item.repo != null) SchemeParser.launchUri(v.context, Uri.parse(repoModel.name), true) + } + } + } + } + + override fun onItemLongClick(position: Int, v: View, item: Event) { + if (item.type == EventsType.ForkEvent) { + if (view != null) { + view!!.onOpenRepoChooser(Stream.of(SimpleUrlsModel(item.repo.name, item.repo.url), + SimpleUrlsModel(item.payload.forkee.fullName, item.payload.forkee.htmlUrl)) + .collect(Collectors.toCollection(::arrayListOf))) + } + } else { + onItemClick(position, v, item) + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java index b1bc6d07..d78497d8 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/IssuePagerActivity.java @@ -139,7 +139,9 @@ public class IssuePagerActivity extends BaseActivity implements I } @Override public void onUpdateIssue(@NonNull Issue issue) { - this.issueModel = issue; + this.issueModel.setBody(issue.getBody()); + this.issueModel.setBodyHtml(issue.getBodyHtml()); + this.issueModel.setTitle(issue.getTitle()); this.issueModel.setLogin(login); this.issueModel.setRepoId(repoId); manageObservable(issueModel.save(issueModel).toObservable()); diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt index 946c0a96..da31dad7 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt @@ -33,7 +33,7 @@ class TrendingActivity : BaseActivity(), Tr @State var selectedTitle: String = "" - companion object TrendingIntent { + companion object { fun getTrendingIntent(context: Context, lang: String?, query: String?): Intent { val intent = Intent(context, TrendingActivity::class.java) intent.putExtras(Bundler.start() diff --git a/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java index a1bd9b2b..508e0d27 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java @@ -6,9 +6,10 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.FragmentPagerAdapterModel; import com.fastaccess.data.dao.model.Login; @@ -19,14 +20,12 @@ import com.fastaccess.ui.adapter.FragmentsPagerAdapter; import com.fastaccess.ui.base.BaseActivity; import com.fastaccess.ui.base.BaseFragment; import com.fastaccess.ui.modules.main.MainActivity; -import com.fastaccess.ui.modules.changelog.ChangelogBottomSheetDialog; import com.fastaccess.ui.modules.profile.org.repos.OrgReposFragment; import com.fastaccess.ui.modules.profile.repos.ProfileReposFragment; import com.fastaccess.ui.widgets.ViewPagerView; import butterknife.BindView; import butterknife.OnClick; -import com.evernote.android.state.State; import shortbread.Shortcut; /** @@ -125,25 +124,13 @@ public class UserPagerActivity extends BaseActivity Date: Sat, 3 Jun 2017 20:29:32 +0300 Subject: [PATCH 04/14] Finish Lithuanian translations --- app/src/main/res/values-lt/strings.xml | 311 ++++++++++++------------- 1 file changed, 154 insertions(+), 157 deletions(-) diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index bfe48633..bf5b25a6 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -105,12 +105,12 @@ Naudotojai Pavadinimas Failas - Slenksčiai (etapai) + Tikslai (slenksčiai arba etapai) Skirti sau Pateikti taloną Nepavyko sukurti taloną Kurti taloną - Please un-select highlighting to continue editing. + Atrinkite pažymėjimą, norint toliau redaguoti tekstą. Pranešimai Turite neperskaitytų pranešimų Atverti @@ -130,32 +130,32 @@ Prisijungti Gisto aprašymas Gali peržiūrėti žmonių profilius, jeigu spustelėsi ant jų profilio paveikslėlių. - Long click on a fork event to open either the original or the forked repo. + Ilgiau spustelėk ant kopijavimo (fork) mygtuko, jei nori atverti orginalią talpyklą arba kopijuotą talpyklą (repo). Parsiųsti leidimą (-us) Pasirinkimai - Download file or share directory - Tap a comment to tag its author or to edit your comment. - \nLong press your comment to delete it. + Parsiųsti failą arba dalintis duomenimis + Spausk komentarą, jei nori pridėti etiketę arba keisti komentarą. + \nIlgiau spaustelėk savo komentarą, norint jį ištrinti. Žvaigždės žymėjimas Stebėti Stebėti talpyklą (repo) - Pin repos to access them faster from the navigation drawer. - Dismiss All - No URL found - Last updated - Preview - Syntax highlighter - Toggle syntax highlighting. - \nScroll the Markdown editor icons for more options. - Creation date - When the file was created - When the file was last updated - Mark all as read - All notifications - Unread - All - Delete Repo - Deleting this repo can\'t be undone + Prisegtas talpyklas galima lengvai matyti viename sąraše, kuris randamas meniu. + Atmesti viską + Nuoroda nebuvo rasta + Pask. kart. atnaujinta + Peržiūrėti + Sintaksės pabrėžimas + Sintaksės pabrėžimas + \nSlinkite ikonus „Markdown“ rengyklėje, jei norite daugiau pasirinkimų. + Sukūrimo data + Kada failas buvo sukurtas + Kada failas buvo preitą kartą atnaujintas + Perskaičiau + Visi pranešimai + Neperskaityta + Viskas + Ištrinti talpyklą (repo) + Jeigu ištrinsite šią talpyklą (repo), jos duomenys dings amžinai 30 min. 20 min. 10 min. @@ -171,118 +171,117 @@ Paliko komentarą Narys PR komentaras - Pushed to + Nustumti pakeitimai (push; commit) Komandos narys Ištrynė Nežinoma(-s) - Commented on commit - Switch branch - Assignees - Edit - \u2022 edited - Update Issue - Update Pull Request - No milestones - Add - Done - Home - Create Milestone - Error creating milestone - Due on - No assignees - this - Commits have been switched to the selected branch - General - Change how frequently FastHub checks for new notifications - Notification Sync Interval - Every - Behavior - Customization - Enable list animations - List Animation - Disable dialog preventing accidental app closure - Press Back to Exit - Restore - Backup - Successfully backed up! - Select Backup to Restore - Permission Not Granted. - Last update: %s - Now - Any unsaved changes will be discarded - Private - Use rounded rectangle avatars instead of circular avatars - Avatar Appearance - Rate this app - Author - Fork on GitHub - Send an email - Question concerning FastHub - Feedback - Report an issue - Having an issue? Report it here - About - Notification - Turn off - Unauthorized user - Two factors OTP is required - No issue - Copy - Copied - Commit message - Error occurred while communicating to the server - Unexpected error while requesting API - Error requesting server, please try again later - Marking notification as read - Forking gist - Login using your default browser (OAuth) - OR - Disable Notification Read Status - Disable marking as read upon clicking a notification. - Choose Theme - Choose your default theme - Choose theme accent color - Theme Accent Color + Paliktas komentaras po pokyčių (commito) + Keisti skyrių (branch) + Paskirtieji + Atnaujinti + \u2022 pakeitė + Atnajinti taloną + Atnaujinti PR + Nepavyko sukurti tikslo (milestonų) + Pridėti + Baigta + Namai + Sukurti tikslą + Nepavyko sukurti tikslą + Pabaigti pagal šią datą: + Nepaskirta + tai + Pakeitimai (commit) buvo pakeisti į pasirinktą skyrių (branch) + Bendra + Kas kiek laiko „FastHub“ turėtų žiūrėti, ar yra naujų pranešimų automatiškai? + Pranešimų grėbimo intervalas + Kiekvieną/kas + Elgesys + Pritaikymas tau + Įgalinti sąrašo animacija + Sąrašo animacija + Klausti ar noriu užverti „FastHub“ + Dar kart spausk atgal, jei nori užverti programą + Parsiųsti atsarg. kopijos duomenis + Įkelti duomenis + Padaryta atsarg. kopija! + Padaryti atsarg. kopiją + Nėra leidimo + Atnaujinta: %s + Dabar + Jeigu neišsaugosite savo pakeitimų, jie dings + Nėra paviešinta + Rodyti stačiakampius arba apskritimo tipo profilio nuotraukas + Profilio nuotraukų išvaizda + Įvertink programėlę + Autorius + Kopijuoti (fork) „GitHub“ + Siųsti el. paštu + Klausimas apie programą + Atsiliepimai + Pranešti problemą + Pranešk savo problemą (tik angl.) + Apie + Pranešimas + Išjungti + Neautorizuotas naudotojas + Reikia 2FA OTP kodo + Nėra talonų(-o) + Kopijuoti + Nukopijuota + Pakeitimo (commito) pastabos + Nepavyko susisiekti su serveriu + Supyko API serveris + Serveris supyko, pabandykite užkrauti iš naujo + Kategorizuojamas, kaip perskaitytas + Kopijuojamas (fork) gistą + Prisijungti naudojant naršykle (OAuth) + arba + Perskaičius pranešimą, nepažymėti kaip perskaitytą + Nepažymėti pranešimą, kaip perskaitytą, kai jį noriu perskaityti + Pasirinkti temą + Pasirinkti numatytą temą + Pasirinkti akcentinę spalvą + Akcentinė spalva Svetainė - Support Development - Thanks for being extremely awesome! - If the theme didn\'t apply properly, please kill and open the app manually. - Pin - Pinned - Unpin - No pinned repositories yet, pin some to see them here - Yes - No - No feeds - No gists - No comments - No notifications - No followers - No followings - No repositories - No starred repositories - No commits - No contributors - No releases - No closed issues - No open issues - No events - No open pull requests - No closed pull requests - No search results - Please accept the permissions in order for FastHub to - store the files on your device to be able to view them + Paremkite programos autorių + Ačiū. Tu šaunus vaikinas (arba šauni panelė)! + Jeigu nematote pakeitimų, iš naujo įjunkite programėlę + Prisegti + Prisegta + Atsegti + Nėra prisegtų talpyklų (repų) + Taip + Ne + Nėra sratų + Nėra gistų + Nėra komentarų + Nėra pranešimų + Niekas neseka + Nieko neseka + Nėra talpyklų (repo) + Nėra žvaigžzde pažymėtų talpyklų (repo) + Nėra pakeitimų + Nėra bendrų autorių + Nėra leidimų + Nėra užvertų talonų + Nėra atvertų talonų + Nėra įvykių + Nėra atvirų PR + Nėra užvertų PR + Paieška nieko nerado + Leiskite programėlei talpinti failus, jeigu norite peržiūrėti juos Vieši gistai Rodyti reklamas - No issues - No unread notifications. + Nėra talonų + Visi pranešimai perskaityti Mano gistai Pakeitimai - Click to open notifications list or swipe to dismiss - Long press to navigate to main screen from anywhere - Created - Assigned - Mentioned + Spausk čia, jei nori peržiūrėti pranešimus ir slink pranešimo kortelę ant dešinės, jei nori pašalinti iš sąrašo + Spustelėkite ilgiau, jei norite aplankyti pagr. ekraną bet kur + Sukurta + Paskirta + Paminėta Pavadinimas Spalva Sukurti etiketę @@ -294,40 +293,40 @@ Nėra narių Nėra grupelių Nęra organizacijų - Pažymėti, kad perskaitei + Perskaičiau Animacijos Įgalinti sarąšo animaciją Animacinis efektas - Slenksčiai (etapai) - Assignee + Tikslai + Paskirtasis Patikrinime buvo rasta problemų Vyksta patikrinimas Viskas OK Rūšiuoti Naujausia (-s) Seniausia (-s) - Most commented - Least commented - Recently updated - Least recently updated - You\'re up to date! - A new version is available. + Daugiausiai koment. + Mažiausiai koment. + Neseniai atnauj. + Seniaus. atnauj. + Viskas atnaujinta! + Nauja versija pasiekiama Kažką parašyk į tuščią laukelį! - Long press to create issue ticket on the fly - This pull request can be merged + Spustelėkite truputį ilgiau, jei norite sukurti taloną iš karto + Šie pakeitimai neturi konfliktų (PR) peržiūrėjo - dismissed their review - approved these changes - No reactions - Reactions - Wrap content - Wrap code in code viewer by default - Wrap Code - Open source libraries - Enable notification sound once a notification is received. + atmetė jų peržiūra (PR) + priėmė šiuos pakeitimus (PR) + Nėra reakcijų + Reakcijos + Teksto kėlimas į kitą liniją + Kelti tekstą į kitą liniją, kai peržiūriamas turinys + Teksto kėlimas į kitą liniją + Atviro kodo „bibliotekos“ + Groti garsą, kai gaunu pranešimą. Įgalinti pranešimų garsą Įgalinti pranešimus - Prisijungti su „Personal Token“ + Prisijungti su „asmeniniu tokenu“ Naudoti asmeninį tokeną Prisijungti „paprastai“ Nematai savo organizacijų? Žiūrėk čia:\nhttps://help.github.com/articles/about-third-party-application-restrictions\nArba iš naujo prisijunk naudodama (-s) tokeną, angl. „access token“. @@ -341,7 +340,7 @@ Programos kalba Kalba Pasirinkti kalbą - Pasirinkite tokią, kuri jums lengviausia + Pasirinkite tokią, kuri jums lengviausia suprasti Atšaukti prenumeratą nuo viduje @@ -358,7 +357,7 @@ Pavyko pakviesti Atsakyti Nepavyko užkrauti paveikslėlį - noriu sujungti kodą + noriu sujungti kodą (PR) Stebėtojai Vertintojai Nėra vertintojų @@ -370,14 +369,12 @@ Etiketės Pridėti komentarą Nauja funkcija: profilio viršeliai - With FastHub 2.5.0, you can now better express yourself with banners - for your profile page.\n\nAnyone using the FastHub app, will see your header, and you\'ll - begin seeing other peoples headers as well! If you\'d like to create a banner for yourself, - make it 1280x384 or divisible, otherwise, it may get cropped.\n\nYou can add or change your - banner at any time, by creating a gist described "header.fst" with a file containing a - direct link to the header image.\n\nOr even simpler, just use the built-in image chooser! + Pastabos: Profilio viršelius mato tik „FastHub“ 2.5+ programėlės vartotojai. + Geriausias dydis viršeliui yra: 1280x384. Jeigu neturite „FastHub“ programėlės, bet vistiek + norite pakeisti viršelį, sukurkite gistą, kuris pavadintas „header.fst“. Jis gali nustatyti + jūsų viršelį. Giste turi būti nuoroda, kurioje yra patalpintas viršelis (pvz. „Imgur direct link“). - PASIRINKTI VIRŠLIO NUOTRAUKĄ + Pasirinkti viršelio nuotrauką Nepavyko įkrauti paveikslėlį, dar kartą pabandykite vėliau. Populiaru „GitHub“ neleidžia rūšiuoti pagal jaustukus dėl techninių limitacijų. From 500efcf8e607ffb3a6d2a077f543286982f203cc Mon Sep 17 00:00:00 2001 From: Kosh Date: Sun, 4 Jun 2017 03:42:15 +0800 Subject: [PATCH 05/14] this commit fixes #544 and makes fastScroller to paginate as per #556 & preparing for 3.0.0 --- app/build.gradle | 4 +- .../vending/billing/IInAppBillingService.aidl | 144 ------------------ .../data/service/UserRestService.java | 3 + .../java/com/fastaccess/helper/RxHelper.java | 1 - .../timeline/handler/ListsHandler.java | 2 - .../fastaccess/ui/adapter/FeedsAdapter.java | 13 +- .../adapter/viewholder/FeedsViewHolder.java | 20 ++- .../com/fastaccess/ui/base/BaseFragment.java | 8 +- .../filter/issues/FilterIssuesActivity.java | 65 ++++---- .../profile/events/ProfileEventsFragment.kt | 8 +- .../profile/events/ProfileEventsPresenter.kt | 12 +- .../starred/ProfileStarredFragment.java | 26 +++- .../profile/starred/ProfileStarredMvp.java | 2 + .../starred/ProfileStarredPresenter.java | 40 +++-- .../code/commit/RepoCommitsPresenter.java | 11 +- .../commit/details/CommitPagerPresenter.java | 44 +++--- .../repos/code/files/RepoFilesPresenter.java | 7 +- .../code/prettifier/ViewerPresenter.java | 73 +++++---- .../code/releases/RepoReleasesPresenter.java | 16 +- .../timeline/IssueTimelineFragment.java | 1 + .../timeline/PullRequestTimelineFragment.java | 1 + .../ui/modules/trending/TrendingActivity.kt | 16 +- .../trending/fragment/TrendingFragment.kt | 3 +- .../ui/modules/user/UserPagerActivity.java | 37 ++++- .../ui/modules/user/UserPagerMvp.java | 3 +- .../scroll/RecyclerFastScroller.java | 86 ++++++----- .../layout/feeds_row_no_image_item.xml | 16 +- 27 files changed, 311 insertions(+), 351 deletions(-) delete mode 100755 app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl diff --git a/app/build.gradle b/app/build.gradle index 07473fe7..43601ee7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,8 +28,8 @@ android { applicationId "com.fastaccess.github" minSdkVersion 21 targetSdkVersion 26 - versionCode 253 - versionName "2.5.3" + versionCode 300 + versionName "3.0.0" signingConfig signingConfigs.signing buildConfigString "GITHUB_CLIENT_ID", (buildProperties.secrets['github_client_id'] | buildProperties.notThere['github_client_id']).string buildConfigString "GITHUB_SECRET", (buildProperties.secrets['github_secret'] | buildProperties.notThere['github_secret']).string diff --git a/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl b/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl deleted file mode 100755 index 2a492f78..00000000 --- a/app/src/main/aidl/com/android/vending/billing/IInAppBillingService.aidl +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.vending.billing; - -import android.os.Bundle; - -/** - * InAppBillingService is the service that provides in-app billing version 3 and beyond. - * This service provides the following features: - * 1. Provides a new API to get details of in-app items published for the app including - * price, type, title and description. - * 2. The purchase flow is synchronous and purchase information is available immediately - * after it completes. - * 3. Purchase information of in-app purchases is maintained within the Google Play system - * till the purchase is consumed. - * 4. An API to consume a purchase of an inapp item. All purchases of one-time - * in-app items are consumable and thereafter can be purchased again. - * 5. An API to get current purchases of the user immediately. This will not contain any - * consumed purchases. - * - * All calls will give a response code with the following possible values - * RESULT_OK = 0 - success - * RESULT_USER_CANCELED = 1 - user pressed back or canceled a dialog - * RESULT_BILLING_UNAVAILABLE = 3 - this billing API version is not supported for the type requested - * RESULT_ITEM_UNAVAILABLE = 4 - requested SKU is not available for purchase - * RESULT_DEVELOPER_ERROR = 5 - invalid arguments provided to the API - * RESULT_ERROR = 6 - Fatal error during the API action - * RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned - * RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned - */ -interface IInAppBillingService { - /** - * Checks support for the requested billing API version, package and in-app type. - * Minimum API version supported by this interface is 3. - * @param apiVersion the billing version which the app is using - * @param packageName the package name of the calling app - * @param type type of the in-app item being purchased "inapp" for one-time purchases - * and "subs" for subscription. - * @return RESULT_OK(0) on success, corresponding result code on failures - */ - int isBillingSupported(int apiVersion, String packageName, String type); - - /** - * Provides details of a list of SKUs - * Given a list of SKUs of a valid type in the skusBundle, this returns a bundle - * with a list JSON strings containing the productId, price, title and description. - * This API can be called with a maximum of 20 SKUs. - * @param apiVersion billing API version that the Third-party is using - * @param packageName the package name of the calling app - * @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST" - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "DETAILS_LIST" with a StringArrayList containing purchase information - * in JSON format similar to: - * '{ "productId" : "exampleSku", "type" : "inapp", "price" : "$5.00", - * "title : "Example Title", "description" : "This is an example description" }' - */ - Bundle getSkuDetails(int apiVersion, String packageName, String type, in Bundle skusBundle); - - /** - * Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU, - * the type, a unique purchase token and an optional developer payload. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param sku the SKU of the in-app item as published in the developer console - * @param type the type of the in-app item ("inapp" for one-time purchases - * and "subs" for subscription). - * @param developerPayload optional argument to be sent back with the purchase information - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "BUY_INTENT" - PendingIntent to start the purchase flow - * - * The Pending intent should be launched with startIntentSenderForResult. When purchase flow - * has completed, the onActivityResult() will give a resultCode of OK or CANCELED. - * If the purchase is successful, the result data will contain the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_DATA" - String in JSON format similar to - * '{"orderId":"12999763169054705758.1371079406387615", - * "packageName":"com.example.app", - * "productId":"exampleSku", - * "purchaseTime":1345678900000, - * "purchaseToken" : "122333444455555", - * "developerPayload":"example developer payload" }' - * "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that - * was signed with the private key of the developer - * TODO: change this to app-specific keys. - */ - Bundle getBuyIntent(int apiVersion, String packageName, String sku, String type, - String developerPayload); - - /** - * Returns the current SKUs owned by the user of the type and package name specified along with - * purchase information and a signature of the data to be validated. - * This will return all SKUs that have been purchased in V3 and managed items purchased using - * V1 and V2 that have not been consumed. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param type the type of the in-app items being requested - * ("inapp" for one-time purchases and "subs" for subscription). - * @param continuationToken to be set as null for the first call, if the number of owned - * skus are too many, a continuationToken is returned in the response bundle. - * This method can be called again with the continuation token to get the next set of - * owned skus. - * @return Bundle containing the following key-value pairs - * "RESPONSE_CODE" with int value, RESULT_OK(0) if success, other response codes on - * failure as listed above. - * "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs - * "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information - * "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures - * of the purchase information - * "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the - * next set of in-app purchases. Only set if the - * user has more owned skus than the current list. - */ - Bundle getPurchases(int apiVersion, String packageName, String type, String continuationToken); - - /** - * Consume the last purchase of the given SKU. This will result in this item being removed - * from all subsequent responses to getPurchases() and allow re-purchase of this item. - * @param apiVersion billing API version that the app is using - * @param packageName package name of the calling app - * @param purchaseToken token in the purchase information JSON that identifies the purchase - * to be consumed - * @return 0 if consumption succeeded. Appropriate error values for failures. - */ - int consumePurchase(int apiVersion, String packageName, String purchaseToken); -} diff --git a/app/src/main/java/com/fastaccess/data/service/UserRestService.java b/app/src/main/java/com/fastaccess/data/service/UserRestService.java index a5fd9fe9..520d5806 100644 --- a/app/src/main/java/com/fastaccess/data/service/UserRestService.java +++ b/app/src/main/java/com/fastaccess/data/service/UserRestService.java @@ -47,6 +47,9 @@ public interface UserRestService { @GET("users/{username}/starred") Observable> getStarred(@Path("username") @NonNull String username, @Query("page") int page); + @GET("users/{username}/starred?per_page=1") Observable> + getStarredCount(@Path("username") @NonNull String username); + @GET("users/{username}/following") Observable> getFollowing(@Path("username") @NonNull String username, @Query("page") int page); diff --git a/app/src/main/java/com/fastaccess/helper/RxHelper.java b/app/src/main/java/com/fastaccess/helper/RxHelper.java index 502f18a7..daf6431e 100644 --- a/app/src/main/java/com/fastaccess/helper/RxHelper.java +++ b/app/src/main/java/com/fastaccess/helper/RxHelper.java @@ -21,7 +21,6 @@ public class RxHelper { public static Observable safeObservable(@NonNull Observable observable) { return getObserver(observable) - .onErrorReturn(throwable -> (T) new Object()) .doOnError(Throwable::printStackTrace); } diff --git a/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java b/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java index 1b210377..f9cc16e8 100644 --- a/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java +++ b/app/src/main/java/com/fastaccess/provider/timeline/handler/ListsHandler.java @@ -4,7 +4,6 @@ import android.graphics.drawable.Drawable; import android.support.annotation.Nullable; import android.text.SpannableStringBuilder; -import com.fastaccess.helper.Logger; import com.fastaccess.ui.widgets.SpannableBuilder; import net.nightwhistler.htmlspanner.TagNodeHandler; @@ -47,7 +46,6 @@ import lombok.NoArgsConstructor; TodoItems todoItem = null; if (node.getChildTags() != null && node.getChildTags().length > 0) { for (TagNode tagNode : node.getChildTags()) { - Logger.e(tagNode.getName(), tagNode.getAttributes(), tagNode.getText()); if (tagNode.getName() != null && tagNode.getName().equals("input")) { todoItem = new TodoItems(); todoItem.isChecked = tagNode.getAttributeByName("checked") != null; diff --git a/app/src/main/java/com/fastaccess/ui/adapter/FeedsAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/FeedsAdapter.java index d6e35821..43cf047e 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/FeedsAdapter.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/FeedsAdapter.java @@ -16,12 +16,19 @@ import java.util.ArrayList; public class FeedsAdapter extends BaseRecyclerAdapter> { - public FeedsAdapter(@NonNull ArrayList Events) { - super(Events); + private boolean noImage; + + public FeedsAdapter(@NonNull ArrayList events) { + this(events, false); + } + + public FeedsAdapter(@NonNull ArrayList events, boolean noImage) { + super(events); + this.noImage = noImage; } @Override protected FeedsViewHolder viewHolder(ViewGroup parent, int viewType) { - return new FeedsViewHolder(FeedsViewHolder.getView(parent), this); + return new FeedsViewHolder(FeedsViewHolder.getView(parent, noImage), this); } @Override protected void onBindView(FeedsViewHolder holder, int position) { diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FeedsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FeedsViewHolder.java index c4a9a0d3..5550959d 100644 --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FeedsViewHolder.java +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FeedsViewHolder.java @@ -29,7 +29,7 @@ import butterknife.BindView; public class FeedsViewHolder extends BaseViewHolder { - @BindView(R.id.avatarLayout) AvatarLayout avatar; + @Nullable @BindView(R.id.avatarLayout) AvatarLayout avatar; @BindView(R.id.description) FontTextView description; @BindView(R.id.title) FontTextView title; @BindView(R.id.date) FontTextView date; @@ -40,15 +40,21 @@ public class FeedsViewHolder extends BaseViewHolder { super(itemView, adapter); } - public static View getView(@NonNull ViewGroup viewGroup) { - return getView(viewGroup, R.layout.feeds_row_item); + public static View getView(@NonNull ViewGroup viewGroup, boolean noImage) { + if (noImage) { + return getView(viewGroup, R.layout.feeds_row_no_image_item); + } else { + return getView(viewGroup, R.layout.feeds_row_item); + } } @Override public void bind(@NonNull Event eventsModel) { - if (eventsModel.getActor() != null) { - avatar.setUrl(eventsModel.getActor().getAvatarUrl(), eventsModel.getActor().getLogin(), eventsModel.getActor().isOrganizationType()); - } else { - avatar.setUrl(null, null); + if (avatar != null) { + if (eventsModel.getActor() != null) { + avatar.setUrl(eventsModel.getActor().getAvatarUrl(), eventsModel.getActor().getLogin(), eventsModel.getActor().isOrganizationType()); + } else { + avatar.setUrl(null, null); + } } SpannableBuilder spannableBuilder = SpannableBuilder.builder(); spannableBuilder.append(eventsModel.getActor() != null ? eventsModel.getActor().getLogin() : "N/A").append(" "); diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java b/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java index dff26516..70cbe91c 100644 --- a/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java +++ b/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java @@ -114,13 +114,9 @@ public abstract class BaseFragment lists = new ArrayList<>(); Collections.addAll(lists, getResources().getStringArray(R.array.sort_prs_issues)); lists.add(CommentsHelper.getThumbsUp()); @@ -301,12 +287,23 @@ public class FilterIssuesActivity extends BaseActivity new Handler().postDelayed(() -> { + //hacky way to dismiss on re-selecting tab. + if (assignee == null || milestone == null || sort == null || labels == null) return; + assignee.setTag(null); + milestone.setTag(null); + sort.setTag(null); + labels.setTag(null); + }, 100)); } - return false; + popupWindow.setContentView(viewHolder.view); } private void onSearch() { @@ -470,7 +467,7 @@ public class FilterIssuesActivity extends BaseActivity? = null override fun fragmentLayout(): Int { @@ -44,6 +44,7 @@ open class ProfileEventsFragment : BaseFragment(), ProfileEvent } override fun onWorkOffline() { - if (eventsModels.isEmpty()) { - manageDisposable(RxHelper.getObserver(Event.getEvents().toObservable()) - .subscribe({ modelList -> - if (modelList != null) { - sendToView { view -> view.onNotifyAdapter(modelList, 1) } - } - }, { it.printStackTrace() })) - } else { - sendToView({ it.hideProgress() }) - } + //TODO + sendToView({ it.hideProgress() }) } override fun onItemClick(position: Int, v: View, item: Event) { diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredFragment.java index 92ddf34c..c5d51b2c 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredFragment.java @@ -1,5 +1,6 @@ package com.fastaccess.ui.modules.profile.starred; +import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -14,6 +15,7 @@ import com.fastaccess.helper.Bundler; import com.fastaccess.provider.rest.loadmore.OnLoadMore; import com.fastaccess.ui.adapter.ReposAdapter; import com.fastaccess.ui.base.BaseFragment; +import com.fastaccess.ui.modules.repos.RepoPagerMvp; import com.fastaccess.ui.widgets.StateLayout; import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; @@ -32,6 +34,7 @@ public class ProfileStarredFragment extends BaseFragment onLoadMore; private ReposAdapter adapter; + private RepoPagerMvp.TabsBadgeListener tabsBadgeListener; public static ProfileStarredFragment newInstance(@NonNull String username) { ProfileStarredFragment view = new ProfileStarredFragment(); @@ -39,6 +42,21 @@ public class ProfileStarredFragment extends BaseFragment items, int page) { hideProgress(); if (items == null || items.isEmpty()) { @@ -81,9 +99,7 @@ public class ProfileStarredFragment extends BaseFragment items, int page); @NonNull OnLoadMore getLoadMore(); + + void onUpdateCount(int starredCount); } interface Presenter extends BaseMvp.FAPresenter, diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredPresenter.java index 9c8bb7fb..26cba3da 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/starred/ProfileStarredPresenter.java @@ -5,6 +5,7 @@ import android.support.annotation.Nullable; import android.view.View; import com.fastaccess.data.dao.NameParser; +import com.fastaccess.data.dao.Pageable; import com.fastaccess.data.dao.model.Repo; import com.fastaccess.helper.RxHelper; import com.fastaccess.provider.rest.RestProvider; @@ -13,12 +14,15 @@ import com.fastaccess.ui.modules.repos.RepoPagerActivity; import java.util.ArrayList; +import io.reactivex.Observable; + /** * Created by Kosh on 03 Dec 2016, 3:48 PM */ class ProfileStarredPresenter extends BasePresenter implements ProfileStarredMvp.Presenter { + @com.evernote.android.state.State int starredCount = -1; private ArrayList repos = new ArrayList<>(); private int page; private int previousTotal; @@ -62,14 +66,28 @@ class ProfileStarredPresenter extends BasePresenter impl sendToView(ProfileStarredMvp.View::hideProgress); return; } - makeRestCall(RestProvider.getUserService().getStarred(parameter, page), - repoModelPageable -> { - lastPage = repoModelPageable.getLast(); - if (getCurrentPage() == 1) { - manageObservable(Repo.saveStarred(repoModelPageable.getItems(), parameter)); - } - sendToView(view -> view.onNotifyAdapter(repoModelPageable.getItems(), page)); - }); + Observable> observable; + if (starredCount == -1) { + observable = Observable.zip(RestProvider.getUserService().getStarred(parameter, page), + RestProvider.getUserService().getStarredCount(parameter), (repoPageable, count) -> { + if (count != null) { + starredCount = count.getLast(); + } + return repoPageable; + }); + } else { + observable = RestProvider.getUserService().getStarred(parameter, page); + } + makeRestCall(observable, repoModelPageable -> { + lastPage = repoModelPageable.getLast(); + if (getCurrentPage() == 1) { + manageObservable(Repo.saveStarred(repoModelPageable.getItems(), parameter)); + } + sendToView(view -> { + view.onUpdateCount(starredCount); + view.onNotifyAdapter(repoModelPageable.getItems(), page); + }); + }); } @NonNull @Override public ArrayList getRepos() { @@ -79,7 +97,11 @@ class ProfileStarredPresenter extends BasePresenter impl @Override public void onWorkOffline(@NonNull String login) { if (repos.isEmpty()) { manageDisposable(RxHelper.getObserver(Repo.getStarred(login).toObservable()).subscribe(repoModels -> - sendToView(view -> view.onNotifyAdapter(repoModels, 1)))); + sendToView(view -> { + starredCount = -1; + view.onUpdateCount(repoModels != null ? repoModels.size() : 0); + view.onNotifyAdapter(repoModels, 1); + }))); } else { sendToView(ProfileStarredMvp.View::hideProgress); } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/RepoCommitsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/RepoCommitsPresenter.java index 815a85d6..9ad02dbf 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/RepoCommitsPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/RepoCommitsPresenter.java @@ -93,14 +93,14 @@ class RepoCommitsPresenter extends BasePresenter implements RestProvider.getRepoService().getTags(login, repoId), (branchPageable, tags) -> { ArrayList branchesModels = new ArrayList<>(); - if (branchPageable.getItems() != null) { + if (branchPageable != null && branchPageable.getItems() != null) { branchesModels.addAll(Stream.of(branchPageable.getItems()) .map(branchesModel -> { branchesModel.setTag(false); return branchesModel; }).collect(Collectors.toList())); } - if (tags != null) { + if (tags != null && tags.getItems() != null) { branchesModels.addAll(Stream.of(tags.getItems()) .map(branchesModel -> { branchesModel.setTag(true); @@ -112,12 +112,11 @@ class RepoCommitsPresenter extends BasePresenter implements })); manageDisposable(observable .doOnSubscribe(disposable -> sendToView(RepoCommitsMvp.View::showBranchesProgress)) - .doOnNext(branchesModels -> { + .subscribe(branchesModels -> { branches.clear(); branches.addAll(branchesModels); sendToView(view -> view.setBranchesData(branches, true)); - }) - .subscribe(branchesModels -> {/**/}, throwable -> sendToView(view -> view.setBranchesData(branches, true)))); + }, throwable -> sendToView(view -> view.setBranchesData(branches, true)))); } if (!InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { onCallApi(1, null); @@ -166,6 +165,6 @@ class RepoCommitsPresenter extends BasePresenter implements if (response != null) { sendToView(view -> view.onShowCommitCount(response.getLast())); } - })); + }, Throwable::printStackTrace)); } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/CommitPagerPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/CommitPagerPresenter.java index 565d341f..b1dd13d7 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/CommitPagerPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/commit/details/CommitPagerPresenter.java @@ -49,29 +49,27 @@ class CommitPagerPresenter extends BasePresenter implements return; } else if (!InputHelper.isEmpty(sha) && !InputHelper.isEmpty(login) && !InputHelper.isEmpty(repoId)) { makeRestCall(RestProvider.getRepoService() - .getCommit(login, repoId, sha) - .flatMap(commit -> { - if (commit.getGitCommit() != null && commit.getGitCommit().getMessage() != null) { - MarkdownModel markdownModel = new MarkdownModel(); - markdownModel.setContext(login + "/" + repoId); - markdownModel.setText(commit.getGitCommit().getMessage()); - return RestProvider.getRepoService().convertReadmeToHtml(markdownModel) - .onErrorReturn(throwable -> null); - } - return Observable.just(commit); - }, (commit, u) -> { - if (!InputHelper.isEmpty(u) && u instanceof String) { - commit.getGitCommit().setMessage(u.toString()); - } - return commit; - }), - commit -> { - commitModel = commit; - commitModel.setRepoId(repoId); - commitModel.setLogin(login); - sendToView(CommitPagerMvp.View::onSetup); - manageObservable(commitModel.save(commitModel).toObservable()); - }); + .getCommit(login, repoId, sha) + .flatMap(commit -> { + if (commit.getGitCommit() != null && commit.getGitCommit().getMessage() != null) { + MarkdownModel markdownModel = new MarkdownModel(); + markdownModel.setContext(login + "/" + repoId); + markdownModel.setText(commit.getGitCommit().getMessage()); + return RestProvider.getRepoService().convertReadmeToHtml(markdownModel); + } + return Observable.just(commit); + }, (commit, u) -> { + if (!InputHelper.isEmpty(u) && u instanceof String) { + commit.getGitCommit().setMessage(u.toString()); + } + return commit; + }), commit -> { + commitModel = commit; + commitModel.setRepoId(repoId); + commitModel.setLogin(login); + sendToView(CommitPagerMvp.View::onSetup); + manageObservable(commitModel.save(commitModel).toObservable()); + }); return; } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/files/RepoFilesPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/files/RepoFilesPresenter.java index b27e6770..34c3505c 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/files/RepoFilesPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/files/RepoFilesPresenter.java @@ -59,10 +59,9 @@ class RepoFilesPresenter extends BasePresenter implements Rep }) .toList() .subscribe(models -> { - files.addAll(models); - sendToView(RepoFilesMvp.View::onNotifyAdapter); - } - )); + files.addAll(models); + sendToView(RepoFilesMvp.View::onNotifyAdapter); + })); } @Override public void onCallApi(@Nullable RepoFile toAppend) { diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/prettifier/ViewerPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/prettifier/ViewerPresenter.java index ae896f41..7b0e7e06 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/prettifier/ViewerPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/prettifier/ViewerPresenter.java @@ -103,45 +103,44 @@ class ViewerPresenter extends BasePresenter implements ViewerMvp ? RestProvider.getRepoService(true).getFileAsHtmlStream(url) : RestProvider.getRepoService(true).getFileAsStream(url); makeRestCall(isRepo ? RestProvider.getRepoService(true).getReadmeHtml(url) - : streamObservable, - content -> { - downloadedStream = content; - ViewerFile fileModel = new ViewerFile(); - fileModel.setContent(downloadedStream); - fileModel.setFullUrl(url); - fileModel.setRepo(isRepo); - if (isRepo) { - fileModel.setMarkdown(true); - isMarkdown = true; - isRepo = true; - sendToView(view -> view.onSetMdText(downloadedStream, url)); + : streamObservable, content -> { + downloadedStream = content; + ViewerFile fileModel = new ViewerFile(); + fileModel.setContent(downloadedStream); + fileModel.setFullUrl(url); + fileModel.setRepo(isRepo); + if (isRepo) { + fileModel.setMarkdown(true); + isMarkdown = true; + isRepo = true; + sendToView(view -> view.onSetMdText(downloadedStream, url)); + } else { + isMarkdown = MarkDownProvider.isMarkdown(url); + if (isMarkdown) { + MarkdownModel model = new MarkdownModel(); + model.setText(downloadedStream); + NameParser parser = new NameParser(url); + if (parser.getUsername() != null && parser.getName() != null) { + model.setContext(parser.getUsername() + "/" + parser.getName()); } else { - isMarkdown = MarkDownProvider.isMarkdown(url); - if (isMarkdown) { - MarkdownModel model = new MarkdownModel(); - model.setText(downloadedStream); - NameParser parser = new NameParser(url); - if (parser.getUsername() != null && parser.getName() != null) { - model.setContext(parser.getUsername() + "/" + parser.getName()); - } else { - model.setContext(""); - } - Logger.e(model.getContext()); - makeRestCall(RestProvider.getRepoService().convertReadmeToHtml(model), string -> { - isMarkdown = true; - downloadedStream = string; - fileModel.setMarkdown(true); - fileModel.setContent(downloadedStream); - manageObservable(fileModel.save(fileModel).toObservable()); - sendToView(view -> view.onSetMdText(downloadedStream, url)); - }); - return; - } - fileModel.setMarkdown(false); - sendToView(view -> view.onSetCode(downloadedStream)); + model.setContext(""); } - manageObservable(fileModel.save(fileModel).toObservable()); - }); + Logger.e(model.getContext()); + makeRestCall(RestProvider.getRepoService().convertReadmeToHtml(model), string -> { + isMarkdown = true; + downloadedStream = string; + fileModel.setMarkdown(true); + fileModel.setContent(downloadedStream); + manageObservable(fileModel.save(fileModel).toObservable()); + sendToView(view -> view.onSetMdText(downloadedStream, url)); + }); + return; + } + fileModel.setMarkdown(false); + sendToView(view -> view.onSetCode(downloadedStream)); + } + manageObservable(fileModel.save(fileModel).toObservable()); + }); } @Override public boolean isRepo() { diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/code/releases/RepoReleasesPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/repos/code/releases/RepoReleasesPresenter.java index 0dd0af81..63378544 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/code/releases/RepoReleasesPresenter.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/code/releases/RepoReleasesPresenter.java @@ -71,14 +71,6 @@ class RepoReleasesPresenter extends BasePresenter implemen } - private void onResponse(Pageable response) { - lastPage = response.getLast(); - if (getCurrentPage() == 1) { - manageObservable(Release.save(response.getItems(), repoId, login)); - } - sendToView(view -> view.onNotifyAdapter(response.getItems(), getCurrentPage())); - } - @Override public void onFragmentCreated(@NonNull Bundle bundle) { repoId = bundle.getString(BundleConstant.ID); login = bundle.getString(BundleConstant.EXTRA); @@ -110,4 +102,12 @@ class RepoReleasesPresenter extends BasePresenter implemen } @Override public void onItemLongClick(int position, View v, Release item) {} + + private void onResponse(Pageable response) { + lastPage = response.getLast(); + if (getCurrentPage() == 1) { + manageObservable(Release.save(response.getItems(), repoId, login)); + } + sendToView(view -> view.onNotifyAdapter(response.getItems(), getCurrentPage())); + } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java index faa30889..a73d0441 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/repos/issues/issue/details/timeline/IssueTimelineFragment.java @@ -116,6 +116,7 @@ public class IssueTimelineFragment extends BaseFragment(), Tr } override fun onOptionsItemSelected(item: MenuItem?): Boolean { - if (item?.itemId == R.id.menu) { - drawerLayout.openDrawer(Gravity.END) - return true + when (item?.itemId) { + R.id.menu -> { + drawerLayout.openDrawer(Gravity.END) + return true + } + android.R.id.home -> { + startActivity(Intent(this, MainActivity::class.java)) + finish() + return true + } + else -> return super.onOptionsItemSelected(item) } - return super.onOptionsItemSelected(item) } override fun onAppend(title: String) { diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt index f30709f3..908fa28c 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt @@ -25,13 +25,12 @@ class TrendingFragment : BaseFragment counts = new HashSet<>(); public static void startActivity(@NonNull Context context, @NonNull String login) { startActivity(context, login, false); @@ -130,6 +138,11 @@ public class UserPagerActivity extends BaseActivity= barHeight) { + setTranslationX(mHiddenTranslationX); + mHideOverride = true; + return; + } + + mHideOverride = false; + + float y = ratio * (barHeight - calculatedHandleHeight); + + mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight); + } + private void updateHandleColorsAndInset() { StateListDrawable drawable = new StateListDrawable(); @@ -202,8 +234,7 @@ public class RecyclerFastScroller extends FrameLayout { public void attachRecyclerView(RecyclerView recyclerView) { mRecyclerView = recyclerView; mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); RecyclerFastScroller.this.show(); } @@ -217,9 +248,6 @@ public class RecyclerFastScroller extends FrameLayout { } } - /** - * Show the fast scroller and hide after delay - */ public void show() { requestLayout(); @@ -262,48 +290,22 @@ public class RecyclerFastScroller extends FrameLayout { } } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (mRecyclerView == null) return; - - int scrollOffset = mRecyclerView.computeVerticalScrollOffset(); - int verticalScrollRange = mRecyclerView.computeVerticalScrollRange() + mRecyclerView.getPaddingBottom(); - - int barHeight = mBar.getHeight(); - float ratio = (float) scrollOffset / (verticalScrollRange - barHeight); - - int calculatedHandleHeight = (int) ((float) barHeight / verticalScrollRange * barHeight); - if (calculatedHandleHeight < mMinScrollHandleHeight) { - calculatedHandleHeight = mMinScrollHandleHeight; - } - - if (calculatedHandleHeight >= barHeight) { - setTranslationX(mHiddenTranslationX); - mHideOverride = true; - return; - } - - mHideOverride = false; - - float y = ratio * (barHeight - calculatedHandleHeight); - - mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight); - } - - private static boolean isRTL(Context context) { - return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; - } - - @ColorInt - public static int resolveColor(Context context, @AttrRes int color) { + @ColorInt public static int resolveColor(Context context, @AttrRes int color) { TypedArray a = context.obtainStyledAttributes(new int[]{color}); int resId = a.getColor(0, 0); a.recycle(); return resId; } + private static boolean isRTL(Context context) { + return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + } + public static int convertDpToPx(Context context, float dp) { return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f); } + + public void setOnLoadMore(OnLoadMore onLoadMore) { + this.onLoadMore = onLoadMore; + } } \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/feeds_row_no_image_item.xml b/app/src/main/res/layouts/row_layouts/layout/feeds_row_no_image_item.xml index 82eb04bb..3c0234d0 100644 --- a/app/src/main/res/layouts/row_layouts/layout/feeds_row_no_image_item.xml +++ b/app/src/main/res/layouts/row_layouts/layout/feeds_row_no_image_item.xml @@ -28,13 +28,27 @@ android:transitionName="@string/title_transition" tools:text="When one acquires music and afterlife, one is able to capture heaven."/> + + + \ No newline at end of file From 6e520e5f137b4811323517927e35a703b614c063 Mon Sep 17 00:00:00 2001 From: Kosh Date: Sun, 4 Jun 2017 15:22:44 +0800 Subject: [PATCH 06/14] this commit fixes #542 --- app/build.gradle | 4 +- app/proguard-rules.pro | 9 +- .../data/dao/FragmentPagerAdapterModel.java | 2 +- .../modules/about/FastHubAboutActivity.java | 169 +++++++++++------- .../ui/modules/settings/SettingsActivity.java | 26 ++- .../ui/modules/settings/SettingsMvp.java | 13 -- .../modules/settings/SettingsPresenter.java | 9 - .../category/SettingsCategoryActivity.java | 7 +- .../category/SettingsCategoryFragment.java | 58 ++---- .../GitHubContributionsView.java | 82 ++++----- app/src/main/res/header-.svg | 1 - .../layout-v26/login_form_layout.xml | 168 ----------------- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/fasthub_settings.xml | 37 +--- 14 files changed, 189 insertions(+), 397 deletions(-) delete mode 100644 app/src/main/java/com/fastaccess/ui/modules/settings/SettingsMvp.java delete mode 100644 app/src/main/java/com/fastaccess/ui/modules/settings/SettingsPresenter.java delete mode 100644 app/src/main/res/header-.svg delete mode 100644 app/src/main/res/layouts/main_layouts/layout-v26/login_form_layout.xml diff --git a/app/build.gradle b/app/build.gradle index 43601ee7..c6ff87d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,7 +49,7 @@ android { buildTypes { release { minifyEnabled true - shrinkResources true + shrinkResources false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -105,8 +105,6 @@ repositories { mavenCentral() } - - dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile "com.android.support:appcompat-v7:${supportVersion}" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index eda99f73..ece94ad6 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -72,7 +72,8 @@ -keepclasseswithmembernames class * { @icepick.* ; } --keep class com.fastaccess.data.dao.** { *; } +-keep class com.fastaccess.data.** { *; } +-keep class com.fastaccess.provider.rest.** { *; } -keepclassmembers class com.prettifier.pretty.callback.MarkDownInterceptorInterface { public *; } @@ -101,11 +102,13 @@ ; } --keep class net.nightwhistler.** {*;} --keep class org.htmlcleaner.** {*;} +-keep class net.nightwhistler.** { *; } +-keep class org.htmlcleaner.** { *; } -dontwarn org.jaxen.** -dontwarn org.jdom.** -dontwarn com.google.android.gms.** -dontwarn android.animation.** -dontwarn java.io.** -dontwarn android.content.** +-keeppackagenames org.jsoup.nodes +-keep class com.github.florent37.** { *; } diff --git a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java index 27392c78..fd0d1e5c 100644 --- a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java +++ b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java @@ -74,7 +74,7 @@ import lombok.Setter; @NonNull public static List buildForProfile(@NonNull Context context, @NonNull String login) { return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.overview), ProfileOverviewFragment.newInstance(login)), - new FragmentPagerAdapterModel(context.getString(R.string.feeds), ProfileEventsFragment.Companion.newInstance(login)), + new FragmentPagerAdapterModel(context.getString(R.string.feed), ProfileEventsFragment.Companion.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.repos), ProfileReposFragment.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.starred), ProfileStarredFragment.newInstance(login)), new FragmentPagerAdapterModel(context.getString(R.string.gists), ProfileGistsFragment.newInstance(login)), diff --git a/app/src/main/java/com/fastaccess/ui/modules/about/FastHubAboutActivity.java b/app/src/main/java/com/fastaccess/ui/modules/about/FastHubAboutActivity.java index 6b797d1a..cb7efcff 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/about/FastHubAboutActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/about/FastHubAboutActivity.java @@ -2,7 +2,6 @@ package com.fastaccess.ui.modules.about; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; @@ -15,11 +14,14 @@ import com.danielstone.materialaboutlibrary.MaterialAboutActivity; import com.danielstone.materialaboutlibrary.items.MaterialAboutActionItem; import com.danielstone.materialaboutlibrary.model.MaterialAboutCard; import com.danielstone.materialaboutlibrary.model.MaterialAboutList; +import com.fastaccess.BuildConfig; import com.fastaccess.R; +import com.fastaccess.data.dao.model.Release; import com.fastaccess.helper.ActivityHelper; import com.fastaccess.helper.AppHelper; import com.fastaccess.helper.BundleConstant; import com.fastaccess.helper.PrefGetter; +import com.fastaccess.ui.modules.changelog.ChangelogBottomSheetDialog; import com.fastaccess.ui.modules.repos.RepoPagerActivity; import com.fastaccess.ui.modules.repos.issues.create.CreateIssueActivity; import com.fastaccess.ui.modules.user.UserPagerActivity; @@ -27,13 +29,16 @@ import com.mikepenz.aboutlibraries.Libs; import com.mikepenz.aboutlibraries.LibsBuilder; import es.dmoral.toasty.Toasty; +import io.reactivex.disposables.Disposable; /** * Created by danielstone on 12 Mar 2017, 1:57 AM */ public class FastHubAboutActivity extends MaterialAboutActivity { - View malRecyclerview; + private View malRecyclerview; + private Disposable disposable; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { int themeMode = PrefGetter.getThemeType(getApplicationContext()); @@ -48,70 +53,14 @@ public class FastHubAboutActivity extends MaterialAboutActivity { @Override protected MaterialAboutList getMaterialAboutList(Context context) { MaterialAboutCard.Builder appCardBuilder = new MaterialAboutCard.Builder(); - try { - appCardBuilder.addItem(ConvenienceBuilder.createVersionActionItem(context, ContextCompat.getDrawable(context, R.drawable.ic_issues), - getString(R.string.version), false)); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - - appCardBuilder.addItem(ConvenienceBuilder.createRateActionItem(context, ContextCompat.getDrawable(context, R.drawable.ic_star_filled), - getString(R.string.rate_app), null)); - - appCardBuilder.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.report_issue) - .subText(R.string.report_issue_here) - .icon(ContextCompat.getDrawable(context, R.drawable.ic_bug)) - .setOnClickListener(b -> CreateIssueActivity.startForResult(this, CreateIssueActivity.startForResult(this), malRecyclerview)) - .build()); - appCardBuilder.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.open_source_libs) - .icon(R.drawable.ic_github) - .setOnClickListener(b -> new LibsBuilder() - .withActivityStyle(AppHelper.isNightMode(getResources()) ? Libs.ActivityStyle.DARK : Libs.ActivityStyle.LIGHT) - .withAutoDetect(true) - .withAboutIconShown(true) - .withAboutVersionShown(true) - .start(this)) - .build()); - + buildApp(context, appCardBuilder); + MaterialAboutCard.Builder miscCardBuilder = new MaterialAboutCard.Builder(); + buildMisc(context, miscCardBuilder); MaterialAboutCard.Builder authorCardBuilder = new MaterialAboutCard.Builder(); - authorCardBuilder.title(R.string.author); - authorCardBuilder.addItem(new MaterialAboutActionItem.Builder() - .text("Kosh") - .subText("k0shk0sh") - .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) - .setOnClickListener(b -> UserPagerActivity.startActivity(context, "k0shk0sh")) - .build()); - authorCardBuilder.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.fork_github) - .icon(ContextCompat.getDrawable(context, R.drawable.ic_github)) - .setOnClickListener(b -> startActivity(RepoPagerActivity.createIntent(this, "FastHub", "k0shk0sh"))) - .build()); - - authorCardBuilder.addItem(ConvenienceBuilder.createEmailItem(context, ContextCompat.getDrawable(context, R.drawable.ic_email), - getString(R.string.send_email), true, getString(R.string.email_address), getString(R.string.question_concerning_fasthub))); - + buildAuthor(context, authorCardBuilder); MaterialAboutCard.Builder logoAuthor = new MaterialAboutCard.Builder(); - logoAuthor.title(getString(R.string.logo_designer, "Kevin Aguilar")); - logoAuthor.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.google_plus) - .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) - .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://plus.google.com/+KevinAguilarC")) - .build()); - logoAuthor.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.twitter) - .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) - .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://twitter.com/kevttob")) - .build()); - logoAuthor.addItem(new MaterialAboutActionItem.Builder() - .text(R.string.website) - .icon(ContextCompat.getDrawable(context, R.drawable.ic_brower)) - .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://www.221pixels.com/")) - .build()); - - - return new MaterialAboutList(appCardBuilder.build(), authorCardBuilder.build(), logoAuthor.build()); + buildLogo(context, logoAuthor); + return new MaterialAboutList(appCardBuilder.build(), miscCardBuilder.build(), authorCardBuilder.build(), logoAuthor.build()); } @Override protected CharSequence getActivityTitle() { @@ -131,4 +80,96 @@ public class FastHubAboutActivity extends MaterialAboutActivity { } return false;//override } + + @Override protected void onDestroy() { + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + } + super.onDestroy(); + } + + private void buildLogo(Context context, MaterialAboutCard.Builder logoAuthor) { + logoAuthor.title(getString(R.string.logo_designer, "Kevin Aguilar")); + logoAuthor.addItem(new MaterialAboutActionItem.Builder() + .text(R.string.google_plus) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) + .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://plus.google.com/+KevinAguilarC")) + .build()) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.twitter) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) + .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://twitter.com/kevttob")) + .build()) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.website) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_brower)) + .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "https://www.221pixels.com/")) + .build()); + } + + private void buildAuthor(Context context, MaterialAboutCard.Builder authorCardBuilder) { + authorCardBuilder.title(R.string.author); + authorCardBuilder.addItem(new MaterialAboutActionItem.Builder() + .text("Kosh Sergani") + .subText("k0shk0sh") + .icon(ContextCompat.getDrawable(context, R.drawable.ic_profile)) + .setOnClickListener(b -> UserPagerActivity.startActivity(context, "k0shk0sh")) + .build()) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.fork_github) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_github)) + .setOnClickListener(b -> startActivity(RepoPagerActivity.createIntent(this, "FastHub", "k0shk0sh"))) + .build()) + .addItem(ConvenienceBuilder.createEmailItem(context, ContextCompat.getDrawable(context, R.drawable.ic_email), + getString(R.string.send_email), true, getString(R.string.email_address), getString(R.string.question_concerning_fasthub))); + } + + private void buildMisc(Context context, MaterialAboutCard.Builder miscCardBuilder) { + miscCardBuilder.title(R.string.about) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.changelog) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_track_changes)) + .setOnClickListener(b -> new ChangelogBottomSheetDialog().show(getSupportFragmentManager(), "ChangelogBottomSheetDialog")) + .build()) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.join_slack) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_slack)) + .setOnClickListener(b -> ActivityHelper.startCustomTab(this, "http://rebrand.ly/fasthub")) + .build()) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.open_source_libs) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_github)) + .setOnClickListener(b -> new LibsBuilder() + .withActivityStyle(AppHelper.isNightMode(getResources()) ? Libs.ActivityStyle.DARK : Libs.ActivityStyle.LIGHT) + .withAutoDetect(true) + .withAboutIconShown(true) + .withAboutVersionShown(true) + .start(this)) + .build()); + } + + private void buildApp(Context context, MaterialAboutCard.Builder appCardBuilder) { + appCardBuilder.addItem(new MaterialAboutActionItem.Builder() + .text(getString(R.string.version)) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_update)) + .subText(BuildConfig.VERSION_NAME) + .setOnClickListener(b -> disposable = Release.get("FastHub", "k0shk0sh") + .subscribe(releases -> { + if (releases != null && !releases.isEmpty()) { + if (releases.get(0).getTagName().contains(BuildConfig.VERSION_NAME)) + Toasty.success(context, getString(R.string.up_to_date)).show(); + else + Toasty.warning(context, getString(R.string.new_version)).show(); + } + })) + .build()) + .addItem(ConvenienceBuilder.createRateActionItem(context, ContextCompat.getDrawable(context, R.drawable.ic_star_filled), + getString(R.string.rate_app), null)) + .addItem(new MaterialAboutActionItem.Builder() + .text(R.string.report_issue) + .subText(R.string.report_issue_here) + .icon(ContextCompat.getDrawable(context, R.drawable.ic_bug)) + .setOnClickListener(b -> CreateIssueActivity.startForResult(this, CreateIssueActivity.startForResult(this), malRecyclerview)) + .build()); + } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsActivity.java b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsActivity.java index abf0ac87..63d58b1b 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsActivity.java @@ -11,6 +11,7 @@ import com.fastaccess.data.dao.SettingsModel; import com.fastaccess.helper.ActivityHelper; import com.fastaccess.ui.adapter.SettingsAdapter; import com.fastaccess.ui.base.BaseActivity; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; import com.fastaccess.ui.modules.settings.category.SettingsCategoryActivity; import net.grandcentrix.thirtyinch.TiPresenter; @@ -33,7 +34,7 @@ public class SettingsActivity extends BaseActivity { case 1: ActivityHelper.startReveal(this, intent, settingsList, THEME_CHANGE); break; - case 5: + case 4: showLanguageList(); break; default: @@ -67,29 +68,24 @@ public class SettingsActivity extends BaseActivity { SettingsModel.newInstance(R.drawable.ic_ring, getString(R.string.notifications), ""), SettingsModel.newInstance(R.drawable.ic_settings, getString(R.string.behavior), ""), SettingsModel.newInstance(R.drawable.ic_brush, getString(R.string.customization), ""), - SettingsModel.newInstance(R.drawable.ic_info, getString(R.string.about), ""), SettingsModel.newInstance(R.drawable.ic_backup, getString(R.string.backup), ""), SettingsModel.newInstance(R.drawable.ic_language, getString(R.string.app_language), "") }; - settingsList.setAdapter(new SettingsAdapter(this, settings)); } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == THEME_CHANGE) + setResult(resultCode); + } + + @NonNull @Override public TiPresenter providePresenter() { + return new BasePresenter(); + } + private void showLanguageList() { LanguageBottomSheetDialog languageBottomSheetDialog = new LanguageBottomSheetDialog(); languageBottomSheetDialog.onAttach((Context) this); languageBottomSheetDialog.show(getSupportFragmentManager(), "LanguageBottomSheetDialog"); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == THEME_CHANGE) - setResult(resultCode); - } - - @NonNull - @Override - public TiPresenter providePresenter() { - return new SettingsPresenter(); - } } diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsMvp.java b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsMvp.java deleted file mode 100644 index 13d026d2..00000000 --- a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsMvp.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.fastaccess.ui.modules.settings; - -import com.fastaccess.ui.base.mvp.BaseMvp; - -/** - * Created by JediB on 5/12/2017. - */ - -public interface SettingsMvp { - interface Presenter extends BaseMvp.FAPresenter { - - } -} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsPresenter.java deleted file mode 100644 index ef93cb28..00000000 --- a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsPresenter.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.fastaccess.ui.modules.settings; - -import com.fastaccess.ui.base.mvp.presenter.BasePresenter; - -/** - * Created by JediB on 5/12/2017. - */ - -class SettingsPresenter extends BasePresenter implements SettingsMvp.Presenter {} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryActivity.java b/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryActivity.java index 55e14ad5..62974160 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryActivity.java +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryActivity.java @@ -3,13 +3,12 @@ package com.fastaccess.ui.modules.settings.category; import android.os.Bundle; import android.support.annotation.NonNull; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.ui.base.BaseActivity; import net.grandcentrix.thirtyinch.TiPresenter; -import com.evernote.android.state.State; - public class SettingsCategoryActivity extends BaseActivity { @State String title; @@ -35,7 +34,9 @@ public class SettingsCategoryActivity extends BaseActivity { setResult(RESULT_CANCELED); if (savedInstanceState == null) { title = getIntent() != null ? getIntent().getStringExtra("title") : getString(R.string.settings); - SettingsCategoryFragment settingsCategoryFragment = new SettingsCategoryFragment(); + + SettingsCategoryFragment settingsCategoryFragment = SettingsCategoryFragment. + newInstance(getIntent().getIntExtra("settings", 0)); getSupportFragmentManager() .beginTransaction() .replace(R.id.settingsContainer, settingsCategoryFragment) diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryFragment.java b/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryFragment.java index 8f5e4bfb..c66bf6d9 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/category/SettingsCategoryFragment.java @@ -14,17 +14,14 @@ import android.util.Log; import android.widget.FrameLayout; import android.widget.Toast; -import com.fastaccess.BuildConfig; import com.fastaccess.R; -import com.fastaccess.data.dao.model.Release; -import com.fastaccess.helper.ActivityHelper; +import com.fastaccess.helper.BundleConstant; +import com.fastaccess.helper.Bundler; import com.fastaccess.helper.InputHelper; import com.fastaccess.helper.PrefGetter; import com.fastaccess.helper.PrefHelper; import com.fastaccess.provider.tasks.notification.NotificationSchedulerJobTask; import com.fastaccess.ui.base.mvp.BaseMvp; -import com.fastaccess.ui.modules.changelog.ChangelogBottomSheetDialog; -import com.fastaccess.ui.widgets.SpannableBuilder; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -44,7 +41,6 @@ import java.util.Set; import butterknife.BindView; import es.dmoral.toasty.Toasty; -import io.reactivex.disposables.Disposable; import static android.app.Activity.RESULT_OK; @@ -64,7 +60,14 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement private Preference notificationTime; private Preference notificationRead; private Preference notificationSound; - private Disposable disposable; + + @NonNull public static SettingsCategoryFragment newInstance(int settings) { + SettingsCategoryFragment fragment = new SettingsCategoryFragment(); + fragment.setArguments(Bundler.start() + .put(BundleConstant.EXTRA, settings) + .end()); + return fragment; + } @Override public void onAttach(Context context) { super.onAttach(context); @@ -75,7 +78,7 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - int settings = getActivity().getIntent().getExtras().getInt("settings", 0); + int settings = getArguments().getInt("settings", 0); switch (settings) { case 0: addPreferencesFromResource(R.xml.notification_settings); @@ -106,37 +109,8 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement findPreference("appColor").setOnPreferenceChangeListener(this); break; case 3: - addPreferencesFromResource(R.xml.about_settings); - findPreference("showChangelog").setOnPreferenceClickListener(preference -> { - new ChangelogBottomSheetDialog().show(getChildFragmentManager(), "ChangelogBottomSheetDialog"); - return true; - }); - findPreference("joinSlack").setOnPreferenceClickListener(preference -> { - ActivityHelper.startCustomTab(getActivity(), "http://rebrand.ly/fasthub"); - return true; - }); - findPreference("currentVersion").setSummary(SpannableBuilder.builder() - .append(getString(R.string.current_version)) - .append("(") - .bold(BuildConfig.VERSION_NAME) - .append(")")); - findPreference("currentVersion").setOnPreferenceClickListener(preference -> { - disposable = Release.get("FastHub", "k0shk0sh").subscribe(releases -> { - if (releases != null) { - if (releases.get(0).getTagName().equals(BuildConfig.VERSION_NAME)) - Toasty.success(getContext(), getString(R.string.up_to_date)).show(); - else - Toasty.warning(getContext(), getString(R.string.new_version)).show(); - } - }); - return true; - }); - break; - case 4: addPreferencesFromResource(R.xml.backup_settings); findPreference("backup").setOnPreferenceClickListener((Preference preference) -> { - - if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { Map settings_ = PrefHelper.getAll(); @@ -162,7 +136,7 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement Log.e(getTag(), "Couldn't backup: " + e.toString()); } - PrefHelper.set("backed_up", new SimpleDateFormat("MM/dd").format(new Date())); + PrefHelper.set("backed_up", new SimpleDateFormat("MM/dd", Locale.ENGLISH).format(new Date())); } else { requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE); } @@ -244,7 +218,6 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == PERMISSION_REQUEST_CODE) { if (permissions[0].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { @@ -323,13 +296,6 @@ public class SettingsCategoryFragment extends PreferenceFragmentCompat implement } } - @Override public void onDestroyView() { - super.onDestroyView(); - if (disposable != null && !disposable.isDisposed()) { - disposable.dispose(); - } - } - private void showFileChooser() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("application/json"); diff --git a/app/src/main/java/com/fastaccess/ui/widgets/contributions/GitHubContributionsView.java b/app/src/main/java/com/fastaccess/ui/widgets/contributions/GitHubContributionsView.java index 6f2fc75c..9b4ac98e 100644 --- a/app/src/main/java/com/fastaccess/ui/widgets/contributions/GitHubContributionsView.java +++ b/app/src/main/java/com/fastaccess/ui/widgets/contributions/GitHubContributionsView.java @@ -302,52 +302,54 @@ public class GitHubContributionsView extends View { } } + Bitmap bitmap = null; + private Bitmap drawOnCanvas(Canvas canvas) { - canvas.getClipBounds(rect); - int width = rect.width(); - int verticalBlockNumber = 7; - int horizontalBlockNumber = getHorizontalBlockNumber(contributionsFilter.size(), verticalBlockNumber); - float marginBlock = (1.0F - 0.1F); - float blockWidth = width / (float) horizontalBlockNumber * marginBlock; - float spaceWidth = width / (float) horizontalBlockNumber - blockWidth; - float topMargin = (displayMonth) ? 7f : 0; - float monthTextHeight = (displayMonth) ? blockWidth * 1.5F : 0; - int height = (int) ((blockWidth + spaceWidth) * 7 + topMargin + monthTextHeight); - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas1 = new Canvas(bitmap); - // Background - blockPaint.setColor(backgroundBaseColor); - canvas1.drawRect(0, (topMargin + monthTextHeight), width, height + monthTextHeight, blockPaint); - monthTextPaint.setColor(textColor); - monthTextPaint.setTextSize(monthTextHeight); - // draw the blocks - int currentWeekDay = DatesUtils.getWeekDayFromDate( - contributions.get(0).year, - contributions.get(0).month, - contributions.get(0).day); - float x = 0; - float y = (currentWeekDay - 7) % 7 * (blockWidth + spaceWidth) + (topMargin + monthTextHeight); - for (ContributionsDay day : contributionsFilter) { - blockPaint.setColor(ColorsUtils.calculateLevelColor(baseColor, baseEmptyColor, day.level)); - canvas1.drawRect(x, y, x + blockWidth, y + blockWidth, blockPaint); + if (bitmap == null) { + canvas.getClipBounds(rect); + int width = rect.width(); + int verticalBlockNumber = 7; + int horizontalBlockNumber = getHorizontalBlockNumber(contributionsFilter.size(), verticalBlockNumber); + float marginBlock = (1.0F - 0.1F); + float blockWidth = width / (float) horizontalBlockNumber * marginBlock; + float spaceWidth = width / (float) horizontalBlockNumber - blockWidth; + float topMargin = (displayMonth) ? 7f : 0; + float monthTextHeight = (displayMonth) ? blockWidth * 1.5F : 0; + int height = (int) ((blockWidth + spaceWidth) * 7 + topMargin + monthTextHeight); + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas1 = new Canvas(bitmap); + // Background + blockPaint.setColor(backgroundBaseColor); + canvas1.drawRect(0, (topMargin + monthTextHeight), width, height + monthTextHeight, blockPaint); + monthTextPaint.setColor(textColor); + monthTextPaint.setTextSize(monthTextHeight); + // draw the blocks + int currentWeekDay = DatesUtils.getWeekDayFromDate( + contributions.get(0).year, + contributions.get(0).month, + contributions.get(0).day); + float x = 0; + float y = (currentWeekDay - 7) % 7 * (blockWidth + spaceWidth) + (topMargin + monthTextHeight); + for (ContributionsDay day : contributionsFilter) { + blockPaint.setColor(ColorsUtils.calculateLevelColor(baseColor, baseEmptyColor, day.level)); + canvas1.drawRect(x, y, x + blockWidth, y + blockWidth, blockPaint); + if (DatesUtils.isFirstDayOfWeek(day.year, day.month, day.day + 1)) { + // another column + x += blockWidth + spaceWidth; + y = topMargin + monthTextHeight; - if (DatesUtils.isFirstDayOfWeek(day.year, day.month, day.day + 1)) { - // another column - x += blockWidth + spaceWidth; - y = topMargin + monthTextHeight; + if (DatesUtils.isFirstWeekOfMount(day.year, day.month, day.day + 1)) { + canvas1.drawText( + DatesUtils.getShortMonthName(day.year, day.month, day.day + 1), + x, monthTextHeight, monthTextPaint); + } - if (DatesUtils.isFirstWeekOfMount(day.year, day.month, day.day + 1)) { - canvas1.drawText( - DatesUtils.getShortMonthName(day.year, day.month, day.day + 1), - x, monthTextHeight, monthTextPaint); + } else { + y += blockWidth + spaceWidth; } - - } else { - y += blockWidth + spaceWidth; } + adjustHeight(height); } - - adjustHeight(height); return bitmap; } diff --git a/app/src/main/res/header-.svg b/app/src/main/res/header-.svg deleted file mode 100644 index 6c36b533..00000000 --- a/app/src/main/res/header-.svg +++ /dev/null @@ -1 +0,0 @@ -header- \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout-v26/login_form_layout.xml b/app/src/main/res/layouts/main_layouts/layout-v26/login_form_layout.xml deleted file mode 100644 index bb724de0..00000000 --- a/app/src/main/res/layouts/main_layouts/layout-v26/login_form_layout.xml +++ /dev/null @@ -1,168 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 631892e6..ecc0cada 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -478,4 +478,5 @@ Assignee added successfully Reviewer added successfully Milestone added successfully + Feed diff --git a/app/src/main/res/xml/fasthub_settings.xml b/app/src/main/res/xml/fasthub_settings.xml index f88d3b88..6a86d3fe 100644 --- a/app/src/main/res/xml/fasthub_settings.xml +++ b/app/src/main/res/xml/fasthub_settings.xml @@ -1,7 +1,7 @@ + > @@ -10,22 +10,22 @@ android:dialogTitle="@string/every" android:entries="@array/notification_duration" android:entryValues="@array/notification_duration" - android:key="notificationTime" android:icon="@drawable/ic_time" + android:key="notificationTime" android:summary="@string/background_job_summary" android:title="@string/background_job_title"/> @@ -43,8 +43,8 @@ android:dialogTitle="@string/theme_title" android:entries="@array/theme_modes" android:entryValues="@array/theme_modes" - android:key="appTheme" android:icon="@drawable/ic_brush" + android:key="appTheme" android:summary="@string/theme_summary" android:title="@string/theme_title"/> @@ -53,8 +53,8 @@ android:dialogTitle="@string/theme_color_title" android:entries="@array/theme_colors" android:entryValues="@array/theme_colors" - android:key="appColor" android:icon="@drawable/ic_blank" + android:key="appColor" android:summary="@string/theme_color_summary" android:title="@string/theme_color_title"/> @@ -107,29 +107,4 @@ - - - - - - - - - - - - - - \ No newline at end of file From b27e6abbd583ac0742b73398ff286a37f3644fc0 Mon Sep 17 00:00:00 2001 From: Kosh Date: Sun, 4 Jun 2017 16:44:50 +0800 Subject: [PATCH 07/14] this commit fixes #598 --- .../ui/modules/feeds/FeedsFragment.java | 44 +-------------- .../ui/modules/trending/TrendingActivity.kt | 9 ++-- .../ui/modules/trending/TrendingMvp.kt | 3 +- .../pretty/helper/PrettifyHelper.java | 53 ++++++++++--------- 4 files changed, 36 insertions(+), 73 deletions(-) diff --git a/app/src/main/java/com/fastaccess/ui/modules/feeds/FeedsFragment.java b/app/src/main/java/com/fastaccess/ui/modules/feeds/FeedsFragment.java index f494fbb3..cebf29c4 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/feeds/FeedsFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/feeds/FeedsFragment.java @@ -11,7 +11,6 @@ import android.view.View; import com.fastaccess.R; import com.fastaccess.data.dao.SimpleUrlsModel; import com.fastaccess.data.dao.model.Event; -import com.fastaccess.helper.ActivityHelper; import com.fastaccess.helper.PrefGetter; import com.fastaccess.provider.rest.loadmore.OnLoadMore; import com.fastaccess.provider.scheme.SchemeParser; @@ -82,8 +81,7 @@ public class FeedsFragment extends BaseFragment i } @Override public void showProgress(@StringRes int resId) { - -refresh.setRefreshing(true); + refresh.setRefreshing(true); stateLayout.showProgress(); } @@ -133,45 +131,7 @@ refresh.setRefreshing(true); } @Override public void onShowGuide(@NonNull View itemView, @NonNull Event model) { - if (!PrefGetter.isUserIconGuideShowed()) { - final boolean[] dismissed = {false}; - /*new MaterialTapTargetPrompt.Builder(getActivity()) - .setTarget(itemView.findViewById(R.id.avatarLayout)) - .setPrimaryText(R.string.users) - .setSecondaryText(R.string.avatar_click_hint) - .setBackgroundColourAlpha(244) - .setBackgroundColour(ViewHelper.getAccentColor(getContext())) - .setOnHidePromptListener(new MaterialTapTargetPrompt.OnHidePromptListener() { - @Override public void onHidePrompt(MotionEvent event, boolean tappedTarget) {} - - @Override public void onHidePromptComplete() { - if (!dismissed[0]) - new MaterialTapTargetPrompt.Builder(getActivity()) - .setTarget(itemView) - .setPrimaryText(R.string.fork) - .setSecondaryText(R.string.feeds_fork_hint) - .setCaptureTouchEventOutsidePrompt(true) - .setBackgroundColourAlpha(244) - .setBackgroundColour(ViewHelper.getAccentColor(getContext())) - .setOnHidePromptListener(new MaterialTapTargetPrompt.OnHidePromptListener() { - @Override - public void onHidePrompt(MotionEvent motionEvent, boolean b) { - ActivityHelper.hideDismissHints(FeedsFragment.this.getContext()); - } - - @Override - public void onHidePromptComplete() { - - } - }) - .show(); - ActivityHelper.bringDismissAllToFront(getContext()); - } - }) - .setCaptureTouchEventOutsidePrompt(true) - .show();*/ - ActivityHelper.showDismissHints(getContext(), () -> dismissed[0] = true); - } + if (!PrefGetter.isUserIconGuideShowed()) {} } private void showReload() { diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt index 266500f4..e7e16369 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingActivity.kt @@ -93,7 +93,10 @@ class TrendingActivity : BaseActivity(), Tr daily.setOnClickListener { onDailyClicked() } weekly.setOnClickListener { onWeeklyClicked() } monthly.setOnClickListener { onMonthlyClicked() } - navMenu.setNavigationItemSelectedListener(this) + navMenu.setNavigationItemSelectedListener({ item -> + closeDrawerLayout() + onItemClicked(item) + }) setupIntent(savedInstanceState) presenter.onLoadLanguage() onSelectTrending() @@ -131,10 +134,6 @@ class TrendingActivity : BaseActivity(), Tr return true } - override fun onNavigationItemSelected(item: MenuItem): Boolean { - closeDrawerLayout() - return onItemClicked(item) - } private fun closeDrawerLayout() { drawerLayout.closeDrawer(Gravity.END) diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingMvp.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingMvp.kt index 44cccfcd..57614628 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingMvp.kt +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/TrendingMvp.kt @@ -1,6 +1,5 @@ package com.fastaccess.ui.modules.trending -import android.support.design.widget.NavigationView import com.fastaccess.ui.base.mvp.BaseMvp /** @@ -8,7 +7,7 @@ import com.fastaccess.ui.base.mvp.BaseMvp */ interface TrendingMvp { - interface View : BaseMvp.FAView, NavigationView.OnNavigationItemSelectedListener { + interface View : BaseMvp.FAView{ fun onAppend(title: String) } diff --git a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java index d61fdb97..ea303608 100755 --- a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java +++ b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java @@ -8,29 +8,30 @@ import android.support.annotation.NonNull; public class PrettifyHelper { - @NonNull private final static String HTML_CONTENT = - "\n" + - "\n" + - "\n" + - " \n" + - " \n" + - " %s\n" + - "\n" + - "\n" + - "
%s
\n" + - "\n" + - "" + - "\n" + - ""; + @NonNull private static String getHtmlContent(@NonNull String css, @NonNull String text, @NonNull String wrapStyle) { + return "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " " + wrapStyle + "\n" + + "\n" + + "\n" + + "
" + text + "
\n" + + "\n" + + "" + + "\n" + + ""; + } @NonNull private static final String WRAPPED_STYLE = " + + +

FastHub changelog +

+

Version 3.0.0 (Trending Finally!) +

+
+

+ P.S: I’m not asking for to much, if you are facing a problem in FastHub please report it at the issue tracker in GitHub either + via FastHub app or from GitHub website, your reviews are what keeps me motivated to further improve FastHub. +
+ Keep in mind that FastHub is free & open source. +
+

+
+

Bugs , Enhancements & new Features +

+
    +
  • (New) Explore trending with all the languages out there.
  • +
  • (New) Filter Repos (Thanks to @aadithyabk)
  • +
  • (New) See specific user feed.
  • +
  • (New) Display amount of starred repos.
  • +
  • (New) Copy Repo URL to clipboard
  • +
  • (New) Added participated issue sorting to personal issue list
  • +
  • (New) Showing multiple build statues.
  • +
  • (New) Lithuanian language (Thanks to @mistermantas)
  • +
  • (New) no new line at end of file now shows a picture
  • +
  • (Enhancement/Fix) Sort issues by reaction
  • +
  • (Fix) Issues/PRs editing, was semi-broken in preview release
  • +
  • (Fix) Showing missing repo (broken in previous version)
  • +
  • (Fix) Support development now works, + please use it. +
  • +
  • (Fix) Issue count not updating.
  • +
  • (Fix) Emoji’s were joining :crossed_swords:
  • +
  • (Fix) Back button when opening FastHub from an external link
  • +
  • (Fix) Issue comment were only showing up to 100 comments
  • +
  • (Fix) Dropdown menus not closing
  • +
  • (Fix) Some tablets had text wrapping issues
  • +
  • (Fix) Pictures not loading in PR’s/commits
  • +
  • (Fix) Fixed crash when adding a react
  • +
  • (Fix) Some tooltips were incorrect
  • +
  • (Fix) Stargazers are now updated in pinned repo’s
  • +
  • (Fix) Gist links (edited)
  • +
  • There are more stuff are not mentioned, find them out :p
  • +
  • Lots of bug fixes
  • +
+
+

Thanks to everyone who contributed either via reporting bugs or via code contribution

+
+

+ Thank you very much +

- + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 6ff29d37..bab39de9 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -124,7 +124,7 @@ @string/all - + Daily Weekly Monthly From 330466403eb86df2c87231111b5dd148c39dbab8 Mon Sep 17 00:00:00 2001 From: Kosh Date: Sun, 4 Jun 2017 22:07:21 +0800 Subject: [PATCH 09/14] this commit fixes #602 --- .../profile/overview/ProfileOverviewFragment.java | 3 ++- .../java/com/fastaccess/ui/widgets/AvatarLayout.java | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java index 04edacfa..eecda587 100644 --- a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java @@ -23,6 +23,7 @@ import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.RelativeLayout; +import com.evernote.android.state.State; import com.fastaccess.R; import com.fastaccess.data.dao.model.Login; import com.fastaccess.data.dao.model.User; @@ -52,7 +53,6 @@ import java.util.List; import butterknife.BindView; import butterknife.OnClick; import es.dmoral.toasty.Toasty; -import com.evernote.android.state.State; import static android.view.Gravity.TOP; import static android.view.View.GONE; @@ -178,6 +178,7 @@ public class ProfileOverviewFragment extends BaseFragment userInformation.callOnClick()); organization.setText(InputHelper.toNA(userModel.getCompany())); location.setText(InputHelper.toNA(userModel.getLocation())); email.setText(InputHelper.toNA(userModel.getEmail())); diff --git a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java index c9dd2463..1374aae5 100644 --- a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java +++ b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java @@ -37,8 +37,10 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener { @Nullable private String login; private boolean isOrg; private Toast toast; + private OnClickListener onClickListener; @OnClick(R.id.avatar) void onClick(@NonNull View view) { + if (onClickListener != null) onClickListener.onClick(view); if (InputHelper.isEmpty(login)) return; UserPagerActivity.startActivity(view.getContext(), login, isOrg); } @@ -72,6 +74,11 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener { super.onAttachedToWindow(); } + @Override protected void onDetachedFromWindow() { + onClickListener = null; + super.onDetachedFromWindow(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); inflate(getContext(), R.layout.avatar_layout, this); @@ -123,4 +130,8 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener { } } } + + public void setOnAvatarClick(OnClickListener onClickListener) { + this.onClickListener = onClickListener; + } } From 6745287400cda7b31dc9f8260fc6cb3b208b2673 Mon Sep 17 00:00:00 2001 From: Kosh Date: Sun, 4 Jun 2017 22:46:40 +0800 Subject: [PATCH 10/14] hotfix, no newline might be showing in anyplace that has `\` in 3.0.0 --- app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java b/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java index 5011658e..139ffc45 100644 --- a/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java +++ b/app/src/main/java/com/fastaccess/ui/widgets/DiffLineSpan.java @@ -82,7 +82,8 @@ public class DiffLineSpan extends MetricAffectingSpan implements LineBackgroundS color = patchDeletionColor; } else if (token.startsWith("@@")) { color = patchRefColor; - } else if (firstChar == '\\') { + } + if (token.startsWith("\\ No")) { token = token.replace("\\ No newline at end of file", ""); index = i; } From e96ece8d3c66bf54c39da7077e6e70e202851972 Mon Sep 17 00:00:00 2001 From: Kosh Date: Tue, 6 Jun 2017 20:25:39 +0800 Subject: [PATCH 11/14] this commit fixes #603 and fixes #606 and redid the trending rendering plus added language to trending as mentioned in #611 --- README.md | 2 +- app/build.gradle | 3 - app/src/main/AndroidManifest.xml | 2 +- .../main/assets/highlight/js/lang-Splus.js | 0 app/src/main/assets/highlight/js/lang-aea.js | 0 app/src/main/assets/highlight/js/lang-agc.js | 0 .../main/assets/highlight/js/lang-apollo.js | 0 .../main/assets/highlight/js/lang-basic.js | 0 app/src/main/assets/highlight/js/lang-cbm.js | 0 app/src/main/assets/highlight/js/lang-cl.js | 0 app/src/main/assets/highlight/js/lang-clj.js | 0 app/src/main/assets/highlight/js/lang-css.js | 0 app/src/main/assets/highlight/js/lang-dart.js | 0 app/src/main/assets/highlight/js/lang-el.js | 0 app/src/main/assets/highlight/js/lang-erl.js | 0 .../main/assets/highlight/js/lang-erlang.js | 0 app/src/main/assets/highlight/js/lang-fs.js | 0 app/src/main/assets/highlight/js/lang-go.js | 0 app/src/main/assets/highlight/js/lang-hs.js | 0 .../main/assets/highlight/js/lang-lasso.js | 0 .../assets/highlight/js/lang-lassoscript.js | 0 .../main/assets/highlight/js/lang-latex.js | 0 app/src/main/assets/highlight/js/lang-lgt.js | 0 app/src/main/assets/highlight/js/lang-lisp.js | 0 app/src/main/assets/highlight/js/lang-ll.js | 0 app/src/main/assets/highlight/js/lang-llvm.js | 0 .../main/assets/highlight/js/lang-logtalk.js | 0 app/src/main/assets/highlight/js/lang-ls.js | 0 app/src/main/assets/highlight/js/lang-lsp.js | 0 app/src/main/assets/highlight/js/lang-lua.js | 0 .../main/assets/highlight/js/lang-matlab.js | 0 app/src/main/assets/highlight/js/lang-ml.js | 0 .../main/assets/highlight/js/lang-mumps.js | 0 app/src/main/assets/highlight/js/lang-n.js | 0 .../main/assets/highlight/js/lang-nemerle.js | 0 .../main/assets/highlight/js/lang-pascal.js | 0 .../main/assets/highlight/js/lang-proto.js | 0 app/src/main/assets/highlight/js/lang-r.js | 0 app/src/main/assets/highlight/js/lang-rd.js | 0 app/src/main/assets/highlight/js/lang-rkt.js | 0 app/src/main/assets/highlight/js/lang-rust.js | 0 app/src/main/assets/highlight/js/lang-s.js | 0 .../main/assets/highlight/js/lang-scala.js | 0 app/src/main/assets/highlight/js/lang-scm.js | 0 app/src/main/assets/highlight/js/lang-sql.js | 0 app/src/main/assets/highlight/js/lang-ss.js | 0 .../main/assets/highlight/js/lang-swift.js | 4 +- app/src/main/assets/highlight/js/lang-tcl.js | 0 app/src/main/assets/highlight/js/lang-tex.js | 0 app/src/main/assets/highlight/js/lang-vb.js | 0 app/src/main/assets/highlight/js/lang-vbs.js | 0 app/src/main/assets/highlight/js/lang-vhd.js | 0 app/src/main/assets/highlight/js/lang-vhdl.js | 0 app/src/main/assets/highlight/js/lang-wiki.js | 0 app/src/main/assets/highlight/js/lang-xq.js | 0 .../main/assets/highlight/js/lang-xquery.js | 0 app/src/main/assets/highlight/js/lang-yaml.js | 0 app/src/main/assets/highlight/js/lang-yml.js | 0 app/src/main/assets/highlight/js/prettify.js | 3528 +++++++++-------- .../main/assets/highlight/styles/prettify.css | 155 +- .../assets/highlight/styles/prettify_dark.css | 154 +- .../data/dao/FragmentPagerAdapterModel.java | 3 +- .../fastaccess/data/dao/GitCommitModel.java | 4 +- .../fastaccess/data/dao/TrendingResponse.java | 47 - .../data/dao/model/AbstractGist.java | 22 +- .../fastaccess/data/service/GistService.java | 7 +- .../data/service/TrendingService.java | 8 +- .../fastaccess/helper/ParseDateFormat.java | 2 +- ...oJsoupProvider.java => JsoupProvider.java} | 20 +- .../NotificationSchedulerJobTask.java | 7 +- .../fastaccess/ui/adapter/TrendingAdapter.kt | 7 +- .../adapter/viewholder/GistsViewHolder.java | 3 +- .../adapter/viewholder/TrendingViewHolder.kt | 14 +- .../com/fastaccess/ui/base/BaseActivity.java | 13 +- .../ui/modules/gists/GistsListActivity.java | 10 +- .../ui/modules/gists/gist/GistActivity.java | 1 - .../ui/modules/gists/gist/GistPresenter.java | 13 +- .../gist/files/GistFilesListFragment.java | 3 +- .../profile/banner/BannerInfoPresenter.java | 99 +- .../profile/gists/ProfileGistsFragment.java | 5 +- .../profile/gists/ProfileGistsPresenter.java | 10 +- .../overview/ProfileOverviewFragment.java | 77 +- .../profile/overview/ProfileOverviewMvp.java | 6 - .../overview/ProfileOverviewPresenter.java | 76 - .../trending/fragment/TrendingFragment.kt | 4 +- .../trending/fragment/TrendingFragmentMvp.kt | 9 +- .../fragment/TrendingFragmentPresenter.kt | 43 +- .../ui/modules/user/UserPagerActivity.java | 5 + .../fastaccess/ui/widgets/AvatarLayout.java | 11 - .../scroll/RecyclerFastScroller.java | 40 +- .../pretty/helper/PrettifyHelper.java | 6 +- .../main/res/drawable/ic_language_small.xml | 11 + .../row_layouts/layout/trending_row_item.xml | 16 +- build.gradle | 1 - 94 files changed, 2250 insertions(+), 2201 deletions(-) mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-Splus.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-aea.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-agc.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-apollo.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-basic.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-cbm.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-cl.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-clj.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-css.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-dart.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-el.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-erl.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-erlang.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-fs.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-go.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-hs.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lasso.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lassoscript.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-latex.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lgt.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lisp.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-ll.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-llvm.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-logtalk.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-ls.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lsp.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-lua.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-matlab.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-ml.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-mumps.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-n.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-nemerle.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-pascal.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-proto.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-r.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-rd.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-rkt.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-rust.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-s.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-scala.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-scm.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-sql.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-ss.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-swift.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-tcl.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-tex.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-vb.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-vbs.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-vhd.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-vhdl.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-wiki.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-xq.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-xquery.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-yaml.js mode change 100644 => 100755 app/src/main/assets/highlight/js/lang-yml.js delete mode 100644 app/src/main/java/com/fastaccess/data/dao/TrendingResponse.java rename app/src/main/java/com/fastaccess/provider/rest/jsoup/{RetroJsoupProvider.java => JsoupProvider.java} (67%) create mode 100644 app/src/main/res/drawable/ic_language_small.xml diff --git a/README.md b/README.md index 46454a2a..78bfa558 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Ads are no longer available until further notice - [**Toasty**](https://github.com/GrenderG/Toasty) for displaying error/success messages - [**ShapedImageView**](https://github.com/gavinliu/ShapedImageView) for round avatars - [**MaterialTapTargetPrompt**](https://github.com/sjwall/MaterialTapTargetPrompt) for displaying tips throughout the app -- **Firebase** crash reporting +- **Fabric** analytics & crash reporting. - **Android Support Libraries**, the almighty ;-) # Contribution diff --git a/app/build.gradle b/app/build.gradle index c6ff87d0..1e2d0cea 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -147,8 +147,6 @@ dependencies { } compile "com.github.miguelbcr:RxBillingService:0.0.3" compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" - compile "com.github.florent37:retrojsoup:${retrojsoupVersion}" - compile "com.github.florent37:rxjsoup:${retrojsoupVersion}" compile 'org.jsoup:jsoup:1.10.2' compile "com.evernote:android-state:${state_version}" provided "org.projectlombok:lombok:${lombokVersion}" @@ -158,7 +156,6 @@ dependencies { kapt "com.jakewharton:butterknife-compiler:${butterKnifeVersion}" kapt 'com.github.matthiasrobbers:shortbread-compiler:1.0.1' kapt "org.projectlombok:lombok:${lombokVersion}" - kapt "com.github.florent37:retrojsoup-compiler:${retrojsoupVersion}" // testCompile "net.grandcentrix.thirtyinch:thirtyinch-test:$thirtyinchVersion" testCompile "junit:junit:${junitVersion}" testCompile "org.mockito:mockito-core:${mockitoVersion}" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a5807739..64f0ef8d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -220,7 +220,7 @@ + android:exported="true"> diff --git a/app/src/main/assets/highlight/js/lang-Splus.js b/app/src/main/assets/highlight/js/lang-Splus.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-aea.js b/app/src/main/assets/highlight/js/lang-aea.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-agc.js b/app/src/main/assets/highlight/js/lang-agc.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-apollo.js b/app/src/main/assets/highlight/js/lang-apollo.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-basic.js b/app/src/main/assets/highlight/js/lang-basic.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-cbm.js b/app/src/main/assets/highlight/js/lang-cbm.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-cl.js b/app/src/main/assets/highlight/js/lang-cl.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-clj.js b/app/src/main/assets/highlight/js/lang-clj.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-css.js b/app/src/main/assets/highlight/js/lang-css.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-dart.js b/app/src/main/assets/highlight/js/lang-dart.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-el.js b/app/src/main/assets/highlight/js/lang-el.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-erl.js b/app/src/main/assets/highlight/js/lang-erl.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-erlang.js b/app/src/main/assets/highlight/js/lang-erlang.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-fs.js b/app/src/main/assets/highlight/js/lang-fs.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-go.js b/app/src/main/assets/highlight/js/lang-go.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-hs.js b/app/src/main/assets/highlight/js/lang-hs.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lasso.js b/app/src/main/assets/highlight/js/lang-lasso.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lassoscript.js b/app/src/main/assets/highlight/js/lang-lassoscript.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-latex.js b/app/src/main/assets/highlight/js/lang-latex.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lgt.js b/app/src/main/assets/highlight/js/lang-lgt.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lisp.js b/app/src/main/assets/highlight/js/lang-lisp.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-ll.js b/app/src/main/assets/highlight/js/lang-ll.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-llvm.js b/app/src/main/assets/highlight/js/lang-llvm.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-logtalk.js b/app/src/main/assets/highlight/js/lang-logtalk.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-ls.js b/app/src/main/assets/highlight/js/lang-ls.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lsp.js b/app/src/main/assets/highlight/js/lang-lsp.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-lua.js b/app/src/main/assets/highlight/js/lang-lua.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-matlab.js b/app/src/main/assets/highlight/js/lang-matlab.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-ml.js b/app/src/main/assets/highlight/js/lang-ml.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-mumps.js b/app/src/main/assets/highlight/js/lang-mumps.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-n.js b/app/src/main/assets/highlight/js/lang-n.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-nemerle.js b/app/src/main/assets/highlight/js/lang-nemerle.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-pascal.js b/app/src/main/assets/highlight/js/lang-pascal.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-proto.js b/app/src/main/assets/highlight/js/lang-proto.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-r.js b/app/src/main/assets/highlight/js/lang-r.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-rd.js b/app/src/main/assets/highlight/js/lang-rd.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-rkt.js b/app/src/main/assets/highlight/js/lang-rkt.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-rust.js b/app/src/main/assets/highlight/js/lang-rust.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-s.js b/app/src/main/assets/highlight/js/lang-s.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-scala.js b/app/src/main/assets/highlight/js/lang-scala.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-scm.js b/app/src/main/assets/highlight/js/lang-scm.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-sql.js b/app/src/main/assets/highlight/js/lang-sql.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-ss.js b/app/src/main/assets/highlight/js/lang-ss.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-swift.js b/app/src/main/assets/highlight/js/lang-swift.js old mode 100644 new mode 100755 index 5442fa77..eb5c3143 --- a/app/src/main/assets/highlight/js/lang-swift.js +++ b/app/src/main/assets/highlight/js/lang-swift.js @@ -1,16 +1,18 @@ /* Copyright (C) 2015 Google Inc. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \n\r\t\v\f\0]+/,null," \n\r\t\v\f\x00"],["str",/^"(?:[^"\\]|(?:\\.)|(?:\\\((?:[^"\\)]|\\.)*\)))*"/,null,'"']],[["lit",/^(?:(?:0x[\da-fA-F][\da-fA-F_]*\.[\da-fA-F][\da-fA-F_]*[pP]?)|(?:\d[\d_]*\.\d[\d_]*[eE]?))[+-]?\d[\d_]*/,null],["lit",/^-?(?:(?:0(?:(?:b[01][01_]*)|(?:o[0-7][0-7_]*)|(?:x[\da-fA-F][\da-fA-F_]*)))|(?:\d[\d_]*))/,null],["lit",/^(?:true|false|nil)\b/,null],["kwd",/^\b(?:__COLUMN__|__FILE__|__FUNCTION__|__LINE__|#available|#else|#elseif|#endif|#if|#line|arch|arm|arm64|associativity|as|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|dynamicType|else|enum|fallthrough|final|for|func|get|import|indirect|infix|init|inout|internal|i386|if|in|iOS|iOSApplicationExtension|is|lazy|left|let|mutating|none|nonmutating|operator|optional|OSX|OSXApplicationExtension|override|postfix|precedence|prefix|private|protocol|Protocol|public|required|rethrows|return|right|safe|self|set|static|struct|subscript|super|switch|throw|try|Type|typealias|unowned|unsafe|var|weak|watchOS|while|willSet|x86_64)\b/, +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \n\r\t\v\f\0]+/,null," \n\r\t\v\f\x00"],["str",/^"(?:[^"\\]|(?:\\.)|(?:\\\((?:[^"\\)]|\\.)*\)))*"/,null,'"']],[["lit",/^(?:(?:0x[\da-fA-F][\da-fA-F_]*\.[\da-fA-F][\da-fA-F_]*[pP]?)|(?:\d[\d_]*\.\d[\d_]*[eE]?))[+-]?\d[\d_]*/,null],["lit",/^-?(?:(?:0(?:(?:b[01][01_]*)|(?:o[0-7][0-7_]*)|(?:x[\da-fA-F][\da-fA-F_]*)))|(?:\d[\d_]*))/,null],["lit",/^(?:true|false|nil)\b/,null],["kwd",/^\b(?:__COLUMN__|__FILE__|__FUNCTION__|__LINE__|#available|#else|#elseif|#endif|#if|#line|arch|arm|arm64|associativity|as|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|dynamicType|else|enum|extension|fallthrough|final|for|func|get|guard|import|indirect|infix|init|inout|internal|i386|if|in|iOS|iOSApplicationExtension|is|lazy|left|let|mutating|none|nonmutating|operator|optional|OSX|OSXApplicationExtension|override|postfix|precedence|prefix|private|protocol|Protocol|public|required|rethrows|return|right|safe|self|set|static|struct|subscript|super|switch|throw|try|Type|typealias|unowned|unsafe|var|weak|watchOS|while|willSet|x86_64)\b/, null],["com",/^\/\/.*?[\n\r]/,null],["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null],["pun",/^<<=|<=|<<|>>=|>=|>>|===|==|\.\.\.|&&=|\.\.<|!==|!=|&=|~=|~|\(|\)|\[|\]|{|}|@|#|;|\.|,|:|\|\|=|\?\?|\|\||&&|&\*|&\+|&-|&=|\+=|-=|\/=|\*=|\^=|%=|\|=|->|`|==|\+\+|--|\/|\+|!|\*|%|<|>|&|\||\^|\?|=|-|_/,null],["typ",/^\b(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null]]),["swift"]); diff --git a/app/src/main/assets/highlight/js/lang-tcl.js b/app/src/main/assets/highlight/js/lang-tcl.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-tex.js b/app/src/main/assets/highlight/js/lang-tex.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-vb.js b/app/src/main/assets/highlight/js/lang-vb.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-vbs.js b/app/src/main/assets/highlight/js/lang-vbs.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-vhd.js b/app/src/main/assets/highlight/js/lang-vhd.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-vhdl.js b/app/src/main/assets/highlight/js/lang-vhdl.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-wiki.js b/app/src/main/assets/highlight/js/lang-wiki.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-xq.js b/app/src/main/assets/highlight/js/lang-xq.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-xquery.js b/app/src/main/assets/highlight/js/lang-xquery.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-yaml.js b/app/src/main/assets/highlight/js/lang-yaml.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/lang-yml.js b/app/src/main/assets/highlight/js/lang-yml.js old mode 100644 new mode 100755 diff --git a/app/src/main/assets/highlight/js/prettify.js b/app/src/main/assets/highlight/js/prettify.js index abf46393..7809a8f0 100644 --- a/app/src/main/assets/highlight/js/prettify.js +++ b/app/src/main/assets/highlight/js/prettify.js @@ -1,6 +1,6 @@ /** * @license - * Copyright (C) 2006 Google Inc. + * Copyright (C) 2013 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,47 +17,50 @@ /** * @fileoverview - * some functions for browser-side pretty printing of code contained in html. + *
+ * Looks at query parameters to decide which language handlers and style-sheets + * to load. * - *

- * For a fairly comprehensive set of languages see the - * README - * file that came with this source. At a minimum, the lexer should work on a - * number of languages including C and friends, Java, Python, Bash, SQL, HTML, - * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk - * and a subset of Perl, but, because of commenting conventions, doesn't work on - * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. - *

- * Usage:

    - *
  1. include this source file in an html page via - * {@code } - *
  2. define style rules. See the example page for examples. - *
  3. mark the {@code
    } and {@code } tags in your source with
    - *    {@code class=prettyprint.}
    - *    You can also use the (html deprecated) {@code } tag, but the pretty
    - *    printer needs to do more substantial DOM manipulations to support that, so
    - *    some css styles may not be preserved.
    - * </ol>
    - * That's it.  I wanted to keep the API as simple as possible, so there's no
    - * need to specify which language the code is in, but if you wish, you can add
    - * another class to the {@code <pre>} or {@code <code>} element to specify the
    - * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    - * starts with "lang-" followed by a file extension, specifies the file type.
    - * See the "lang-*.js" files in this directory for code that implements
    - * per-language file handlers.
    - * <p>
    - * Change log:<br>
    - * cbeust, 2006/08/22
    - * <blockquote>
    - *   Java annotations (start with "@") are now captured as literals ("lit")
    - * </blockquote>
    - * @requires console
    + * Query Parameter     Format           Effect                        Default
    + * +------------------+---------------+------------------------------+--------+
    + * | autorun=         | true | false  | If true then prettyPrint()   | "true" |
    + * |                  |               | is called on page load.      |        |
    + * +------------------+---------------+------------------------------+--------+
    + * | lang=            | language name | Loads the language handler   | Can    |
    + * |                  |               | named "lang-<NAME>.js".      | appear |
    + * |                  |               | See available handlers at    | many   |
    + * |                  |               | https://github.com/google/   | times. |
    + * |                  |               | code-prettify/tree/master/   |        |
    + * |                  |               | src                          |        |
    + * +------------------+---------------+------------------------------+--------+
    + * | skin=            | skin name     | Loads the skin stylesheet    | none.  |
    + * |                  |               | named "<NAME>.css".          |        |
    + * |                  |               | https://cdn.rawgit.com/      |        |
    + * |                  |               | google/code-prettify/master/ |        |
    + * |                  |               | styles/index.html            |        |
    + * +------------------+---------------+------------------------------+--------+
    + * | callback=        | JS identifier | When "prettyPrint" finishes  | none   |
    + * |                  |               | window.exports[js_ident] is  |        |
    + * |                  |               | called.                      |        |
    + * |                  |               | The callback must be under   |        |
    + * |                  |               | exports to reduce the risk   |        |
    + * |                  |               | of XSS via query parameter   |        |
    + * |                  |               | injection.                   |        |
    + * +------------------+---------------+------------------------------+--------+
    + *
    + * Exmaples
    + * .../run_prettify.js?lang=css&skin=sunburst
    + *   1. Loads the CSS language handler which can be used to prettify CSS
    + *      stylesheets, HTML <style> element bodies and style="..." attributes
    + *      values.
    + *   2. Loads the sunburst.css stylesheet instead of the default prettify.css
    + *      stylesheet.
    + *      A gallery of stylesheets is available at
    + *      https://cdn.rawgit.com/google/code-prettify/master/styles/index.html
    + *   3. Since autorun=false is not specified, calls prettyPrint() on page load.
    + * </div>
      */
     
    -// JSLint declarations
    -/*global console, document, navigator, setTimeout, window, define */
    -
    -
     /**
     * @typedef {!Array.<number|string>}
     * Alternating indices and the decorations that should be inserted there.
    @@ -109,1634 +112,1887 @@ var SourceSpansT;
     /** @define {boolean} */
     var IN_GLOBAL_SCOPE = false;
     
    -var HACK_TO_FIX_JS_INCLUDE_PL;
    -
    -/**
    - * {@type !{
    - *   'createSimpleLexer': function (Array, Array): (function (JobT)),
    - *   'registerLangHandler': function (function (JobT), Array.<string>),
    - *   'PR_ATTRIB_NAME': string,
    - *   'PR_ATTRIB_NAME': string,
    - *   'PR_ATTRIB_VALUE': string,
    - *   'PR_COMMENT': string,
    - *   'PR_DECLARATION': string,
    - *   'PR_KEYWORD': string,
    - *   'PR_LITERAL': string,
    - *   'PR_NOCODE': string,
    - *   'PR_PLAIN': string,
    - *   'PR_PUNCTUATION': string,
    - *   'PR_SOURCE': string,
    - *   'PR_STRING': string,
    - *   'PR_TAG': string,
    - *   'PR_TYPE': string,
    - *   'prettyPrintOne': function (string, string, number|boolean),
    - *   'prettyPrint': function (?function, ?(HTMLElement|HTMLDocument))
    - * }}
    - * @const
    - */
    -var PR;
    -
    -/**
    - * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    - * UI events.
    - * If set to {@code false}, {@code prettyPrint()} is synchronous.
    - */
    -window['PR_SHOULD_USE_CONTINUATION'] = true;
    -
    -/**
    - * Pretty print a chunk of code.
    - * @param {string} sourceCodeHtml The HTML to pretty print.
    - * @param {string} opt_langExtension The language name to use.
    - *     Typically, a filename extension like 'cpp' or 'java'.
    - * @param {number|boolean} opt_numberLines True to number lines,
    - *     or the 1-indexed number of the first line in sourceCodeHtml.
    - * @return {string} code as html, but prettier
    - */
    -var prettyPrintOne;
    -/**
    - * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    - * {@code class=prettyprint} and prettify them.
    - *
    - * @param {Function} opt_whenDone called when prettifying is done.
    - * @param {HTMLElement|HTMLDocument} opt_root an element or document
    - *   containing all the elements to pretty print.
    - *   Defaults to {@code document.body}.
    - */
    -var prettyPrint;
    -
    -
     (function () {
    +  "use strict";
    +
       var win = window;
    -  // Keyword lists for various languages.
    -  // We use things that coerce to strings to make them compact when minified
    -  // and to defeat aggressive optimizers that fold large string constants.
    -  var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
    -  var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
    -      "double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed," +
    -      "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
    -  var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
    -      "new,operator,private,protected,public,this,throw,true,try,typeof"];
    -  var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignas,alignof,align_union,asm,axiom,bool," +
    -      "concept,concept_map,const_cast,constexpr,decltype,delegate," +
    -      "dynamic_cast,explicit,export,friend,generic,late_check," +
    -      "mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert," +
    -      "static_cast,template,typeid,typename,using,virtual,where"];
    -  var JAVA_KEYWORDS = [COMMON_KEYWORDS,
    -      "abstract,assert,boolean,byte,extends,finally,final,implements,import," +
    -      "instanceof,interface,null,native,package,strictfp,super,synchronized," +
    -      "throws,transient"];
    -  var CSHARP_KEYWORDS = [COMMON_KEYWORDS,
    -      "abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending," +
    -      "dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface," +
    -      "internal,into,is,join,let,lock,null,object,out,override,orderby,params," +
    -      "partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong," +
    -      "unchecked,unsafe,ushort,value,var,virtual,where,yield"];
    -  var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
    -      "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
    -      "throw,true,try,unless,until,when,while,yes";
    -  var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
    -      "abstract,async,await,constructor,debugger,enum,eval,export,function," +
    -      "get,implements,instanceof,interface,let,null,set,undefined,var,with," +
    -      "yield,Infinity,NaN"];
    -  var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
    -      "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
    -      "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
    -  var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
    -      "elif,except,exec,finally,from,global,import,in,is,lambda," +
    -      "nonlocal,not,or,pass,print,raise,try,with,yield," +
    -      "False,True,None"];
    -  var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
    -      "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
    -      "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
    -      "BEGIN,END"];
    -  var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
    -      "function,in,local,set,then,until"];
    -  var ALL_KEYWORDS = [
    -      CPP_KEYWORDS, CSHARP_KEYWORDS, JAVA_KEYWORDS, JSCRIPT_KEYWORDS,
    -      PERL_KEYWORDS, PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
    -  var C_TYPES = /^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
    +  var doc = document;
    +  var root = doc.documentElement;
    +  var head = doc['head'] || doc.getElementsByTagName("head")[0] || root;
     
    -  // token style names.  correspond to css classes
    -  /**
    -   * token style for a string literal
    -   * @const
    -   */
    -  var PR_STRING = 'str';
    -  /**
    -   * token style for a keyword
    -   * @const
    -   */
    -  var PR_KEYWORD = 'kwd';
    -  /**
    -   * token style for a comment
    -   * @const
    -   */
    -  var PR_COMMENT = 'com';
    -  /**
    -   * token style for a type
    -   * @const
    -   */
    -  var PR_TYPE = 'typ';
    -  /**
    -   * token style for a literal value.  e.g. 1, null, true.
    -   * @const
    -   */
    -  var PR_LITERAL = 'lit';
    -  /**
    -   * token style for a punctuation string.
    -   * @const
    -   */
    -  var PR_PUNCTUATION = 'pun';
    -  /**
    -   * token style for plain text.
    -   * @const
    -   */
    -  var PR_PLAIN = 'pln';
    +  // From http://javascript.nwbox.com/ContentLoaded/contentloaded.js
    +  // Author: Diego Perini (diego.perini at gmail.com)
    +  // Summary: cross-browser wrapper for DOMContentLoaded
    +  // Updated: 20101020
    +  // License: MIT
    +  // Version: 1.2
    +  function contentLoaded(callback) {
    +    var addEventListener = doc['addEventListener'];
    +    var done = false, top = true,
    +        add = addEventListener ? 'addEventListener' : 'attachEvent',
    +        rem = addEventListener ? 'removeEventListener' : 'detachEvent',
    +        pre = addEventListener ? '' : 'on',
     
    -  /**
    -   * token style for an sgml tag.
    -   * @const
    -   */
    -  var PR_TAG = 'tag';
    -  /**
    -   * token style for a markup declaration such as a DOCTYPE.
    -   * @const
    -   */
    -  var PR_DECLARATION = 'dec';
    -  /**
    -   * token style for embedded source.
    -   * @const
    -   */
    -  var PR_SOURCE = 'src';
    -  /**
    -   * token style for an sgml attribute name.
    -   * @const
    -   */
    -  var PR_ATTRIB_NAME = 'atn';
    -  /**
    -   * token style for an sgml attribute value.
    -   * @const
    -   */
    -  var PR_ATTRIB_VALUE = 'atv';
    -
    -  /**
    -   * A class that indicates a section of markup that is not code, e.g. to allow
    -   * embedding of line numbers within code listings.
    -   * @const
    -   */
    -  var PR_NOCODE = 'nocode';
    -
    -  
    -  
    -  /**
    -   * A set of tokens that can precede a regular expression literal in
    -   * javascript
    -   * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
    -   * has the full list, but I've removed ones that might be problematic when
    -   * seen in languages that don't support regular expression literals.
    -   *
    -   * <p>Specifically, I've removed any keywords that can't precede a regexp
    -   * literal in a syntactically legal javascript program, and I've removed the
    -   * "in" keyword since it's not a keyword in many languages, and might be used
    -   * as a count of inches.
    -   *
    -   * <p>The link above does not accurately describe EcmaScript rules since
    -   * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    -   * very well in practice.
    -   *
    -   * @private
    -   * @const
    -   */
    -  var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
    -  
    -  // CAVEAT: this does not properly handle the case where a regular
    -  // expression immediately follows another since a regular expression may
    -  // have flags for case-sensitivity and the like.  Having regexp tokens
    -  // adjacent is not valid in any language I'm aware of, so I'm punting.
    -  // TODO: maybe style special characters inside a regexp as punctuation.
    -
    -  /**
    -   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    -   * matches the union of the sets of strings matched by the input RegExp.
    -   * Since it matches globally, if the input strings have a start-of-input
    -   * anchor (/^.../), it is ignored for the purposes of unioning.
    -   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    -   * @return {RegExp} a global regex.
    -   */
    -  function combinePrefixPatterns(regexs) {
    -    var capturedGroupIndex = 0;
    -  
    -    var needToFoldCase = false;
    -    var ignoreCase = false;
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.ignoreCase) {
    -        ignoreCase = true;
    -      } else if (/[a-z]/i.test(regex.source.replace(
    -                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    -        needToFoldCase = true;
    -        ignoreCase = false;
    -        break;
    -      }
    -    }
    -  
    -    var escapeCharToCodeUnit = {
    -      'b': 8,
    -      't': 9,
    -      'n': 0xa,
    -      'v': 0xb,
    -      'f': 0xc,
    -      'r': 0xd
    -    };
    -  
    -    function decodeEscape(charsetPart) {
    -      var cc0 = charsetPart.charCodeAt(0);
    -      if (cc0 !== 92 /* \\ */) {
    -        return cc0;
    -      }
    -      var c1 = charsetPart.charAt(1);
    -      cc0 = escapeCharToCodeUnit[c1];
    -      if (cc0) {
    -        return cc0;
    -      } else if ('0' <= c1 && c1 <= '7') {
    -        return parseInt(charsetPart.substring(1), 8);
    -      } else if (c1 === 'u' || c1 === 'x') {
    -        return parseInt(charsetPart.substring(2), 16);
    -      } else {
    -        return charsetPart.charCodeAt(1);
    -      }
    -    }
    -  
    -    function encodeEscape(charCode) {
    -      if (charCode < 0x20) {
    -        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    -      }
    -      var ch = String.fromCharCode(charCode);
    -      return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
    -          ? "\\" + ch : ch;
    -    }
    -  
    -    function caseFoldCharset(charSet) {
    -      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    -          new RegExp(
    -              '\\\\u[0-9A-Fa-f]{4}'
    -              + '|\\\\x[0-9A-Fa-f]{2}'
    -              + '|\\\\[0-3][0-7]{0,2}'
    -              + '|\\\\[0-7]{1,2}'
    -              + '|\\\\[\\s\\S]'
    -              + '|-'
    -              + '|[^-\\\\]',
    -              'g'));
    -      var ranges = [];
    -      var inverse = charsetParts[0] === '^';
    -  
    -      var out = ['['];
    -      if (inverse) { out.push('^'); }
    -  
    -      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    -        var p = charsetParts[i];
    -        if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
    -          out.push(p);
    -        } else {
    -          var start = decodeEscape(p);
    -          var end;
    -          if (i + 2 < n && '-' === charsetParts[i + 1]) {
    -            end = decodeEscape(charsetParts[i + 2]);
    -            i += 2;
    -          } else {
    -            end = start;
    +        init = function(e) {
    +          if (e.type == 'readystatechange' && doc.readyState != 'complete') {
    +            return;
               }
    -          ranges.push([start, end]);
    -          // If the range might intersect letters, then expand it.
    -          // This case handling is too simplistic.
    -          // It does not deal with non-latin case folding.
    -          // It works for latin source code identifiers though.
    -          if (!(end < 65 || start > 122)) {
    -            if (!(end < 65 || start > 90)) {
    -              ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    -            }
    -            if (!(end < 97 || start > 122)) {
    -              ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    -            }
    +          (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
    +          if (!done && (done = true)) { callback.call(win, e.type || e); }
    +        },
    +
    +        poll = function() {
    +          try {
    +            root.doScroll('left');
    +          } catch(e) {
    +            win.setTimeout(poll, 50);
    +            return;
               }
    -        }
    -      }
    -  
    -      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    -      // -> [[1, 12], [14, 14], [16, 17]]
    -      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    -      var consolidatedRanges = [];
    -      var lastRange = [];
    -      for (var i = 0; i < ranges.length; ++i) {
    -        var range = ranges[i];
    -        if (range[0] <= lastRange[1] + 1) {
    -          lastRange[1] = Math.max(lastRange[1], range[1]);
    -        } else {
    -          consolidatedRanges.push(lastRange = range);
    -        }
    -      }
    -  
    -      for (var i = 0; i < consolidatedRanges.length; ++i) {
    -        var range = consolidatedRanges[i];
    -        out.push(encodeEscape(range[0]));
    -        if (range[1] > range[0]) {
    -          if (range[1] + 1 > range[0]) { out.push('-'); }
    -          out.push(encodeEscape(range[1]));
    -        }
    -      }
    -      out.push(']');
    -      return out.join('');
    -    }
    -  
    -    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    -      // Split into character sets, escape sequences, punctuation strings
    -      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    -      // include any of the above.
    -      var parts = regex.source.match(
    -          new RegExp(
    -              '(?:'
    -              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    -              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    -              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    -              + '|\\\\[0-9]+'  // a back-reference or octal escape
    -              + '|\\\\[^ux0-9]'  // other escape sequence
    -              + '|\\(\\?[:!=]'  // start of a non-capturing group
    -              + '|[\\(\\)\\^]'  // start/end of a group, or line start
    -              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    -              + ')',
    -              'g'));
    -      var n = parts.length;
    -  
    -      // Maps captured group numbers to the number they will occupy in
    -      // the output or to -1 if that has not been determined, or to
    -      // undefined if they need not be capturing in the output.
    -      var capturedGroups = [];
    -  
    -      // Walk over and identify back references to build the capturedGroups
    -      // mapping.
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          // groups are 1-indexed, so max group index is count of '('
    -          ++groupIndex;
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue) {
    -            if (decimalValue <= groupIndex) {
    -              capturedGroups[decimalValue] = -1;
    -            } else {
    -              // Replace with an unambiguous escape sequence so that
    -              // an octal escape sequence does not turn into a backreference
    -              // to a capturing group from an earlier regex.
    -              parts[i] = encodeEscape(decimalValue);
    -            }
    -          }
    -        }
    -      }
    -  
    -      // Renumber groups and reduce capturing groups to non-capturing groups
    -      // where possible.
    -      for (var i = 1; i < capturedGroups.length; ++i) {
    -        if (-1 === capturedGroups[i]) {
    -          capturedGroups[i] = ++capturedGroupIndex;
    -        }
    -      }
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          ++groupIndex;
    -          if (!capturedGroups[groupIndex]) {
    -            parts[i] = '(?:';
    -          }
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue && decimalValue <= groupIndex) {
    -            parts[i] = '\\' + capturedGroups[decimalValue];
    -          }
    -        }
    -      }
    -  
    -      // Remove any prefix anchors so that the output will match anywhere.
    -      // ^^ really does mean an anchored match though.
    -      for (var i = 0; i < n; ++i) {
    -        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    -      }
    -  
    -      // Expand letters to groups to handle mixing of case-sensitive and
    -      // case-insensitive patterns if necessary.
    -      if (regex.ignoreCase && needToFoldCase) {
    -        for (var i = 0; i < n; ++i) {
    -          var p = parts[i];
    -          var ch0 = p.charAt(0);
    -          if (p.length >= 2 && ch0 === '[') {
    -            parts[i] = caseFoldCharset(p);
    -          } else if (ch0 !== '\\') {
    -            // TODO: handle letters in numeric escapes.
    -            parts[i] = p.replace(
    -                /[a-zA-Z]/g,
    -                function (ch) {
    -                  var cc = ch.charCodeAt(0);
    -                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    -                });
    -          }
    -        }
    -      }
    -  
    -      return parts.join('');
    -    }
    -  
    -    var rewritten = [];
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    -      rewritten.push(
    -          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    -    }
    -  
    -    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    -  }
    +          init('poll');
    +        };
     
    -  /**
    -   * Split markup into a string of source code and an array mapping ranges in
    -   * that string to the text nodes in which they appear.
    -   *
    -   * <p>
    -   * The HTML DOM structure:</p>
    -   * <pre>
    -   * (Element   "p"
    -   *   (Element "b"
    -   *     (Text  "print "))       ; #1
    -   *   (Text    "'Hello '")      ; #2
    -   *   (Element "br")            ; #3
    -   *   (Text    "  + 'World';")) ; #4
    -   * </pre>
    -   * <p>
    -   * corresponds to the HTML
    -   * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
    -   *
    -   * <p>
    -   * It will produce the output:</p>
    -   * <pre>
    -   * {
    -   *   sourceCode: "print 'Hello '\n  + 'World';",
    -   *   //                     1          2
    -   *   //           012345678901234 5678901234567
    -   *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
    -   * }
    -   * </pre>
    -   * <p>
    -   * where #1 is a reference to the {@code "print "} text node above, and so
    -   * on for the other text nodes.
    -   * </p>
    -   *
    -   * <p>
    -   * The {@code} spans array is an array of pairs.  Even elements are the start
    -   * indices of substrings, and odd elements are the text nodes (or BR elements)
    -   * that contain the text for those substrings.
    -   * Substrings continue until the next index or the end of the source.
    -   * </p>
    -   *
    -   * @param {Node} node an HTML DOM subtree containing source-code.
    -   * @param {boolean|number} isPreformatted truthy if white-space in
    -   *    text nodes should be considered significant.
    -   * @return {SourceSpansT} source code and the nodes in which they occur.
    -   */
    -  function extractSourceSpans(node, isPreformatted) {
    -    var nocode = /(?:^|\s)nocode(?:\s|$)/;
    -  
    -    var chunks = [];
    -    var length = 0;
    -    var spans = [];
    -    var k = 0;
    -  
    -    function walk(node) {
    -      var type = node.nodeType;
    -      if (type == 1) {  // Element
    -        if (nocode.test(node.className)) { return; }
    -        for (var child = node.firstChild; child; child = child.nextSibling) {
    -          walk(child);
    -        }
    -        var nodeName = node.nodeName.toLowerCase();
    -        if ('br' === nodeName || 'li' === nodeName) {
    -          chunks[k] = '\n';
    -          spans[k << 1] = length++;
    -          spans[(k++ << 1) | 1] = node;
    -        }
    -      } else if (type == 3 || type == 4) {  // Text
    -        var text = node.nodeValue;
    -        if (text.length) {
    -          if (!isPreformatted) {
    -            text = text.replace(/[ \t\r\n]+/g, ' ');
    -          } else {
    -            text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
    -          }
    -          // TODO: handle tabs here?
    -          chunks[k] = text;
    -          spans[k << 1] = length;
    -          length += text.length;
    -          spans[(k++ << 1) | 1] = node;
    -        }
    -      }
    -    }
    -  
    -    walk(node);
    -  
    -    return {
    -      sourceCode: chunks.join('').replace(/\n$/, ''),
    -      spans: spans
    -    };
    -  }
    -
    -  /**
    -   * Apply the given language handler to sourceCode and add the resulting
    -   * decorations to out.
    -   * @param {!Element} sourceNode
    -   * @param {number} basePos the index of sourceCode within the chunk of source
    -   *    whose decorations are already present on out.
    -   * @param {string} sourceCode
    -   * @param {function(JobT)} langHandler
    -   * @param {DecorationsT} out
    -   */
    -  function appendDecorations(
    -      sourceNode, basePos, sourceCode, langHandler, out) {
    -    if (!sourceCode) { return; }
    -    /** @type {JobT} */
    -    var job = {
    -      sourceNode: sourceNode,
    -      pre: 1,
    -      langExtension: null,
    -      numberLines: null,
    -      sourceCode: sourceCode,
    -      spans: null,
    -      basePos: basePos,
    -      decorations: null
    -    };
    -    langHandler(job);
    -    out.push.apply(out, job.decorations);
    -  }
    -
    -  var notWs = /\S/;
    -
    -  /**
    -   * Given an element, if it contains only one child element and any text nodes
    -   * it contains contain only space characters, return the sole child element.
    -   * Otherwise returns undefined.
    -   * <p>
    -   * This is meant to return the CODE element in {@code <pre><code ...>} when
    -   * there is a single child element that contains all the non-space textual
    -   * content, but not to return anything where there are multiple child elements
    -   * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
    -   * is textual content.
    -   */
    -  function childContentWrapper(element) {
    -    var wrapper = undefined;
    -    for (var c = element.firstChild; c; c = c.nextSibling) {
    -      var type = c.nodeType;
    -      wrapper = (type === 1)  // Element Node
    -          ? (wrapper ? element : c)
    -          : (type === 3)  // Text Node
    -          ? (notWs.test(c.nodeValue) ? element : wrapper)
    -          : wrapper;
    -    }
    -    return wrapper === element ? undefined : wrapper;
    -  }
    -
    -  /** Given triples of [style, pattern, context] returns a lexing function,
    -    * The lexing function interprets the patterns to find token boundaries and
    -    * returns a decoration list of the form
    -    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    -    * where index_n is an index into the sourceCode, and style_n is a style
    -    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    -    * all characters in sourceCode[index_n-1:index_n].
    -    *
    -    * The stylePatterns is a list whose elements have the form
    -    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    -    *
    -    * Style is a style constant like PR_PLAIN, or can be a string of the
    -    * form 'lang-FOO', where FOO is a language extension describing the
    -    * language of the portion of the token in $1 after pattern executes.
    -    * E.g., if style is 'lang-lisp', and group 1 contains the text
    -    * '(hello (world))', then that portion of the token will be passed to the
    -    * registered lisp handler for formatting.
    -    * The text before and after group 1 will be restyled using this decorator
    -    * so decorators should take care that this doesn't result in infinite
    -    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    -    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    -    * '<script>foo()<\/script>', which would cause the current decorator to
    -    * be called with '<script>' which would not match the same rule since
    -    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    -    * the generic tag rule.  The handler registered for the 'js' extension would
    -    * then be called with 'foo()', and finally, the current decorator would
    -    * be called with '<\/script>' which would not match the original rule and
    -    * so the generic tag rule would identify it as a tag.
    -    *
    -    * Pattern must only match prefixes, and if it matches a prefix, then that
    -    * match is considered a token with the same style.
    -    *
    -    * Context is applied to the last non-whitespace, non-comment token
    -    * recognized.
    -    *
    -    * Shortcut is an optional string of characters, any of which, if the first
    -    * character, gurantee that this pattern and only this pattern matches.
    -    *
    -    * @param {Array} shortcutStylePatterns patterns that always start with
    -    *   a known character.  Must have a shortcut string.
    -    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    -    *   order if the shortcut ones fail.  May have shortcuts.
    -    *
    -    * @return {function (JobT)} a function that takes an undecorated job and
    -    *   attaches a list of decorations.
    -    */
    -  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    -    var shortcuts = {};
    -    var tokenizer;
    -    (function () {
    -      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    -      var allRegexs = [];
    -      var regexKeys = {};
    -      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    -        var patternParts = allPatterns[i];
    -        var shortcutChars = patternParts[3];
    -        if (shortcutChars) {
    -          for (var c = shortcutChars.length; --c >= 0;) {
    -            shortcuts[shortcutChars.charAt(c)] = patternParts;
    -          }
    -        }
    -        var regex = patternParts[1];
    -        var k = '' + regex;
    -        if (!regexKeys.hasOwnProperty(k)) {
    -          allRegexs.push(regex);
    -          regexKeys[k] = null;
    -        }
    -      }
    -      allRegexs.push(/[\0-\uffff]/);
    -      tokenizer = combinePrefixPatterns(allRegexs);
    -    })();
    -
    -    var nPatterns = fallthroughStylePatterns.length;
    -
    -    /**
    -     * Lexes job.sourceCode and attaches an output array job.decorations of
    -     * style classes preceded by the position at which they start in
    -     * job.sourceCode in order.
    -     *
    -     * @type{function (JobT)}
    -     */
    -    var decorate = function (job) {
    -      var sourceCode = job.sourceCode, basePos = job.basePos;
    -      var sourceNode = job.sourceNode;
    -      /** Even entries are positions in source in ascending order.  Odd enties
    -        * are style markers (e.g., PR_COMMENT) that run from that position until
    -        * the end.
    -        * @type {DecorationsT}
    -        */
    -      var decorations = [basePos, PR_PLAIN];
    -      var pos = 0;  // index into sourceCode
    -      var tokens = sourceCode.match(tokenizer) || [];
    -      var styleCache = {};
    -
    -      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    -        var token = tokens[ti];
    -        var style = styleCache[token];
    -        var match = void 0;
    -
    -        var isEmbedded;
    -        if (typeof style === 'string') {
    -          isEmbedded = false;
    -        } else {
    -          var patternParts = shortcuts[token.charAt(0)];
    -          if (patternParts) {
    -            match = token.match(patternParts[1]);
    -            style = patternParts[0];
    -          } else {
    -            for (var i = 0; i < nPatterns; ++i) {
    -              patternParts = fallthroughStylePatterns[i];
    -              match = token.match(patternParts[1]);
    -              if (match) {
    -                style = patternParts[0];
    -                break;
    -              }
    -            }
    -
    -            if (!match) {  // make sure that we make progress
    -              style = PR_PLAIN;
    -            }
    -          }
    -
    -          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    -          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    -            isEmbedded = false;
    -            style = PR_SOURCE;
    -          }
    -
    -          if (!isEmbedded) { styleCache[token] = style; }
    -        }
    -
    -        var tokenStart = pos;
    -        pos += token.length;
    -
    -        if (!isEmbedded) {
    -          decorations.push(basePos + tokenStart, style);
    -        } else {  // Treat group 1 as an embedded block of source code.
    -          var embeddedSource = match[1];
    -          var embeddedSourceStart = token.indexOf(embeddedSource);
    -          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    -          if (match[2]) {
    -            // If embeddedSource can be blank, then it would match at the
    -            // beginning which would cause us to infinitely recurse on the
    -            // entire token, so we catch the right context in match[2].
    -            embeddedSourceEnd = token.length - match[2].length;
    -            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    -          }
    -          var lang = style.substring(5);
    -          // Decorate the left of the embedded source
    -          appendDecorations(
    -              sourceNode,
    -              basePos + tokenStart,
    -              token.substring(0, embeddedSourceStart),
    -              decorate, decorations);
    -          // Decorate the embedded source
    -          appendDecorations(
    -              sourceNode,
    -              basePos + tokenStart + embeddedSourceStart,
    -              embeddedSource,
    -              langHandlerForExtension(lang, embeddedSource),
    -              decorations);
    -          // Decorate the right of the embedded section
    -          appendDecorations(
    -              sourceNode,
    -              basePos + tokenStart + embeddedSourceEnd,
    -              token.substring(embeddedSourceEnd),
    -              decorate, decorations);
    -        }
    -      }
    -      job.decorations = decorations;
    -    };
    -    return decorate;
    -  }
    -
    -  /** returns a function that produces a list of decorations from source text.
    -    *
    -    * This code treats ", ', and ` as string delimiters, and \ as a string
    -    * escape.  It does not recognize perl's qq() style strings.
    -    * It has no special handling for double delimiter escapes as in basic, or
    -    * the tripled delimiters used in python, but should work on those regardless
    -    * although in those cases a single string literal may be broken up into
    -    * multiple adjacent string literals.
    -    *
    -    * It recognizes C, C++, and shell style comments.
    -    *
    -    * @param {Object} options a set of optional parameters.
    -    * @return {function (JobT)} a function that examines the source code
    -    *     in the input job and builds a decoration list which it attaches to
    -    *     the job.
    -    */
    -  function sourceDecorator(options) {
    -    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    -    if (options['tripleQuotedStrings']) {
    -      // '''multi-line-string''', 'single-line-string', and double-quoted
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    -           null, '\'"']);
    -    } else if (options['multiLineStrings']) {
    -      // 'multi-line-string', "multi-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    -           null, '\'"`']);
    +    if (doc.readyState == 'complete') {
    +      callback.call(win, 'lazy');
         } else {
    -      // 'single-line-string', "single-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,
    -           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    -           null, '"\'']);
    -    }
    -    if (options['verbatimStrings']) {
    -      // verbatim-string-literal production from the C# grammar.  See issue 93.
    -      fallthroughStylePatterns.push(
    -          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    -    }
    -    var hc = options['hashComments'];
    -    if (hc) {
    -      if (options['cStyleComments']) {
    -        if (hc > 1) {  // multiline hash comments
    -          shortcutStylePatterns.push(
    -              [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
    -        } else {
    -          // Stop C preprocessor declarations at an unclosed open comment
    -          shortcutStylePatterns.push(
    -              [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
    -               null, '#']);
    -        }
    -        // #include <stdio.h>
    -        fallthroughStylePatterns.push(
    -            [PR_STRING,
    -             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
    -             null]);
    -      } else {
    -        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +      if (doc.createEventObject && root.doScroll) {
    +        try { top = !win.frameElement; } catch(e) { }
    +        if (top) { poll(); }
           }
    +      doc[add](pre + 'DOMContentLoaded', init, false);
    +      doc[add](pre + 'readystatechange', init, false);
    +      win[add](pre + 'load', init, false);
         }
    -    if (options['cStyleComments']) {
    -      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    -      fallthroughStylePatterns.push(
    -          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    -    }
    -    var regexLiterals = options['regexLiterals'];
    -    if (regexLiterals) {
    -      /**
    -       * @const
    -       */
    -      var regexExcls = regexLiterals > 1
    -        ? ''  // Multiline regex literals
    -        : '\n\r';
    -      /**
    -       * @const
    -       */
    -      var regexAny = regexExcls ? '.' : '[\\S\\s]';
    -      /**
    -       * @const
    -       */
    -      var REGEX_LITERAL = (
    -          // A regular expression literal starts with a slash that is
    -          // not followed by * or / so that it is not confused with
    -          // comments.
    -          '/(?=[^/*' + regexExcls + '])'
    -          // and then contains any number of raw characters,
    -          + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
    -          // escape sequences (\x5C),
    -          +    '|\\x5C' + regexAny
    -          // or non-nesting character sets (\x5B\x5D);
    -          +    '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
    -          +             '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
    -          // finally closed by a /.
    -          + '/');
    -      fallthroughStylePatterns.push(
    -          ['lang-regex',
    -           RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    -           ]);
    -    }
    -
    -    var types = options['types'];
    -    if (types) {
    -      fallthroughStylePatterns.push([PR_TYPE, types]);
    -    }
    -
    -    var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
    -    if (keywords.length) {
    -      fallthroughStylePatterns.push(
    -          [PR_KEYWORD,
    -           new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
    -           null]);
    -    }
    -
    -    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    -
    -    var punctuation =
    -      // The Bash man page says
    -
    -      // A word is a sequence of characters considered as a single
    -      // unit by GRUB. Words are separated by metacharacters,
    -      // which are the following plus space, tab, and newline: { }
    -      // | & $ ; < >
    -      // ...
    -
    -      // A word beginning with # causes that word and all remaining
    -      // characters on that line to be ignored.
    -
    -      // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
    -      // comment but empirically
    -      // $ echo {#}
    -      // {#}
    -      // $ echo \$#
    -      // $#
    -      // $ echo }#
    -      // }#
    -
    -      // so /(?:^|[|&;<>\s])/ is more appropriate.
    -
    -      // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
    -      // suggests that this definition is compatible with a
    -      // default mode that tries to use a single token definition
    -      // to recognize both bash/python style comments and C
    -      // preprocessor directives.
    -
    -      // This definition of punctuation does not include # in the list of
    -      // follow-on exclusions, so # will not be broken before if preceeded
    -      // by a punctuation character.  We could try to exclude # after
    -      // [|&;<>] but that doesn't seem to cause many major problems.
    -      // If that does turn out to be a problem, we should change the below
    -      // when hc is truthy to include # in the run of punctuation characters
    -      // only when not followint [|&;<>].
    -      '^.[^\\s\\w.$@\'"`/\\\\]*';
    -    if (options['regexLiterals']) {
    -      punctuation += '(?!\s*\/)';
    -    }
    -
    -    fallthroughStylePatterns.push(
    -        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    -        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
    -        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_LITERAL,
    -         new RegExp(
    -             '^(?:'
    -             // A hex number
    -             + '0x[a-f0-9]+'
    -             // or an octal or decimal number,
    -             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    -             // possibly in scientific notation
    -             + '(?:e[+\\-]?\\d+)?'
    -             + ')'
    -             // with an optional modifier like UL for unsigned long
    -             + '[a-z]*', 'i'),
    -         null, '0123456789'],
    -        // Don't treat escaped quotes in bash as starting strings.
    -        // See issue 144.
    -        [PR_PLAIN,       /^\\[\s\S]?/, null],
    -        [PR_PUNCTUATION, new RegExp(punctuation), null]);
    -
    -    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
       }
     
    -  var decorateSource = sourceDecorator({
    -        'keywords': ALL_KEYWORDS,
    -        'hashComments': true,
    -        'cStyleComments': true,
    -        'multiLineStrings': true,
    -        'regexLiterals': true
    +  // Given a list of URLs to stylesheets, loads the first that loads without
    +  // triggering an error event.
    +  function loadStylesheetsFallingBack(stylesheets) {
    +    var n = stylesheets.length;
    +    function load(i) {
    +      if (i === n) { return; }
    +      var link = doc.createElement('link');
    +      link.rel = 'stylesheet';
    +      link.type = 'text/css';
    +      if (i + 1 < n) {
    +        // http://pieisgood.org/test/script-link-events/ indicates that many
    +        // versions of IE do not support onerror on <link>s, though
    +        // http://msdn.microsoft.com/en-us/library/ie/ms535848(v=vs.85).aspx
    +        // indicates that recent IEs do support error.
    +        link.error = link.onerror = function () { load(i + 1); };
    +      }
    +      link.href = stylesheets[i];
    +      head.appendChild(link);
    +    }
    +    load(0);
    +  }
    +
    +  var scriptQuery = '';
    +  // Look for the <script> node that loads this script to get its parameters.
    +  // This starts looking at the end instead of just considering the last
    +  // because deferred and async scripts run out of order.
    +  // If the script is loaded twice, then this will run in reverse order.
    +  var scripts = doc.getElementsByTagName('script');
    +  for (var i = scripts.length; --i >= 0;) {
    +    var script = scripts[i];
    +    var match = script.src.match(
    +        /^[^?#]*\/run_prettify\.js(\?[^#]*)?(?:#.*)?$/);
    +    if (match) {
    +      scriptQuery = match[1] || '';
    +      // Remove the script from the DOM so that multiple runs at least run
    +      // multiple times even if parameter sets are interpreted in reverse
    +      // order.
    +      script.parentNode.removeChild(script);
    +      break;
    +    }
    +  }
    +
    +  // Pull parameters into local variables.
    +  var autorun = true;
    +  var langs = [];
    +  var skins = [];
    +  var callbacks = [];
    +  scriptQuery.replace(
    +      /[?&]([^&=]+)=([^&]+)/g,
    +      function (_, name, value) {
    +        value = decodeURIComponent(value);
    +        name = decodeURIComponent(name);
    +        if (name == 'autorun')   { autorun = !/^[0fn]/i.test(value); } else
    +        if (name == 'lang')      { langs.push(value);                } else
    +        if (name == 'skin')      { skins.push(value);                } else
    +        if (name == 'callback')  { callbacks.push(value);            }
           });
     
    -  /**
    -   * Given a DOM subtree, wraps it in a list, and puts each line into its own
    -   * list item.
    -   *
    -   * @param {Node} node modified in place.  Its content is pulled into an
    -   *     HTMLOListElement, and each line is moved into a separate list item.
    -   *     This requires cloning elements, so the input might not have unique
    -   *     IDs after numbering.
    -   * @param {number|null|boolean} startLineNum
    -   *     If truthy, coerced to an integer which is the 1-indexed line number
    -   *     of the first line of code.  The number of the first line will be
    -   *     attached to the list.
    -   * @param {boolean} isPreformatted true iff white-space in text nodes should
    -   *     be treated as significant.
    -   */
    -  function numberLines(node, startLineNum, isPreformatted) {
    -    var nocode = /(?:^|\s)nocode(?:\s|$)/;
    -    var lineBreak = /\r\n?|\n/;
    -  
    -    var document = node.ownerDocument;
    -  
    -    var li = document.createElement('li');
    -    while (node.firstChild) {
    -      li.appendChild(node.firstChild);
    -    }
    -    // An array of lines.  We split below, so this is initialized to one
    -    // un-split line.
    -    var listItems = [li];
    -  
    -    function walk(node) {
    -      var type = node.nodeType;
    -      if (type == 1 && !nocode.test(node.className)) {  // Element
    -        if ('br' === node.nodeName) {
    -          breakAfter(node);
    -          // Discard the <BR> since it is now flush against a </LI>.
    -          if (node.parentNode) {
    -            node.parentNode.removeChild(node);
    -          }
    -        } else {
    -          for (var child = node.firstChild; child; child = child.nextSibling) {
    -            walk(child);
    -          }
    +  // Use https to avoid mixed content warnings in client pages and to
    +  // prevent a MITM from rewrite prettify mid-flight.
    +  // This only works if this script is loaded via https : something
    +  // over which we exercise no control.
    +  var LOADER_BASE_URL =
    +     'https://cdn.rawgit.com/google/code-prettify/master/loader';
    +
    +  for (var i = 0, n = langs.length; i < n; ++i) (function (lang) {
    +    var script = doc.createElement("script");
    +
    +    // Excerpted from jQuery.ajaxTransport("script") to fire events when
    +    // a script is finished loading.
    +    // Attach handlers for each script
    +    script.onload = script.onerror = script.onreadystatechange = function () {
    +      if (script && (
    +            !script.readyState || /loaded|complete/.test(script.readyState))) {
    +        // Handle memory leak in IE
    +        script.onerror = script.onload = script.onreadystatechange = null;
    +
    +        --pendingLanguages;
    +        checkPendingLanguages();
    +
    +        // Remove the script
    +        if (script.parentNode) {
    +          script.parentNode.removeChild(script);
             }
    -      } else if ((type == 3 || type == 4) && isPreformatted) {  // Text
    -        var text = node.nodeValue;
    -        var match = text.match(lineBreak);
    -        if (match) {
    -          var firstLine = text.substring(0, match.index);
    -          node.nodeValue = firstLine;
    -          var tail = text.substring(match.index + match[0].length);
    -          if (tail) {
    -            var parent = node.parentNode;
    -            parent.insertBefore(
    -              document.createTextNode(tail), node.nextSibling);
    -          }
    -          breakAfter(node);
    -          if (!firstLine) {
    -            // Don't leave blank text nodes in the DOM.
    -            node.parentNode.removeChild(node);
    -          }
    -        }
    -      }
    -    }
    -  
    -    // Split a line after the given node.
    -    function breakAfter(lineEndNode) {
    -      // If there's nothing to the right, then we can skip ending the line
    -      // here, and move root-wards since splitting just before an end-tag
    -      // would require us to create a bunch of empty copies.
    -      while (!lineEndNode.nextSibling) {
    -        lineEndNode = lineEndNode.parentNode;
    -        if (!lineEndNode) { return; }
    -      }
    -  
    -      function breakLeftOf(limit, copy) {
    -        // Clone shallowly if this node needs to be on both sides of the break.
    -        var rightSide = copy ? limit.cloneNode(false) : limit;
    -        var parent = limit.parentNode;
    -        if (parent) {
    -          // We clone the parent chain.
    -          // This helps us resurrect important styling elements that cross lines.
    -          // E.g. in <i>Foo<br>Bar</i>
    -          // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
    -          var parentClone = breakLeftOf(parent, 1);
    -          // Move the clone and everything to the right of the original
    -          // onto the cloned parent.
    -          var next = limit.nextSibling;
    -          parentClone.appendChild(rightSide);
    -          for (var sibling = next; sibling; sibling = next) {
    -            next = sibling.nextSibling;
    -            parentClone.appendChild(sibling);
    -          }
    -        }
    -        return rightSide;
    -      }
    -  
    -      var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
    -  
    -      // Walk the parent chain until we reach an unattached LI.
    -      for (var parent;
    -           // Check nodeType since IE invents document fragments.
    -           (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
    -        copiedListItem = parent;
    -      }
    -      // Put it on the list of lines for later processing.
    -      listItems.push(copiedListItem);
    -    }
    -  
    -    // Split lines while there are lines left to split.
    -    for (var i = 0;  // Number of lines that have been split so far.
    -         i < listItems.length;  // length updated by breakAfter calls.
    -         ++i) {
    -      walk(listItems[i]);
    -    }
    -  
    -    // Make sure numeric indices show correctly.
    -    if (startLineNum === (startLineNum|0)) {
    -      listItems[0].setAttribute('value', startLineNum);
    -    }
    -  
    -    var ol = document.createElement('ol');
    -    ol.className = 'linenums';
    -    var offset = Math.max(0, ((startLineNum - 1 /* zero index */)) | 0) || 0;
    -    for (var i = 0, n = listItems.length; i < n; ++i) {
    -      li = listItems[i];
    -      // Stick a class on the LIs so that stylesheets can
    -      // color odd/even rows, or any other row pattern that
    -      // is co-prime with 10.
    -      li.className = 'L' + ((i + offset) % 10);
    -      if (!li.firstChild) {
    -        li.appendChild(document.createTextNode('\xA0'));
    -      }
    -      ol.appendChild(li);
    -    }
    -  
    -    node.appendChild(ol);
    -  }
     
    -  /**
    -   * Breaks {@code job.sourceCode} around style boundaries in
    -   * {@code job.decorations} and modifies {@code job.sourceNode} in place.
    -   * @param {JobT} job
    -   * @private
    -   */
    -  function recombineTagsAndDecorations(job) {
    -    var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
    -    isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
    -    var newlineRe = /\n/g;
    -  
    -    var source = job.sourceCode;
    -    var sourceLength = source.length;
    -    // Index into source after the last code-unit recombined.
    -    var sourceIndex = 0;
    -  
    -    var spans = job.spans;
    -    var nSpans = spans.length;
    -    // Index into spans after the last span which ends at or before sourceIndex.
    -    var spanIndex = 0;
    -  
    -    var decorations = job.decorations;
    -    var nDecorations = decorations.length;
    -    // Index into decorations after the last decoration which ends at or before
    -    // sourceIndex.
    -    var decorationIndex = 0;
    -  
    -    // Remove all zero-length decorations.
    -    decorations[nDecorations] = sourceLength;
    -    var decPos, i;
    -    for (i = decPos = 0; i < nDecorations;) {
    -      if (decorations[i] !== decorations[i + 2]) {
    -        decorations[decPos++] = decorations[i++];
    -        decorations[decPos++] = decorations[i++];
    -      } else {
    -        i += 2;
    +        script = null;
           }
    -    }
    -    nDecorations = decPos;
    -  
    -    // Simplify decorations.
    -    for (i = decPos = 0; i < nDecorations;) {
    -      var startPos = decorations[i];
    -      // Conflate all adjacent decorations that use the same style.
    -      var startDec = decorations[i + 1];
    -      var end = i + 2;
    -      while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
    -        end += 2;
    -      }
    -      decorations[decPos++] = startPos;
    -      decorations[decPos++] = startDec;
    -      i = end;
    -    }
    -  
    -    nDecorations = decorations.length = decPos;
    -  
    -    var sourceNode = job.sourceNode;
    -    var oldDisplay = "";
    -    if (sourceNode) {
    -      oldDisplay = sourceNode.style.display;
    -      sourceNode.style.display = 'none';
    -    }
    -    try {
    -      var decoration = null;
    -      while (spanIndex < nSpans) {
    -        var spanStart = spans[spanIndex];
    -        var spanEnd = /** @type{number} */ (spans[spanIndex + 2])
    -            || sourceLength;
    -  
    -        var decEnd = decorations[decorationIndex + 2] || sourceLength;
    -  
    -        var end = Math.min(spanEnd, decEnd);
    -  
    -        var textNode = /** @type{Node} */ (spans[spanIndex + 1]);
    -        var styledText;
    -        if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
    -            // Don't introduce spans around empty text nodes.
    -            && (styledText = source.substring(sourceIndex, end))) {
    -          // This may seem bizarre, and it is.  Emitting LF on IE causes the
    -          // code to display with spaces instead of line breaks.
    -          // Emitting Windows standard issue linebreaks (CRLF) causes a blank
    -          // space to appear at the beginning of every line but the first.
    -          // Emitting an old Mac OS 9 line separator makes everything spiffy.
    -          if (isIE8OrEarlier) {
    -            styledText = styledText.replace(newlineRe, '\r');
    -          }
    -          textNode.nodeValue = styledText;
    -          var document = textNode.ownerDocument;
    -          var span = document.createElement('span');
    -          span.className = decorations[decorationIndex + 1];
    -          var parentNode = textNode.parentNode;
    -          parentNode.replaceChild(span, textNode);
    -          span.appendChild(textNode);
    -          if (sourceIndex < spanEnd) {  // Split off a text node.
    -            spans[spanIndex + 1] = textNode
    -                // TODO: Possibly optimize by using '' if there's no flicker.
    -                = document.createTextNode(source.substring(end, spanEnd));
    -            parentNode.insertBefore(textNode, span.nextSibling);
    -          }
    -        }
    -  
    -        sourceIndex = end;
    -  
    -        if (sourceIndex >= spanEnd) {
    -          spanIndex += 2;
    -        }
    -        if (sourceIndex >= decEnd) {
    -          decorationIndex += 2;
    -        }
    -      }
    -    } finally {
    -      if (sourceNode) {
    -        sourceNode.style.display = oldDisplay;
    -      }
    -    }
    -  }
    -
    -  /** Maps language-specific file extensions to handlers. */
    -  var langHandlerRegistry = {};
    -  /** Register a language handler for the given file extensions.
    -    * @param {function (JobT)} handler a function from source code to a list
    -    *      of decorations.  Takes a single argument job which describes the
    -    *      state of the computation and attaches the decorations to it.
    -    * @param {Array.<string>} fileExtensions
    -    */
    -  function registerLangHandler(handler, fileExtensions) {
    -    for (var i = fileExtensions.length; --i >= 0;) {
    -      var ext = fileExtensions[i];
    -      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    -        langHandlerRegistry[ext] = handler;
    -      } else if (win['console']) {
    -        console['warn']('cannot override language handler %s', ext);
    -      }
    -    }
    -  }
    -  function langHandlerForExtension(extension, source) {
    -    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    -      // Treat it as markup if the first non whitespace character is a < and
    -      // the last non-whitespace character is a >.
    -      extension = /^\s*</.test(source)
    -          ? 'default-markup'
    -          : 'default-code';
    -    }
    -    return langHandlerRegistry[extension];
    -  }
    -  registerLangHandler(decorateSource, ['default-code']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [],
    -          [
    -           [PR_PLAIN,       /^[^<?]+/],
    -           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    -           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    -           // Unescaped content in an unknown language
    -           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    -           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    -           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    -           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    -           // Unescaped content in javascript.  (Or possibly vbscript).
    -           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    -           // Contains unescaped stylesheet content
    -           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    -           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    -          ]),
    -      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [
    -           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    -           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    -           ],
    -          [
    -           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    -           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    -           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    -           [PR_PUNCTUATION,  /^[=<>\/]+/],
    -           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    -           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    -           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    -           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    -           ]),
    -      ['in.tag']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CPP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true,
    -          'types': C_TYPES
    -        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': 'null,true,false'
    -        }), ['json']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CSHARP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true,
    -          'verbatimStrings': true,
    -          'types': C_TYPES
    -        }), ['cs']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JAVA_KEYWORDS,
    -          'cStyleComments': true
    -        }), ['java']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': SH_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true
    -        }), ['bash', 'bsh', 'csh', 'sh']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PYTHON_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'tripleQuotedStrings': true
    -        }), ['cv', 'py', 'python']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PERL_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': 2  // multiline regex literals
    -        }), ['perl', 'pl', 'pm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': RUBY_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': true
    -        }), ['rb', 'ruby']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JSCRIPT_KEYWORDS,
    -          'cStyleComments': true,
    -          'regexLiterals': true
    -        }), ['javascript', 'js', 'ts', 'typescript']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': COFFEE_KEYWORDS,
    -          'hashComments': 3,  // ### style block comments
    -          'cStyleComments': true,
    -          'multilineStrings': true,
    -          'tripleQuotedStrings': true,
    -          'regexLiterals': true
    -        }), ['coffee']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    -
    -  /** @param {JobT} job */
    -  function applyDecorator(job) {
    -    var opt_langExtension = job.langExtension;
    -
    -    try {
    -      // Extract tags, and convert the source code to plain text.
    -      var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
    -      /** Plain text. @type {string} */
    -      var source = sourceAndSpans.sourceCode;
    -      job.sourceCode = source;
    -      job.spans = sourceAndSpans.spans;
    -      job.basePos = 0;
    -
    -      // Apply the appropriate language handler
    -      langHandlerForExtension(opt_langExtension, source)(job);
    -
    -      // Integrate the decorations and tags back into the source code,
    -      // modifying the sourceNode in place.
    -      recombineTagsAndDecorations(job);
    -    } catch (e) {
    -      if (win['console']) {
    -        console['log'](e && e['stack'] || e);
    -      }
    -    }
    -  }
    -
    -  /**
    -   * Pretty print a chunk of code.
    -   * @param sourceCodeHtml {string} The HTML to pretty print.
    -   * @param opt_langExtension {string} The language name to use.
    -   *     Typically, a filename extension like 'cpp' or 'java'.
    -   * @param opt_numberLines {number|boolean} True to number lines,
    -   *     or the 1-indexed number of the first line in sourceCodeHtml.
    -   */
    -  function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
    -    /** @type{number|boolean} */
    -    var nl = opt_numberLines || false;
    -    /** @type{string|null} */
    -    var langExtension = opt_langExtension || null;
    -    /** @type{!Element} */
    -    var container = document.createElement('div');
    -    // This could cause images to load and onload listeners to fire.
    -    // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
    -    // We assume that the inner HTML is from a trusted source.
    -    // The pre-tag is required for IE8 which strips newlines from innerHTML
    -    // when it is injected into a <pre> tag.
    -    // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
    -    // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
    -    container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
    -    container = /** @type{!Element} */(container.firstChild);
    -    if (nl) {
    -      numberLines(container, nl, true);
    -    }
    -
    -    /** @type{JobT} */
    -    var job = {
    -      langExtension: langExtension,
    -      numberLines: nl,
    -      sourceNode: container,
    -      pre: 1,
    -      sourceCode: null,
    -      basePos: null,
    -      spans: null,
    -      decorations: null
         };
    -    applyDecorator(job);
    -    return container.innerHTML;
    +
    +    script.type = 'text/javascript';
    +    script.src = LOADER_BASE_URL
    +      + '/lang-' + encodeURIComponent(langs[i]) + '.js';
    +
    +    // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
    +    head.insertBefore(script, head.firstChild);
    +  })(langs[i]);
    +
    +  var pendingLanguages = langs.length;
    +  function checkPendingLanguages() {
    +    if (!pendingLanguages) {
    +      win.setTimeout(onLangsLoaded, 0);
    +    }
       }
     
    -   /**
    -    * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    -    * {@code class=prettyprint} and prettify them.
    -    *
    -    * @param {Function} opt_whenDone called when prettifying is done.
    -    * @param {HTMLElement|HTMLDocument} opt_root an element or document
    -    *   containing all the elements to pretty print.
    -    *   Defaults to {@code document.body}.
    -    */
    -  function $prettyPrint(opt_whenDone, opt_root) {
    -    var root = opt_root || document.body;
    -    var doc = root.ownerDocument || document;
    -    function byTagName(tn) { return root.getElementsByTagName(tn); }
    -    // fetch a list of nodes to rewrite
    -    var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
    -    var elements = [];
    -    for (var i = 0; i < codeSegments.length; ++i) {
    -      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    -        elements.push(codeSegments[i][j]);
    -      }
    -    }
    -    codeSegments = null;
    +  var skinUrls = [];
    +  for (var i = 0, n = skins.length; i < n; ++i) {
    +    skinUrls.push(LOADER_BASE_URL
    +        + '/skins/' + encodeURIComponent(skins[i]) + '.css');
    +  }
    +  skinUrls.push(LOADER_BASE_URL + '/prettify.css');
    +  loadStylesheetsFallingBack(skinUrls);
     
    -    var clock = Date;
    -    if (!clock['now']) {
    -      clock = { 'now': function () { return +(new Date); } };
    -    }
    -
    -    // The loop is broken into a series of continuations to make sure that we
    -    // don't make the browser unresponsive when rewriting a large page.
    -    var k = 0;
    -
    -    var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
    -    var prettyPrintRe = /\bprettyprint\b/;
    -    var prettyPrintedRe = /\bprettyprinted\b/;
    -    var preformattedTagNameRe = /pre|xmp/i;
    -    var codeRe = /^code$/i;
    -    var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
    -    var EMPTY = {};
    -
    -    function doWork() {
    -      var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
    -                     clock['now']() + 250 /* ms */ :
    -                     Infinity);
    -      for (; k < elements.length && clock['now']() < endTime; k++) {
    -        var cs = elements[k];
    -
    -        // Look for a preceding comment like
    -        // <?prettify lang="..." linenums="..."?>
    -        var attrs = EMPTY;
    -        {
    -          for (var preceder = cs; (preceder = preceder.previousSibling);) {
    -            var nt = preceder.nodeType;
    -            // <?foo?> is parsed by HTML 5 to a comment node (8)
    -            // like <!--?foo?-->, but in XML is a processing instruction
    -            var value = (nt === 7 || nt === 8) && preceder.nodeValue;
    -            if (value
    -                ? !/^\??prettify\b/.test(value)
    -                : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
    -              // Skip over white-space text nodes but not others.
    -              break;
    -            }
    -            if (value) {
    -              attrs = {};
    -              value.replace(
    -                  /\b(\w+)=([\w:.%+-]+)/g,
    -                function (_, name, value) { attrs[name] = value; });
    -              break;
    -            }
    +  var prettyPrint = (function () {
    +    /**
    +     * @license
    +     * Copyright (C) 2006 Google Inc.
    +     *
    +     * Licensed under the Apache License, Version 2.0 (the "License");
    +     * you may not use this file except in compliance with the License.
    +     * You may obtain a copy of the License at
    +     *
    +     *      http://www.apache.org/licenses/LICENSE-2.0
    +     *
    +     * Unless required by applicable law or agreed to in writing, software
    +     * distributed under the License is distributed on an "AS IS" BASIS,
    +     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +     * See the License for the specific language governing permissions and
    +     * limitations under the License.
    +     */
    +    
    +    /**
    +     * @fileoverview
    +     * some functions for browser-side pretty printing of code contained in html.
    +     *
    +     * <p>
    +     * For a fairly comprehensive set of languages see the
    +     * <a href="https://github.com/google/code-prettify#for-which-languages-does-it-work">README</a>
    +     * file that came with this source.  At a minimum, the lexer should work on a
    +     * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
    +     * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
    +     * and a subset of Perl, but, because of commenting conventions, doesn't work on
    +     * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
    +     * <p>
    +     * Usage: <ol>
    +     * <li> include this source file in an html page via
    +     *   {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
    +     * <li> define style rules.  See the example page for examples.
    +     * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
    +     *    {@code class=prettyprint.}
    +     *    You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
    +     *    printer needs to do more substantial DOM manipulations to support that, so
    +     *    some css styles may not be preserved.
    +     * </ol>
    +     * That's it.  I wanted to keep the API as simple as possible, so there's no
    +     * need to specify which language the code is in, but if you wish, you can add
    +     * another class to the {@code <pre>} or {@code <code>} element to specify the
    +     * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    +     * starts with "lang-" followed by a file extension, specifies the file type.
    +     * See the "lang-*.js" files in this directory for code that implements
    +     * per-language file handlers.
    +     * <p>
    +     * Change log:<br>
    +     * cbeust, 2006/08/22
    +     * <blockquote>
    +     *   Java annotations (start with "@") are now captured as literals ("lit")
    +     * </blockquote>
    +     * @requires console
    +     */
    +    
    +    // JSLint declarations
    +    /*global console, document, navigator, setTimeout, window, define */
    +    
    +    
    +    var HACK_TO_FIX_JS_INCLUDE_PL;
    +    
    +    /**
    +     * {@type !{
    +     *   'createSimpleLexer': function (Array, Array): (function (JobT)),
    +     *   'registerLangHandler': function (function (JobT), Array.<string>),
    +     *   'PR_ATTRIB_NAME': string,
    +     *   'PR_ATTRIB_NAME': string,
    +     *   'PR_ATTRIB_VALUE': string,
    +     *   'PR_COMMENT': string,
    +     *   'PR_DECLARATION': string,
    +     *   'PR_KEYWORD': string,
    +     *   'PR_LITERAL': string,
    +     *   'PR_NOCODE': string,
    +     *   'PR_PLAIN': string,
    +     *   'PR_PUNCTUATION': string,
    +     *   'PR_SOURCE': string,
    +     *   'PR_STRING': string,
    +     *   'PR_TAG': string,
    +     *   'PR_TYPE': string,
    +     *   'prettyPrintOne': function (string, string, number|boolean),
    +     *   'prettyPrint': function (?function, ?(HTMLElement|HTMLDocument))
    +     * }}
    +     * @const
    +     */
    +    var PR;
    +    
    +    /**
    +     * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    +     * UI events.
    +     * If set to {@code false}, {@code prettyPrint()} is synchronous.
    +     */
    +    window['PR_SHOULD_USE_CONTINUATION'] = true;
    +    
    +    /**
    +     * Pretty print a chunk of code.
    +     * @param {string} sourceCodeHtml The HTML to pretty print.
    +     * @param {string} opt_langExtension The language name to use.
    +     *     Typically, a filename extension like 'cpp' or 'java'.
    +     * @param {number|boolean} opt_numberLines True to number lines,
    +     *     or the 1-indexed number of the first line in sourceCodeHtml.
    +     * @return {string} code as html, but prettier
    +     */
    +    var prettyPrintOne;
    +    /**
    +     * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +     * {@code class=prettyprint} and prettify them.
    +     *
    +     * @param {Function} opt_whenDone called when prettifying is done.
    +     * @param {HTMLElement|HTMLDocument} opt_root an element or document
    +     *   containing all the elements to pretty print.
    +     *   Defaults to {@code document.body}.
    +     */
    +    var prettyPrint;
    +    
    +    
    +    (function () {
    +      var win = window;
    +      // Keyword lists for various languages.
    +      // We use things that coerce to strings to make them compact when minified
    +      // and to defeat aggressive optimizers that fold large string constants.
    +      var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
    +      var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
    +          "double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed," +
    +          "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
    +      var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
    +          "new,operator,private,protected,public,this,throw,true,try,typeof"];
    +      var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignas,alignof,align_union,asm,axiom,bool," +
    +          "concept,concept_map,const_cast,constexpr,decltype,delegate," +
    +          "dynamic_cast,explicit,export,friend,generic,late_check," +
    +          "mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert," +
    +          "static_cast,template,typeid,typename,using,virtual,where"];
    +      var JAVA_KEYWORDS = [COMMON_KEYWORDS,
    +          "abstract,assert,boolean,byte,extends,finally,final,implements,import," +
    +          "instanceof,interface,null,native,package,strictfp,super,synchronized," +
    +          "throws,transient"];
    +      var CSHARP_KEYWORDS = [COMMON_KEYWORDS,
    +          "abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending," +
    +          "dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface," +
    +          "internal,into,is,join,let,lock,null,object,out,override,orderby,params," +
    +          "partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong," +
    +          "unchecked,unsafe,ushort,value,var,virtual,where,yield"];
    +      var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
    +          "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
    +          "throw,true,try,unless,until,when,while,yes";
    +      var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
    +          "abstract,async,await,constructor,debugger,enum,eval,export,function," +
    +          "get,implements,instanceof,interface,let,null,set,undefined,var,with," +
    +          "yield,Infinity,NaN"];
    +      var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
    +          "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
    +          "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
    +      var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
    +          "elif,except,exec,finally,from,global,import,in,is,lambda," +
    +          "nonlocal,not,or,pass,print,raise,try,with,yield," +
    +          "False,True,None"];
    +      var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
    +          "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
    +          "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
    +          "BEGIN,END"];
    +      var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
    +          "function,in,local,set,then,until"];
    +      var ALL_KEYWORDS = [
    +          CPP_KEYWORDS, CSHARP_KEYWORDS, JAVA_KEYWORDS, JSCRIPT_KEYWORDS,
    +          PERL_KEYWORDS, PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
    +      var C_TYPES = /^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
    +    
    +      // token style names.  correspond to css classes
    +      /**
    +       * token style for a string literal
    +       * @const
    +       */
    +      var PR_STRING = 'str';
    +      /**
    +       * token style for a keyword
    +       * @const
    +       */
    +      var PR_KEYWORD = 'kwd';
    +      /**
    +       * token style for a comment
    +       * @const
    +       */
    +      var PR_COMMENT = 'com';
    +      /**
    +       * token style for a type
    +       * @const
    +       */
    +      var PR_TYPE = 'typ';
    +      /**
    +       * token style for a literal value.  e.g. 1, null, true.
    +       * @const
    +       */
    +      var PR_LITERAL = 'lit';
    +      /**
    +       * token style for a punctuation string.
    +       * @const
    +       */
    +      var PR_PUNCTUATION = 'pun';
    +      /**
    +       * token style for plain text.
    +       * @const
    +       */
    +      var PR_PLAIN = 'pln';
    +    
    +      /**
    +       * token style for an sgml tag.
    +       * @const
    +       */
    +      var PR_TAG = 'tag';
    +      /**
    +       * token style for a markup declaration such as a DOCTYPE.
    +       * @const
    +       */
    +      var PR_DECLARATION = 'dec';
    +      /**
    +       * token style for embedded source.
    +       * @const
    +       */
    +      var PR_SOURCE = 'src';
    +      /**
    +       * token style for an sgml attribute name.
    +       * @const
    +       */
    +      var PR_ATTRIB_NAME = 'atn';
    +      /**
    +       * token style for an sgml attribute value.
    +       * @const
    +       */
    +      var PR_ATTRIB_VALUE = 'atv';
    +    
    +      /**
    +       * A class that indicates a section of markup that is not code, e.g. to allow
    +       * embedding of line numbers within code listings.
    +       * @const
    +       */
    +      var PR_NOCODE = 'nocode';
    +    
    +      
    +      
    +      /**
    +       * A set of tokens that can precede a regular expression literal in
    +       * javascript
    +       * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
    +       * has the full list, but I've removed ones that might be problematic when
    +       * seen in languages that don't support regular expression literals.
    +       *
    +       * <p>Specifically, I've removed any keywords that can't precede a regexp
    +       * literal in a syntactically legal javascript program, and I've removed the
    +       * "in" keyword since it's not a keyword in many languages, and might be used
    +       * as a count of inches.
    +       *
    +       * <p>The link above does not accurately describe EcmaScript rules since
    +       * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    +       * very well in practice.
    +       *
    +       * @private
    +       * @const
    +       */
    +      var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
    +      
    +      // CAVEAT: this does not properly handle the case where a regular
    +      // expression immediately follows another since a regular expression may
    +      // have flags for case-sensitivity and the like.  Having regexp tokens
    +      // adjacent is not valid in any language I'm aware of, so I'm punting.
    +      // TODO: maybe style special characters inside a regexp as punctuation.
    +    
    +      /**
    +       * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    +       * matches the union of the sets of strings matched by the input RegExp.
    +       * Since it matches globally, if the input strings have a start-of-input
    +       * anchor (/^.../), it is ignored for the purposes of unioning.
    +       * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    +       * @return {RegExp} a global regex.
    +       */
    +      function combinePrefixPatterns(regexs) {
    +        var capturedGroupIndex = 0;
    +      
    +        var needToFoldCase = false;
    +        var ignoreCase = false;
    +        for (var i = 0, n = regexs.length; i < n; ++i) {
    +          var regex = regexs[i];
    +          if (regex.ignoreCase) {
    +            ignoreCase = true;
    +          } else if (/[a-z]/i.test(regex.source.replace(
    +                         /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    +            needToFoldCase = true;
    +            ignoreCase = false;
    +            break;
               }
             }
    -
    -        var className = cs.className;
    -        if ((attrs !== EMPTY || prettyPrintRe.test(className))
    -            // Don't redo this if we've already done it.
    -            // This allows recalling pretty print to just prettyprint elements
    -            // that have been added to the page since last call.
    -            && !prettyPrintedRe.test(className)) {
    -
    -          // make sure this is not nested in an already prettified element
    -          var nested = false;
    -          for (var p = cs.parentNode; p; p = p.parentNode) {
    -            var tn = p.tagName;
    -            if (preCodeXmpRe.test(tn)
    -                && p.className && prettyPrintRe.test(p.className)) {
    -              nested = true;
    -              break;
    -            }
    +      
    +        var escapeCharToCodeUnit = {
    +          'b': 8,
    +          't': 9,
    +          'n': 0xa,
    +          'v': 0xb,
    +          'f': 0xc,
    +          'r': 0xd
    +        };
    +      
    +        function decodeEscape(charsetPart) {
    +          var cc0 = charsetPart.charCodeAt(0);
    +          if (cc0 !== 92 /* \\ */) {
    +            return cc0;
               }
    -          if (!nested) {
    -            // Mark done.  If we fail to prettyprint for whatever reason,
    -            // we shouldn't try again.
    -            cs.className += ' prettyprinted';
    -
    -            // If the classes includes a language extensions, use it.
    -            // Language extensions can be specified like
    -            //     <pre class="prettyprint lang-cpp">
    -            // the language extension "cpp" is used to find a language handler
    -            // as passed to PR.registerLangHandler.
    -            // HTML5 recommends that a language be specified using "language-"
    -            // as the prefix instead.  Google Code Prettify supports both.
    -            // http://dev.w3.org/html5/spec-author-view/the-code-element.html
    -            var langExtension = attrs['lang'];
    -            if (!langExtension) {
    -              langExtension = className.match(langExtensionRe);
    -              // Support <pre class="prettyprint"><code class="language-c">
    -              var wrapper;
    -              if (!langExtension && (wrapper = childContentWrapper(cs))
    -                  && codeRe.test(wrapper.tagName)) {
    -                langExtension = wrapper.className.match(langExtensionRe);
    -              }
    -
    -              if (langExtension) { langExtension = langExtension[1]; }
    -            }
    -
    -            var preformatted;
    -            if (preformattedTagNameRe.test(cs.tagName)) {
    -              preformatted = 1;
    +          var c1 = charsetPart.charAt(1);
    +          cc0 = escapeCharToCodeUnit[c1];
    +          if (cc0) {
    +            return cc0;
    +          } else if ('0' <= c1 && c1 <= '7') {
    +            return parseInt(charsetPart.substring(1), 8);
    +          } else if (c1 === 'u' || c1 === 'x') {
    +            return parseInt(charsetPart.substring(2), 16);
    +          } else {
    +            return charsetPart.charCodeAt(1);
    +          }
    +        }
    +      
    +        function encodeEscape(charCode) {
    +          if (charCode < 0x20) {
    +            return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    +          }
    +          var ch = String.fromCharCode(charCode);
    +          return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
    +              ? "\\" + ch : ch;
    +        }
    +      
    +        function caseFoldCharset(charSet) {
    +          var charsetParts = charSet.substring(1, charSet.length - 1).match(
    +              new RegExp(
    +                  '\\\\u[0-9A-Fa-f]{4}'
    +                  + '|\\\\x[0-9A-Fa-f]{2}'
    +                  + '|\\\\[0-3][0-7]{0,2}'
    +                  + '|\\\\[0-7]{1,2}'
    +                  + '|\\\\[\\s\\S]'
    +                  + '|-'
    +                  + '|[^-\\\\]',
    +                  'g'));
    +          var ranges = [];
    +          var inverse = charsetParts[0] === '^';
    +      
    +          var out = ['['];
    +          if (inverse) { out.push('^'); }
    +      
    +          for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    +            var p = charsetParts[i];
    +            if (/\\[bdsw]/i.test(p)) {  // Don't muck with named groups.
    +              out.push(p);
                 } else {
    -              var currentStyle = cs['currentStyle'];
    -              var defaultView = doc.defaultView;
    -              var whitespace = (
    -                  currentStyle
    -                  ? currentStyle['whiteSpace']
    -                  : (defaultView
    -                     && defaultView.getComputedStyle)
    -                  ? defaultView.getComputedStyle(cs, null)
    -                  .getPropertyValue('white-space')
    -                  : 0);
    -              preformatted = whitespace
    -                  && 'pre' === whitespace.substring(0, 3);
    +              var start = decodeEscape(p);
    +              var end;
    +              if (i + 2 < n && '-' === charsetParts[i + 1]) {
    +                end = decodeEscape(charsetParts[i + 2]);
    +                i += 2;
    +              } else {
    +                end = start;
    +              }
    +              ranges.push([start, end]);
    +              // If the range might intersect letters, then expand it.
    +              // This case handling is too simplistic.
    +              // It does not deal with non-latin case folding.
    +              // It works for latin source code identifiers though.
    +              if (!(end < 65 || start > 122)) {
    +                if (!(end < 65 || start > 90)) {
    +                  ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    +                }
    +                if (!(end < 97 || start > 122)) {
    +                  ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    +                }
    +              }
                 }
    -
    -            // Look for a class like linenums or linenums:<n> where <n> is the
    -            // 1-indexed number of the first line.
    -            var lineNums = attrs['linenums'];
    -            if (!(lineNums = lineNums === 'true' || +lineNums)) {
    -              lineNums = className.match(/\blinenums\b(?::(\d+))?/);
    -              lineNums =
    -                lineNums
    -                ? lineNums[1] && lineNums[1].length
    -                  ? +lineNums[1] : true
    -                : false;
    +          }
    +      
    +          // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    +          // -> [[1, 12], [14, 14], [16, 17]]
    +          ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    +          var consolidatedRanges = [];
    +          var lastRange = [];
    +          for (var i = 0; i < ranges.length; ++i) {
    +            var range = ranges[i];
    +            if (range[0] <= lastRange[1] + 1) {
    +              lastRange[1] = Math.max(lastRange[1], range[1]);
    +            } else {
    +              consolidatedRanges.push(lastRange = range);
                 }
    -            if (lineNums) { numberLines(cs, lineNums, preformatted); }
    -
    -            // do the pretty printing
    -            var prettyPrintingJob = {
    -              langExtension: langExtension,
    -              sourceNode: cs,
    -              numberLines: lineNums,
    -              pre: preformatted,
    -              sourceCode: null,
    -              basePos: null,
    -              spans: null,
    -              decorations: null
    -            };
    -            applyDecorator(prettyPrintingJob);
    +          }
    +      
    +          for (var i = 0; i < consolidatedRanges.length; ++i) {
    +            var range = consolidatedRanges[i];
    +            out.push(encodeEscape(range[0]));
    +            if (range[1] > range[0]) {
    +              if (range[1] + 1 > range[0]) { out.push('-'); }
    +              out.push(encodeEscape(range[1]));
    +            }
    +          }
    +          out.push(']');
    +          return out.join('');
    +        }
    +      
    +        function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    +          // Split into character sets, escape sequences, punctuation strings
    +          // like ('(', '(?:', ')', '^'), and runs of characters that do not
    +          // include any of the above.
    +          var parts = regex.source.match(
    +              new RegExp(
    +                  '(?:'
    +                  + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    +                  + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    +                  + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    +                  + '|\\\\[0-9]+'  // a back-reference or octal escape
    +                  + '|\\\\[^ux0-9]'  // other escape sequence
    +                  + '|\\(\\?[:!=]'  // start of a non-capturing group
    +                  + '|[\\(\\)\\^]'  // start/end of a group, or line start
    +                  + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    +                  + ')',
    +                  'g'));
    +          var n = parts.length;
    +      
    +          // Maps captured group numbers to the number they will occupy in
    +          // the output or to -1 if that has not been determined, or to
    +          // undefined if they need not be capturing in the output.
    +          var capturedGroups = [];
    +      
    +          // Walk over and identify back references to build the capturedGroups
    +          // mapping.
    +          for (var i = 0, groupIndex = 0; i < n; ++i) {
    +            var p = parts[i];
    +            if (p === '(') {
    +              // groups are 1-indexed, so max group index is count of '('
    +              ++groupIndex;
    +            } else if ('\\' === p.charAt(0)) {
    +              var decimalValue = +p.substring(1);
    +              if (decimalValue) {
    +                if (decimalValue <= groupIndex) {
    +                  capturedGroups[decimalValue] = -1;
    +                } else {
    +                  // Replace with an unambiguous escape sequence so that
    +                  // an octal escape sequence does not turn into a backreference
    +                  // to a capturing group from an earlier regex.
    +                  parts[i] = encodeEscape(decimalValue);
    +                }
    +              }
    +            }
    +          }
    +      
    +          // Renumber groups and reduce capturing groups to non-capturing groups
    +          // where possible.
    +          for (var i = 1; i < capturedGroups.length; ++i) {
    +            if (-1 === capturedGroups[i]) {
    +              capturedGroups[i] = ++capturedGroupIndex;
    +            }
    +          }
    +          for (var i = 0, groupIndex = 0; i < n; ++i) {
    +            var p = parts[i];
    +            if (p === '(') {
    +              ++groupIndex;
    +              if (!capturedGroups[groupIndex]) {
    +                parts[i] = '(?:';
    +              }
    +            } else if ('\\' === p.charAt(0)) {
    +              var decimalValue = +p.substring(1);
    +              if (decimalValue && decimalValue <= groupIndex) {
    +                parts[i] = '\\' + capturedGroups[decimalValue];
    +              }
    +            }
    +          }
    +      
    +          // Remove any prefix anchors so that the output will match anywhere.
    +          // ^^ really does mean an anchored match though.
    +          for (var i = 0; i < n; ++i) {
    +            if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    +          }
    +      
    +          // Expand letters to groups to handle mixing of case-sensitive and
    +          // case-insensitive patterns if necessary.
    +          if (regex.ignoreCase && needToFoldCase) {
    +            for (var i = 0; i < n; ++i) {
    +              var p = parts[i];
    +              var ch0 = p.charAt(0);
    +              if (p.length >= 2 && ch0 === '[') {
    +                parts[i] = caseFoldCharset(p);
    +              } else if (ch0 !== '\\') {
    +                // TODO: handle letters in numeric escapes.
    +                parts[i] = p.replace(
    +                    /[a-zA-Z]/g,
    +                    function (ch) {
    +                      var cc = ch.charCodeAt(0);
    +                      return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    +                    });
    +              }
    +            }
    +          }
    +      
    +          return parts.join('');
    +        }
    +      
    +        var rewritten = [];
    +        for (var i = 0, n = regexs.length; i < n; ++i) {
    +          var regex = regexs[i];
    +          if (regex.global || regex.multiline) { throw new Error('' + regex); }
    +          rewritten.push(
    +              '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    +        }
    +      
    +        return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    +      }
    +    
    +      /**
    +       * Split markup into a string of source code and an array mapping ranges in
    +       * that string to the text nodes in which they appear.
    +       *
    +       * <p>
    +       * The HTML DOM structure:</p>
    +       * <pre>
    +       * (Element   "p"
    +       *   (Element "b"
    +       *     (Text  "print "))       ; #1
    +       *   (Text    "'Hello '")      ; #2
    +       *   (Element "br")            ; #3
    +       *   (Text    "  + 'World';")) ; #4
    +       * </pre>
    +       * <p>
    +       * corresponds to the HTML
    +       * {@code <p><b>print </b>'Hello '<br>  + 'World';</p>}.</p>
    +       *
    +       * <p>
    +       * It will produce the output:</p>
    +       * <pre>
    +       * {
    +       *   sourceCode: "print 'Hello '\n  + 'World';",
    +       *   //                     1          2
    +       *   //           012345678901234 5678901234567
    +       *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
    +       * }
    +       * </pre>
    +       * <p>
    +       * where #1 is a reference to the {@code "print "} text node above, and so
    +       * on for the other text nodes.
    +       * </p>
    +       *
    +       * <p>
    +       * The {@code} spans array is an array of pairs.  Even elements are the start
    +       * indices of substrings, and odd elements are the text nodes (or BR elements)
    +       * that contain the text for those substrings.
    +       * Substrings continue until the next index or the end of the source.
    +       * </p>
    +       *
    +       * @param {Node} node an HTML DOM subtree containing source-code.
    +       * @param {boolean|number} isPreformatted truthy if white-space in
    +       *    text nodes should be considered significant.
    +       * @return {SourceSpansT} source code and the nodes in which they occur.
    +       */
    +      function extractSourceSpans(node, isPreformatted) {
    +        var nocode = /(?:^|\s)nocode(?:\s|$)/;
    +      
    +        var chunks = [];
    +        var length = 0;
    +        var spans = [];
    +        var k = 0;
    +      
    +        function walk(node) {
    +          var type = node.nodeType;
    +          if (type == 1) {  // Element
    +            if (nocode.test(node.className)) { return; }
    +            for (var child = node.firstChild; child; child = child.nextSibling) {
    +              walk(child);
    +            }
    +            var nodeName = node.nodeName.toLowerCase();
    +            if ('br' === nodeName || 'li' === nodeName) {
    +              chunks[k] = '\n';
    +              spans[k << 1] = length++;
    +              spans[(k++ << 1) | 1] = node;
    +            }
    +          } else if (type == 3 || type == 4) {  // Text
    +            var text = node.nodeValue;
    +            if (text.length) {
    +              if (!isPreformatted) {
    +                text = text.replace(/[ \t\r\n]+/g, ' ');
    +              } else {
    +                text = text.replace(/\r\n?/g, '\n');  // Normalize newlines.
    +              }
    +              // TODO: handle tabs here?
    +              chunks[k] = text;
    +              spans[k << 1] = length;
    +              length += text.length;
    +              spans[(k++ << 1) | 1] = node;
    +            }
    +          }
    +        }
    +      
    +        walk(node);
    +      
    +        return {
    +          sourceCode: chunks.join('').replace(/\n$/, ''),
    +          spans: spans
    +        };
    +      }
    +    
    +      /**
    +       * Apply the given language handler to sourceCode and add the resulting
    +       * decorations to out.
    +       * @param {!Element} sourceNode
    +       * @param {number} basePos the index of sourceCode within the chunk of source
    +       *    whose decorations are already present on out.
    +       * @param {string} sourceCode
    +       * @param {function(JobT)} langHandler
    +       * @param {DecorationsT} out
    +       */
    +      function appendDecorations(
    +          sourceNode, basePos, sourceCode, langHandler, out) {
    +        if (!sourceCode) { return; }
    +        /** @type {JobT} */
    +        var job = {
    +          sourceNode: sourceNode,
    +          pre: 1,
    +          langExtension: null,
    +          numberLines: null,
    +          sourceCode: sourceCode,
    +          spans: null,
    +          basePos: basePos,
    +          decorations: null
    +        };
    +        langHandler(job);
    +        out.push.apply(out, job.decorations);
    +      }
    +    
    +      var notWs = /\S/;
    +    
    +      /**
    +       * Given an element, if it contains only one child element and any text nodes
    +       * it contains contain only space characters, return the sole child element.
    +       * Otherwise returns undefined.
    +       * <p>
    +       * This is meant to return the CODE element in {@code <pre><code ...>} when
    +       * there is a single child element that contains all the non-space textual
    +       * content, but not to return anything where there are multiple child elements
    +       * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
    +       * is textual content.
    +       */
    +      function childContentWrapper(element) {
    +        var wrapper = undefined;
    +        for (var c = element.firstChild; c; c = c.nextSibling) {
    +          var type = c.nodeType;
    +          wrapper = (type === 1)  // Element Node
    +              ? (wrapper ? element : c)
    +              : (type === 3)  // Text Node
    +              ? (notWs.test(c.nodeValue) ? element : wrapper)
    +              : wrapper;
    +        }
    +        return wrapper === element ? undefined : wrapper;
    +      }
    +    
    +      /** Given triples of [style, pattern, context] returns a lexing function,
    +        * The lexing function interprets the patterns to find token boundaries and
    +        * returns a decoration list of the form
    +        * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    +        * where index_n is an index into the sourceCode, and style_n is a style
    +        * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    +        * all characters in sourceCode[index_n-1:index_n].
    +        *
    +        * The stylePatterns is a list whose elements have the form
    +        * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    +        *
    +        * Style is a style constant like PR_PLAIN, or can be a string of the
    +        * form 'lang-FOO', where FOO is a language extension describing the
    +        * language of the portion of the token in $1 after pattern executes.
    +        * E.g., if style is 'lang-lisp', and group 1 contains the text
    +        * '(hello (world))', then that portion of the token will be passed to the
    +        * registered lisp handler for formatting.
    +        * The text before and after group 1 will be restyled using this decorator
    +        * so decorators should take care that this doesn't result in infinite
    +        * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    +        * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    +        * '<script>foo()<\/script>', which would cause the current decorator to
    +        * be called with '<script>' which would not match the same rule since
    +        * group 1 must not be empty, so it would be instead styled as PR_TAG by
    +        * the generic tag rule.  The handler registered for the 'js' extension would
    +        * then be called with 'foo()', and finally, the current decorator would
    +        * be called with '<\/script>' which would not match the original rule and
    +        * so the generic tag rule would identify it as a tag.
    +        *
    +        * Pattern must only match prefixes, and if it matches a prefix, then that
    +        * match is considered a token with the same style.
    +        *
    +        * Context is applied to the last non-whitespace, non-comment token
    +        * recognized.
    +        *
    +        * Shortcut is an optional string of characters, any of which, if the first
    +        * character, gurantee that this pattern and only this pattern matches.
    +        *
    +        * @param {Array} shortcutStylePatterns patterns that always start with
    +        *   a known character.  Must have a shortcut string.
    +        * @param {Array} fallthroughStylePatterns patterns that will be tried in
    +        *   order if the shortcut ones fail.  May have shortcuts.
    +        *
    +        * @return {function (JobT)} a function that takes an undecorated job and
    +        *   attaches a list of decorations.
    +        */
    +      function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    +        var shortcuts = {};
    +        var tokenizer;
    +        (function () {
    +          var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    +          var allRegexs = [];
    +          var regexKeys = {};
    +          for (var i = 0, n = allPatterns.length; i < n; ++i) {
    +            var patternParts = allPatterns[i];
    +            var shortcutChars = patternParts[3];
    +            if (shortcutChars) {
    +              for (var c = shortcutChars.length; --c >= 0;) {
    +                shortcuts[shortcutChars.charAt(c)] = patternParts;
    +              }
    +            }
    +            var regex = patternParts[1];
    +            var k = '' + regex;
    +            if (!regexKeys.hasOwnProperty(k)) {
    +              allRegexs.push(regex);
    +              regexKeys[k] = null;
    +            }
    +          }
    +          allRegexs.push(/[\0-\uffff]/);
    +          tokenizer = combinePrefixPatterns(allRegexs);
    +        })();
    +    
    +        var nPatterns = fallthroughStylePatterns.length;
    +    
    +        /**
    +         * Lexes job.sourceCode and attaches an output array job.decorations of
    +         * style classes preceded by the position at which they start in
    +         * job.sourceCode in order.
    +         *
    +         * @type{function (JobT)}
    +         */
    +        var decorate = function (job) {
    +          var sourceCode = job.sourceCode, basePos = job.basePos;
    +          var sourceNode = job.sourceNode;
    +          /** Even entries are positions in source in ascending order.  Odd enties
    +            * are style markers (e.g., PR_COMMENT) that run from that position until
    +            * the end.
    +            * @type {DecorationsT}
    +            */
    +          var decorations = [basePos, PR_PLAIN];
    +          var pos = 0;  // index into sourceCode
    +          var tokens = sourceCode.match(tokenizer) || [];
    +          var styleCache = {};
    +    
    +          for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    +            var token = tokens[ti];
    +            var style = styleCache[token];
    +            var match = void 0;
    +    
    +            var isEmbedded;
    +            if (typeof style === 'string') {
    +              isEmbedded = false;
    +            } else {
    +              var patternParts = shortcuts[token.charAt(0)];
    +              if (patternParts) {
    +                match = token.match(patternParts[1]);
    +                style = patternParts[0];
    +              } else {
    +                for (var i = 0; i < nPatterns; ++i) {
    +                  patternParts = fallthroughStylePatterns[i];
    +                  match = token.match(patternParts[1]);
    +                  if (match) {
    +                    style = patternParts[0];
    +                    break;
    +                  }
    +                }
    +    
    +                if (!match) {  // make sure that we make progress
    +                  style = PR_PLAIN;
    +                }
    +              }
    +    
    +              isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    +              if (isEmbedded && !(match && typeof match[1] === 'string')) {
    +                isEmbedded = false;
    +                style = PR_SOURCE;
    +              }
    +    
    +              if (!isEmbedded) { styleCache[token] = style; }
    +            }
    +    
    +            var tokenStart = pos;
    +            pos += token.length;
    +    
    +            if (!isEmbedded) {
    +              decorations.push(basePos + tokenStart, style);
    +            } else {  // Treat group 1 as an embedded block of source code.
    +              var embeddedSource = match[1];
    +              var embeddedSourceStart = token.indexOf(embeddedSource);
    +              var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    +              if (match[2]) {
    +                // If embeddedSource can be blank, then it would match at the
    +                // beginning which would cause us to infinitely recurse on the
    +                // entire token, so we catch the right context in match[2].
    +                embeddedSourceEnd = token.length - match[2].length;
    +                embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    +              }
    +              var lang = style.substring(5);
    +              // Decorate the left of the embedded source
    +              appendDecorations(
    +                  sourceNode,
    +                  basePos + tokenStart,
    +                  token.substring(0, embeddedSourceStart),
    +                  decorate, decorations);
    +              // Decorate the embedded source
    +              appendDecorations(
    +                  sourceNode,
    +                  basePos + tokenStart + embeddedSourceStart,
    +                  embeddedSource,
    +                  langHandlerForExtension(lang, embeddedSource),
    +                  decorations);
    +              // Decorate the right of the embedded section
    +              appendDecorations(
    +                  sourceNode,
    +                  basePos + tokenStart + embeddedSourceEnd,
    +                  token.substring(embeddedSourceEnd),
    +                  decorate, decorations);
    +            }
    +          }
    +          job.decorations = decorations;
    +        };
    +        return decorate;
    +      }
    +    
    +      /** returns a function that produces a list of decorations from source text.
    +        *
    +        * This code treats ", ', and ` as string delimiters, and \ as a string
    +        * escape.  It does not recognize perl's qq() style strings.
    +        * It has no special handling for double delimiter escapes as in basic, or
    +        * the tripled delimiters used in python, but should work on those regardless
    +        * although in those cases a single string literal may be broken up into
    +        * multiple adjacent string literals.
    +        *
    +        * It recognizes C, C++, and shell style comments.
    +        *
    +        * @param {Object} options a set of optional parameters.
    +        * @return {function (JobT)} a function that examines the source code
    +        *     in the input job and builds a decoration list which it attaches to
    +        *     the job.
    +        */
    +      function sourceDecorator(options) {
    +        var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    +        if (options['tripleQuotedStrings']) {
    +          // '''multi-line-string''', 'single-line-string', and double-quoted
    +          shortcutStylePatterns.push(
    +              [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    +               null, '\'"']);
    +        } else if (options['multiLineStrings']) {
    +          // 'multi-line-string', "multi-line-string"
    +          shortcutStylePatterns.push(
    +              [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    +               null, '\'"`']);
    +        } else {
    +          // 'single-line-string', "single-line-string"
    +          shortcutStylePatterns.push(
    +              [PR_STRING,
    +               /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    +               null, '"\'']);
    +        }
    +        if (options['verbatimStrings']) {
    +          // verbatim-string-literal production from the C# grammar.  See issue 93.
    +          fallthroughStylePatterns.push(
    +              [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    +        }
    +        var hc = options['hashComments'];
    +        if (hc) {
    +          if (options['cStyleComments']) {
    +            if (hc > 1) {  // multiline hash comments
    +              shortcutStylePatterns.push(
    +                  [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
    +            } else {
    +              // Stop C preprocessor declarations at an unclosed open comment
    +              shortcutStylePatterns.push(
    +                  [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
    +                   null, '#']);
    +            }
    +            // #include <stdio.h>
    +            fallthroughStylePatterns.push(
    +                [PR_STRING,
    +                 /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
    +                 null]);
    +          } else {
    +            shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    +          }
    +        }
    +        if (options['cStyleComments']) {
    +          fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    +          fallthroughStylePatterns.push(
    +              [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    +        }
    +        var regexLiterals = options['regexLiterals'];
    +        if (regexLiterals) {
    +          /**
    +           * @const
    +           */
    +          var regexExcls = regexLiterals > 1
    +            ? ''  // Multiline regex literals
    +            : '\n\r';
    +          /**
    +           * @const
    +           */
    +          var regexAny = regexExcls ? '.' : '[\\S\\s]';
    +          /**
    +           * @const
    +           */
    +          var REGEX_LITERAL = (
    +              // A regular expression literal starts with a slash that is
    +              // not followed by * or / so that it is not confused with
    +              // comments.
    +              '/(?=[^/*' + regexExcls + '])'
    +              // and then contains any number of raw characters,
    +              + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
    +              // escape sequences (\x5C),
    +              +    '|\\x5C' + regexAny
    +              // or non-nesting character sets (\x5B\x5D);
    +              +    '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
    +              +             '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
    +              // finally closed by a /.
    +              + '/');
    +          fallthroughStylePatterns.push(
    +              ['lang-regex',
    +               RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    +               ]);
    +        }
    +    
    +        var types = options['types'];
    +        if (types) {
    +          fallthroughStylePatterns.push([PR_TYPE, types]);
    +        }
    +    
    +        var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
    +        if (keywords.length) {
    +          fallthroughStylePatterns.push(
    +              [PR_KEYWORD,
    +               new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
    +               null]);
    +        }
    +    
    +        shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    +    
    +        var punctuation =
    +          // The Bash man page says
    +    
    +          // A word is a sequence of characters considered as a single
    +          // unit by GRUB. Words are separated by metacharacters,
    +          // which are the following plus space, tab, and newline: { }
    +          // | & $ ; < >
    +          // ...
    +    
    +          // A word beginning with # causes that word and all remaining
    +          // characters on that line to be ignored.
    +    
    +          // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
    +          // comment but empirically
    +          // $ echo {#}
    +          // {#}
    +          // $ echo \$#
    +          // $#
    +          // $ echo }#
    +          // }#
    +    
    +          // so /(?:^|[|&;<>\s])/ is more appropriate.
    +    
    +          // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
    +          // suggests that this definition is compatible with a
    +          // default mode that tries to use a single token definition
    +          // to recognize both bash/python style comments and C
    +          // preprocessor directives.
    +    
    +          // This definition of punctuation does not include # in the list of
    +          // follow-on exclusions, so # will not be broken before if preceeded
    +          // by a punctuation character.  We could try to exclude # after
    +          // [|&;<>] but that doesn't seem to cause many major problems.
    +          // If that does turn out to be a problem, we should change the below
    +          // when hc is truthy to include # in the run of punctuation characters
    +          // only when not followint [|&;<>].
    +          '^.[^\\s\\w.$@\'"`/\\\\]*';
    +        if (options['regexLiterals']) {
    +          punctuation += '(?!\s*\/)';
    +        }
    +    
    +        fallthroughStylePatterns.push(
    +            // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    +            [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    +            [PR_TYPE,        /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
    +            [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    +            [PR_LITERAL,
    +             new RegExp(
    +                 '^(?:'
    +                 // A hex number
    +                 + '0x[a-f0-9]+'
    +                 // or an octal or decimal number,
    +                 + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    +                 // possibly in scientific notation
    +                 + '(?:e[+\\-]?\\d+)?'
    +                 + ')'
    +                 // with an optional modifier like UL for unsigned long
    +                 + '[a-z]*', 'i'),
    +             null, '0123456789'],
    +            // Don't treat escaped quotes in bash as starting strings.
    +            // See issue 144.
    +            [PR_PLAIN,       /^\\[\s\S]?/, null],
    +            [PR_PUNCTUATION, new RegExp(punctuation), null]);
    +    
    +        return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    +      }
    +    
    +      var decorateSource = sourceDecorator({
    +            'keywords': ALL_KEYWORDS,
    +            'hashComments': true,
    +            'cStyleComments': true,
    +            'multiLineStrings': true,
    +            'regexLiterals': true
    +          });
    +    
    +      /**
    +       * Given a DOM subtree, wraps it in a list, and puts each line into its own
    +       * list item.
    +       *
    +       * @param {Node} node modified in place.  Its content is pulled into an
    +       *     HTMLOListElement, and each line is moved into a separate list item.
    +       *     This requires cloning elements, so the input might not have unique
    +       *     IDs after numbering.
    +       * @param {number|null|boolean} startLineNum
    +       *     If truthy, coerced to an integer which is the 1-indexed line number
    +       *     of the first line of code.  The number of the first line will be
    +       *     attached to the list.
    +       * @param {boolean} isPreformatted true iff white-space in text nodes should
    +       *     be treated as significant.
    +       */
    +      function numberLines(node, startLineNum, isPreformatted) {
    +        var nocode = /(?:^|\s)nocode(?:\s|$)/;
    +        var lineBreak = /\r\n?|\n/;
    +      
    +        var document = node.ownerDocument;
    +      
    +        var li = document.createElement('li');
    +        while (node.firstChild) {
    +          li.appendChild(node.firstChild);
    +        }
    +        // An array of lines.  We split below, so this is initialized to one
    +        // un-split line.
    +        var listItems = [li];
    +      
    +        function walk(node) {
    +          var type = node.nodeType;
    +          if (type == 1 && !nocode.test(node.className)) {  // Element
    +            if ('br' === node.nodeName) {
    +              breakAfter(node);
    +              // Discard the <BR> since it is now flush against a </LI>.
    +              if (node.parentNode) {
    +                node.parentNode.removeChild(node);
    +              }
    +            } else {
    +              for (var child = node.firstChild; child; child = child.nextSibling) {
    +                walk(child);
    +              }
    +            }
    +          } else if ((type == 3 || type == 4) && isPreformatted) {  // Text
    +            var text = node.nodeValue;
    +            var match = text.match(lineBreak);
    +            if (match) {
    +              var firstLine = text.substring(0, match.index);
    +              node.nodeValue = firstLine;
    +              var tail = text.substring(match.index + match[0].length);
    +              if (tail) {
    +                var parent = node.parentNode;
    +                parent.insertBefore(
    +                  document.createTextNode(tail), node.nextSibling);
    +              }
    +              breakAfter(node);
    +              if (!firstLine) {
    +                // Don't leave blank text nodes in the DOM.
    +                node.parentNode.removeChild(node);
    +              }
    +            }
    +          }
    +        }
    +      
    +        // Split a line after the given node.
    +        function breakAfter(lineEndNode) {
    +          // If there's nothing to the right, then we can skip ending the line
    +          // here, and move root-wards since splitting just before an end-tag
    +          // would require us to create a bunch of empty copies.
    +          while (!lineEndNode.nextSibling) {
    +            lineEndNode = lineEndNode.parentNode;
    +            if (!lineEndNode) { return; }
    +          }
    +      
    +          function breakLeftOf(limit, copy) {
    +            // Clone shallowly if this node needs to be on both sides of the break.
    +            var rightSide = copy ? limit.cloneNode(false) : limit;
    +            var parent = limit.parentNode;
    +            if (parent) {
    +              // We clone the parent chain.
    +              // This helps us resurrect important styling elements that cross lines.
    +              // E.g. in <i>Foo<br>Bar</i>
    +              // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
    +              var parentClone = breakLeftOf(parent, 1);
    +              // Move the clone and everything to the right of the original
    +              // onto the cloned parent.
    +              var next = limit.nextSibling;
    +              parentClone.appendChild(rightSide);
    +              for (var sibling = next; sibling; sibling = next) {
    +                next = sibling.nextSibling;
    +                parentClone.appendChild(sibling);
    +              }
    +            }
    +            return rightSide;
    +          }
    +      
    +          var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
    +      
    +          // Walk the parent chain until we reach an unattached LI.
    +          for (var parent;
    +               // Check nodeType since IE invents document fragments.
    +               (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
    +            copiedListItem = parent;
    +          }
    +          // Put it on the list of lines for later processing.
    +          listItems.push(copiedListItem);
    +        }
    +      
    +        // Split lines while there are lines left to split.
    +        for (var i = 0;  // Number of lines that have been split so far.
    +             i < listItems.length;  // length updated by breakAfter calls.
    +             ++i) {
    +          walk(listItems[i]);
    +        }
    +      
    +        // Make sure numeric indices show correctly.
    +        if (startLineNum === (startLineNum|0)) {
    +          listItems[0].setAttribute('value', startLineNum);
    +        }
    +      
    +        var ol = document.createElement('ol');
    +        ol.className = 'linenums';
    +        var offset = Math.max(0, ((startLineNum - 1 /* zero index */)) | 0) || 0;
    +        for (var i = 0, n = listItems.length; i < n; ++i) {
    +          li = listItems[i];
    +          // Stick a class on the LIs so that stylesheets can
    +          // color odd/even rows, or any other row pattern that
    +          // is co-prime with 10.
    +          li.className = 'L' + ((i + offset) % 10);
    +          if (!li.firstChild) {
    +            li.appendChild(document.createTextNode('\xA0'));
    +          }
    +          ol.appendChild(li);
    +        }
    +      
    +        node.appendChild(ol);
    +      }
    +    
    +      /**
    +       * Breaks {@code job.sourceCode} around style boundaries in
    +       * {@code job.decorations} and modifies {@code job.sourceNode} in place.
    +       * @param {JobT} job
    +       * @private
    +       */
    +      function recombineTagsAndDecorations(job) {
    +        var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
    +        isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
    +        var newlineRe = /\n/g;
    +      
    +        var source = job.sourceCode;
    +        var sourceLength = source.length;
    +        // Index into source after the last code-unit recombined.
    +        var sourceIndex = 0;
    +      
    +        var spans = job.spans;
    +        var nSpans = spans.length;
    +        // Index into spans after the last span which ends at or before sourceIndex.
    +        var spanIndex = 0;
    +      
    +        var decorations = job.decorations;
    +        var nDecorations = decorations.length;
    +        // Index into decorations after the last decoration which ends at or before
    +        // sourceIndex.
    +        var decorationIndex = 0;
    +      
    +        // Remove all zero-length decorations.
    +        decorations[nDecorations] = sourceLength;
    +        var decPos, i;
    +        for (i = decPos = 0; i < nDecorations;) {
    +          if (decorations[i] !== decorations[i + 2]) {
    +            decorations[decPos++] = decorations[i++];
    +            decorations[decPos++] = decorations[i++];
    +          } else {
    +            i += 2;
    +          }
    +        }
    +        nDecorations = decPos;
    +      
    +        // Simplify decorations.
    +        for (i = decPos = 0; i < nDecorations;) {
    +          var startPos = decorations[i];
    +          // Conflate all adjacent decorations that use the same style.
    +          var startDec = decorations[i + 1];
    +          var end = i + 2;
    +          while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
    +            end += 2;
    +          }
    +          decorations[decPos++] = startPos;
    +          decorations[decPos++] = startDec;
    +          i = end;
    +        }
    +      
    +        nDecorations = decorations.length = decPos;
    +      
    +        var sourceNode = job.sourceNode;
    +        var oldDisplay = "";
    +        if (sourceNode) {
    +          oldDisplay = sourceNode.style.display;
    +          sourceNode.style.display = 'none';
    +        }
    +        try {
    +          var decoration = null;
    +          while (spanIndex < nSpans) {
    +            var spanStart = spans[spanIndex];
    +            var spanEnd = /** @type{number} */ (spans[spanIndex + 2])
    +                || sourceLength;
    +      
    +            var decEnd = decorations[decorationIndex + 2] || sourceLength;
    +      
    +            var end = Math.min(spanEnd, decEnd);
    +      
    +            var textNode = /** @type{Node} */ (spans[spanIndex + 1]);
    +            var styledText;
    +            if (textNode.nodeType !== 1  // Don't muck with <BR>s or <LI>s
    +                // Don't introduce spans around empty text nodes.
    +                && (styledText = source.substring(sourceIndex, end))) {
    +              // This may seem bizarre, and it is.  Emitting LF on IE causes the
    +              // code to display with spaces instead of line breaks.
    +              // Emitting Windows standard issue linebreaks (CRLF) causes a blank
    +              // space to appear at the beginning of every line but the first.
    +              // Emitting an old Mac OS 9 line separator makes everything spiffy.
    +              if (isIE8OrEarlier) {
    +                styledText = styledText.replace(newlineRe, '\r');
    +              }
    +              textNode.nodeValue = styledText;
    +              var document = textNode.ownerDocument;
    +              var span = document.createElement('span');
    +              span.className = decorations[decorationIndex + 1];
    +              var parentNode = textNode.parentNode;
    +              parentNode.replaceChild(span, textNode);
    +              span.appendChild(textNode);
    +              if (sourceIndex < spanEnd) {  // Split off a text node.
    +                spans[spanIndex + 1] = textNode
    +                    // TODO: Possibly optimize by using '' if there's no flicker.
    +                    = document.createTextNode(source.substring(end, spanEnd));
    +                parentNode.insertBefore(textNode, span.nextSibling);
    +              }
    +            }
    +      
    +            sourceIndex = end;
    +      
    +            if (sourceIndex >= spanEnd) {
    +              spanIndex += 2;
    +            }
    +            if (sourceIndex >= decEnd) {
    +              decorationIndex += 2;
    +            }
    +          }
    +        } finally {
    +          if (sourceNode) {
    +            sourceNode.style.display = oldDisplay;
               }
             }
           }
    -      if (k < elements.length) {
    -        // finish up in a continuation
    -        win.setTimeout(doWork, 250);
    -      } else if ('function' === typeof opt_whenDone) {
    -        opt_whenDone();
    +    
    +      /** Maps language-specific file extensions to handlers. */
    +      var langHandlerRegistry = {};
    +      /** Register a language handler for the given file extensions.
    +        * @param {function (JobT)} handler a function from source code to a list
    +        *      of decorations.  Takes a single argument job which describes the
    +        *      state of the computation and attaches the decorations to it.
    +        * @param {Array.<string>} fileExtensions
    +        */
    +      function registerLangHandler(handler, fileExtensions) {
    +        for (var i = fileExtensions.length; --i >= 0;) {
    +          var ext = fileExtensions[i];
    +          if (!langHandlerRegistry.hasOwnProperty(ext)) {
    +            langHandlerRegistry[ext] = handler;
    +          } else if (win['console']) {
    +            console['warn']('cannot override language handler %s', ext);
    +          }
    +        }
           }
    +      function langHandlerForExtension(extension, source) {
    +        if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    +          // Treat it as markup if the first non whitespace character is a < and
    +          // the last non-whitespace character is a >.
    +          extension = /^\s*</.test(source)
    +              ? 'default-markup'
    +              : 'default-code';
    +        }
    +        return langHandlerRegistry[extension];
    +      }
    +      registerLangHandler(decorateSource, ['default-code']);
    +      registerLangHandler(
    +          createSimpleLexer(
    +              [],
    +              [
    +               [PR_PLAIN,       /^[^<?]+/],
    +               [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    +               [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    +               // Unescaped content in an unknown language
    +               ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    +               ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    +               [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    +               ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    +               // Unescaped content in javascript.  (Or possibly vbscript).
    +               ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    +               // Contains unescaped stylesheet content
    +               ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    +               ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    +              ]),
    +          ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    +      registerLangHandler(
    +          createSimpleLexer(
    +              [
    +               [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    +               [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    +               ],
    +              [
    +               [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    +               [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    +               ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    +               [PR_PUNCTUATION,  /^[=<>\/]+/],
    +               ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    +               ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    +               ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    +               ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    +               ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    +               ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    +               ]),
    +          ['in.tag']);
    +      registerLangHandler(
    +          createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': CPP_KEYWORDS,
    +              'hashComments': true,
    +              'cStyleComments': true,
    +              'types': C_TYPES
    +            }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': 'null,true,false'
    +            }), ['json']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': CSHARP_KEYWORDS,
    +              'hashComments': true,
    +              'cStyleComments': true,
    +              'verbatimStrings': true,
    +              'types': C_TYPES
    +            }), ['cs']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': JAVA_KEYWORDS,
    +              'cStyleComments': true
    +            }), ['java']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': SH_KEYWORDS,
    +              'hashComments': true,
    +              'multiLineStrings': true
    +            }), ['bash', 'bsh', 'csh', 'sh']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': PYTHON_KEYWORDS,
    +              'hashComments': true,
    +              'multiLineStrings': true,
    +              'tripleQuotedStrings': true
    +            }), ['cv', 'py', 'python']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': PERL_KEYWORDS,
    +              'hashComments': true,
    +              'multiLineStrings': true,
    +              'regexLiterals': 2  // multiline regex literals
    +            }), ['perl', 'pl', 'pm']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': RUBY_KEYWORDS,
    +              'hashComments': true,
    +              'multiLineStrings': true,
    +              'regexLiterals': true
    +            }), ['rb', 'ruby']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': JSCRIPT_KEYWORDS,
    +              'cStyleComments': true,
    +              'regexLiterals': true
    +            }), ['javascript', 'js', 'ts', 'typescript']);
    +      registerLangHandler(sourceDecorator({
    +              'keywords': COFFEE_KEYWORDS,
    +              'hashComments': 3,  // ### style block comments
    +              'cStyleComments': true,
    +              'multilineStrings': true,
    +              'tripleQuotedStrings': true,
    +              'regexLiterals': true
    +            }), ['coffee']);
    +      registerLangHandler(
    +          createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    +    
    +      /** @param {JobT} job */
    +      function applyDecorator(job) {
    +        var opt_langExtension = job.langExtension;
    +    
    +        try {
    +          // Extract tags, and convert the source code to plain text.
    +          var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
    +          /** Plain text. @type {string} */
    +          var source = sourceAndSpans.sourceCode;
    +          job.sourceCode = source;
    +          job.spans = sourceAndSpans.spans;
    +          job.basePos = 0;
    +    
    +          // Apply the appropriate language handler
    +          langHandlerForExtension(opt_langExtension, source)(job);
    +    
    +          // Integrate the decorations and tags back into the source code,
    +          // modifying the sourceNode in place.
    +          recombineTagsAndDecorations(job);
    +        } catch (e) {
    +          if (win['console']) {
    +            console['log'](e && e['stack'] || e);
    +          }
    +        }
    +      }
    +    
    +      /**
    +       * Pretty print a chunk of code.
    +       * @param sourceCodeHtml {string} The HTML to pretty print.
    +       * @param opt_langExtension {string} The language name to use.
    +       *     Typically, a filename extension like 'cpp' or 'java'.
    +       * @param opt_numberLines {number|boolean} True to number lines,
    +       *     or the 1-indexed number of the first line in sourceCodeHtml.
    +       */
    +      function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
    +        /** @type{number|boolean} */
    +        var nl = opt_numberLines || false;
    +        /** @type{string|null} */
    +        var langExtension = opt_langExtension || null;
    +        /** @type{!Element} */
    +        var container = document.createElement('div');
    +        // This could cause images to load and onload listeners to fire.
    +        // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
    +        // We assume that the inner HTML is from a trusted source.
    +        // The pre-tag is required for IE8 which strips newlines from innerHTML
    +        // when it is injected into a <pre> tag.
    +        // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
    +        // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
    +        container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
    +        container = /** @type{!Element} */(container.firstChild);
    +        if (nl) {
    +          numberLines(container, nl, true);
    +        }
    +    
    +        /** @type{JobT} */
    +        var job = {
    +          langExtension: langExtension,
    +          numberLines: nl,
    +          sourceNode: container,
    +          pre: 1,
    +          sourceCode: null,
    +          basePos: null,
    +          spans: null,
    +          decorations: null
    +        };
    +        applyDecorator(job);
    +        return container.innerHTML;
    +      }
    +    
    +       /**
    +        * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    +        * {@code class=prettyprint} and prettify them.
    +        *
    +        * @param {Function} opt_whenDone called when prettifying is done.
    +        * @param {HTMLElement|HTMLDocument} opt_root an element or document
    +        *   containing all the elements to pretty print.
    +        *   Defaults to {@code document.body}.
    +        */
    +      function $prettyPrint(opt_whenDone, opt_root) {
    +        var root = opt_root || document.body;
    +        var doc = root.ownerDocument || document;
    +        function byTagName(tn) { return root.getElementsByTagName(tn); }
    +        // fetch a list of nodes to rewrite
    +        var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
    +        var elements = [];
    +        for (var i = 0; i < codeSegments.length; ++i) {
    +          for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    +            elements.push(codeSegments[i][j]);
    +          }
    +        }
    +        codeSegments = null;
    +    
    +        var clock = Date;
    +        if (!clock['now']) {
    +          clock = { 'now': function () { return +(new Date); } };
    +        }
    +    
    +        // The loop is broken into a series of continuations to make sure that we
    +        // don't make the browser unresponsive when rewriting a large page.
    +        var k = 0;
    +    
    +        var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
    +        var prettyPrintRe = /\bprettyprint\b/;
    +        var prettyPrintedRe = /\bprettyprinted\b/;
    +        var preformattedTagNameRe = /pre|xmp/i;
    +        var codeRe = /^code$/i;
    +        var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
    +        var EMPTY = {};
    +    
    +        function doWork() {
    +          var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
    +                         clock['now']() + 250 /* ms */ :
    +                         Infinity);
    +          for (; k < elements.length && clock['now']() < endTime; k++) {
    +            var cs = elements[k];
    +    
    +            // Look for a preceding comment like
    +            // <?prettify lang="..." linenums="..."?>
    +            var attrs = EMPTY;
    +            {
    +              for (var preceder = cs; (preceder = preceder.previousSibling);) {
    +                var nt = preceder.nodeType;
    +                // <?foo?> is parsed by HTML 5 to a comment node (8)
    +                // like <!--?foo?-->, but in XML is a processing instruction
    +                var value = (nt === 7 || nt === 8) && preceder.nodeValue;
    +                if (value
    +                    ? !/^\??prettify\b/.test(value)
    +                    : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
    +                  // Skip over white-space text nodes but not others.
    +                  break;
    +                }
    +                if (value) {
    +                  attrs = {};
    +                  value.replace(
    +                      /\b(\w+)=([\w:.%+-]+)/g,
    +                    function (_, name, value) { attrs[name] = value; });
    +                  break;
    +                }
    +              }
    +            }
    +    
    +            var className = cs.className;
    +            if ((attrs !== EMPTY || prettyPrintRe.test(className))
    +                // Don't redo this if we've already done it.
    +                // This allows recalling pretty print to just prettyprint elements
    +                // that have been added to the page since last call.
    +                && !prettyPrintedRe.test(className)) {
    +    
    +              // make sure this is not nested in an already prettified element
    +              var nested = false;
    +              for (var p = cs.parentNode; p; p = p.parentNode) {
    +                var tn = p.tagName;
    +                if (preCodeXmpRe.test(tn)
    +                    && p.className && prettyPrintRe.test(p.className)) {
    +                  nested = true;
    +                  break;
    +                }
    +              }
    +              if (!nested) {
    +                // Mark done.  If we fail to prettyprint for whatever reason,
    +                // we shouldn't try again.
    +                cs.className += ' prettyprinted';
    +    
    +                // If the classes includes a language extensions, use it.
    +                // Language extensions can be specified like
    +                //     <pre class="prettyprint lang-cpp">
    +                // the language extension "cpp" is used to find a language handler
    +                // as passed to PR.registerLangHandler.
    +                // HTML5 recommends that a language be specified using "language-"
    +                // as the prefix instead.  Google Code Prettify supports both.
    +                // http://dev.w3.org/html5/spec-author-view/the-code-element.html
    +                var langExtension = attrs['lang'];
    +                if (!langExtension) {
    +                  langExtension = className.match(langExtensionRe);
    +                  // Support <pre class="prettyprint"><code class="language-c">
    +                  var wrapper;
    +                  if (!langExtension && (wrapper = childContentWrapper(cs))
    +                      && codeRe.test(wrapper.tagName)) {
    +                    langExtension = wrapper.className.match(langExtensionRe);
    +                  }
    +    
    +                  if (langExtension) { langExtension = langExtension[1]; }
    +                }
    +    
    +                var preformatted;
    +                if (preformattedTagNameRe.test(cs.tagName)) {
    +                  preformatted = 1;
    +                } else {
    +                  var currentStyle = cs['currentStyle'];
    +                  var defaultView = doc.defaultView;
    +                  var whitespace = (
    +                      currentStyle
    +                      ? currentStyle['whiteSpace']
    +                      : (defaultView
    +                         && defaultView.getComputedStyle)
    +                      ? defaultView.getComputedStyle(cs, null)
    +                      .getPropertyValue('white-space')
    +                      : 0);
    +                  preformatted = whitespace
    +                      && 'pre' === whitespace.substring(0, 3);
    +                }
    +    
    +                // Look for a class like linenums or linenums:<n> where <n> is the
    +                // 1-indexed number of the first line.
    +                var lineNums = attrs['linenums'];
    +                if (!(lineNums = lineNums === 'true' || +lineNums)) {
    +                  lineNums = className.match(/\blinenums\b(?::(\d+))?/);
    +                  lineNums =
    +                    lineNums
    +                    ? lineNums[1] && lineNums[1].length
    +                      ? +lineNums[1] : true
    +                    : false;
    +                }
    +                if (lineNums) { numberLines(cs, lineNums, preformatted); }
    +    
    +                // do the pretty printing
    +                var prettyPrintingJob = {
    +                  langExtension: langExtension,
    +                  sourceNode: cs,
    +                  numberLines: lineNums,
    +                  pre: preformatted,
    +                  sourceCode: null,
    +                  basePos: null,
    +                  spans: null,
    +                  decorations: null
    +                };
    +                applyDecorator(prettyPrintingJob);
    +              }
    +            }
    +          }
    +          if (k < elements.length) {
    +            // finish up in a continuation
    +            win.setTimeout(doWork, 250);
    +          } else if ('function' === typeof opt_whenDone) {
    +            opt_whenDone();
    +          }
    +        }
    +    
    +        doWork();
    +      }
    +    
    +      /**
    +       * Contains functions for creating and registering new language handlers.
    +       * @type {Object}
    +       */
    +      var PR = win['PR'] = {
    +            'createSimpleLexer': createSimpleLexer,
    +            'registerLangHandler': registerLangHandler,
    +            'sourceDecorator': sourceDecorator,
    +            'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    +            'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    +            'PR_COMMENT': PR_COMMENT,
    +            'PR_DECLARATION': PR_DECLARATION,
    +            'PR_KEYWORD': PR_KEYWORD,
    +            'PR_LITERAL': PR_LITERAL,
    +            'PR_NOCODE': PR_NOCODE,
    +            'PR_PLAIN': PR_PLAIN,
    +            'PR_PUNCTUATION': PR_PUNCTUATION,
    +            'PR_SOURCE': PR_SOURCE,
    +            'PR_STRING': PR_STRING,
    +            'PR_TAG': PR_TAG,
    +            'PR_TYPE': PR_TYPE,
    +            'prettyPrintOne':
    +               IN_GLOBAL_SCOPE
    +                 ? (win['prettyPrintOne'] = $prettyPrintOne)
    +                 : (prettyPrintOne = $prettyPrintOne),
    +            'prettyPrint': prettyPrint =
    +               IN_GLOBAL_SCOPE
    +                 ? (win['prettyPrint'] = $prettyPrint)
    +                 : (prettyPrint = $prettyPrint)
    +          };
    +    
    +      // Make PR available via the Asynchronous Module Definition (AMD) API.
    +      // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
    +      // The Asynchronous Module Definition (AMD) API specifies a
    +      // mechanism for defining modules such that the module and its
    +      // dependencies can be asynchronously loaded.
    +      // ...
    +      // To allow a clear indicator that a global define function (as
    +      // needed for script src browser loading) conforms to the AMD API,
    +      // any global define function SHOULD have a property called "amd"
    +      // whose value is an object. This helps avoid conflict with any
    +      // other existing JavaScript code that could have defined a define()
    +      // function that does not conform to the AMD API.
    +      var define = win['define'];
    +      if (typeof define === "function" && define['amd']) {
    +        define("google-code-prettify", [], function () {
    +          return PR;
    +        });
    +      }
    +    })();
    +    return prettyPrint;
    +  })();
    +
    +  // If this script is deferred or async and the document is already
    +  // loaded we need to wait for language handlers to load before performing
    +  // any autorun.
    +  function onLangsLoaded() {
    +    if (autorun) {
    +      contentLoaded(
    +        function () {
    +          var n = callbacks.length;
    +          var callback = n ? function () {
    +            for (var i = 0; i < n; ++i) {
    +              (function (i) {
    +                win.setTimeout(
    +                   function () {
    +                     win['exports'][callbacks[i]].apply(win, arguments);
    +                   }, 0);
    +               })(i);
    +            }
    +          } : void 0;
    +          prettyPrint(callback);
    +        });
         }
    -
    -    doWork();
       }
    +  checkPendingLanguages();
     
    -  /**
    -   * Contains functions for creating and registering new language handlers.
    -   * @type {Object}
    -   */
    -  var PR = win['PR'] = {
    -        'createSimpleLexer': createSimpleLexer,
    -        'registerLangHandler': registerLangHandler,
    -        'sourceDecorator': sourceDecorator,
    -        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    -        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    -        'PR_COMMENT': PR_COMMENT,
    -        'PR_DECLARATION': PR_DECLARATION,
    -        'PR_KEYWORD': PR_KEYWORD,
    -        'PR_LITERAL': PR_LITERAL,
    -        'PR_NOCODE': PR_NOCODE,
    -        'PR_PLAIN': PR_PLAIN,
    -        'PR_PUNCTUATION': PR_PUNCTUATION,
    -        'PR_SOURCE': PR_SOURCE,
    -        'PR_STRING': PR_STRING,
    -        'PR_TAG': PR_TAG,
    -        'PR_TYPE': PR_TYPE,
    -        'prettyPrintOne':
    -           IN_GLOBAL_SCOPE
    -             ? (win['prettyPrintOne'] = $prettyPrintOne)
    -             : (prettyPrintOne = $prettyPrintOne),
    -        'prettyPrint': prettyPrint =
    -           IN_GLOBAL_SCOPE
    -             ? (win['prettyPrint'] = $prettyPrint)
    -             : (prettyPrint = $prettyPrint)
    -      };
    -
    -  // Make PR available via the Asynchronous Module Definition (AMD) API.
    -  // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
    -  // The Asynchronous Module Definition (AMD) API specifies a
    -  // mechanism for defining modules such that the module and its
    -  // dependencies can be asynchronously loaded.
    -  // ...
    -  // To allow a clear indicator that a global define function (as
    -  // needed for script src browser loading) conforms to the AMD API,
    -  // any global define function SHOULD have a property called "amd"
    -  // whose value is an object. This helps avoid conflict with any
    -  // other existing JavaScript code that could have defined a define()
    -  // function that does not conform to the AMD API.
    -  var define = win['define'];
    -  if (typeof define === "function" && define['amd']) {
    -    define("google-code-prettify", [], function () {
    -      return PR;
    -    });
    -  }
    -})();
    \ No newline at end of file
    +}());
    diff --git a/app/src/main/assets/highlight/styles/prettify.css b/app/src/main/assets/highlight/styles/prettify.css
    index af016d1a..2974a100 100644
    --- a/app/src/main/assets/highlight/styles/prettify.css
    +++ b/app/src/main/assets/highlight/styles/prettify.css
    @@ -1,32 +1,32 @@
     .prettyprint {
    -  font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace;
    -  border: 0 !important;
    +    font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace !important;
    +    border: 0 !important;
     }
     
     .pln {
    -  color: #333;
    +    color: #333 !important;
     }
     
     ol.linenums {
    -  margin-top: 0;
    -  margin-bottom: 0;
    -  color: #727272;
    +    margin-top: 0 !important;
    +    margin-bottom: 0 !important;
    +    color: #727272 !important;
     }
     
     ol {
    -    margin-left: 0;
    -    padding-left: 0.2em;
    +    margin-left: 0 !important;
    +    padding-left: 0.2em !important;
         counter-reset: ol-item
     }
     
     ol li:nth-child(-n+9) {
    -	padding-left: 8px;
    +    padding-left: 8px !important;
     }
     
     ol li {
    -    margin-left: 0;
    -    padding-left: 0;
    -    list-style-type: none;
    +    margin-left: 0 !important;
    +    padding-left: 0 !important;
    +    list-style-type: none !important;
         counter-increment: ol-item
     }
     
    @@ -34,7 +34,6 @@ ol li:before {
         content: counter(ol-item) " "
     }
     
    -
     li.L0,
     li.L1,
     li.L2,
    @@ -44,91 +43,79 @@ li.L5,
     li.L6,
     li.L7,
     li.L8,
    -li.L9 {}
    +li.L9 {
    +    background: transparent !important;
    +}
     
     @media screen {
     
    -  /* string content */
    +    /* string content */
    +    .str {
    +        color: #183691 !important;
    +    }
     
    -  .str {
    -    color: #183691;
    -  }
    +    /* keyword */
    +    .kwd {
    +        color: #a71d5d !important;
    +    }
     
    -  /* keyword */
    +    /* comment */
    +    .com {
    +        color: #969896 !important;
    +    }
     
    -  .kwd {
    -    color: #a71d5d;
    -  }
    +    /* type name */
    +    .typ {
    +        color: #0086b3 !important;
    +    }
     
    -  /* comment */
    +    /* literal value */
    +    .lit {
    +        color: #0086b3 !important;
    +    }
     
    -  .com {
    -    color: #969896;
    -  }
    +    /* punctuation */
    +    .pun {
    +        color: #333 !important;
    +    }
     
    -  /* type name */
    +    /* lisp open bracket */
    +    .opn {
    +        color: #333 !important;
    +    }
     
    -  .typ {
    -    color: #0086b3;
    -  }
    +    /* lisp close bracket */
    +    .clo {
    +        color: #333 !important;
    +    }
     
    -  /* literal value */
    +    /* markup tag name */
    +    .tag {
    +        color: #000080 !important;
    +    }
     
    -  .lit {
    -    color: #0086b3;
    -  }
    +    /* markup attribute name */
    +    .atn {
    +        color: #795da3 !important;
    +    }
     
    -  /* punctuation */
    +    /* markup attribute value */
    +    .atv {
    +        color: #183691 !important;
    +    }
     
    -  .pun {
    -    color: #333;
    -  }
    +    /* declaration */
    +    .dec {
    +        color: #333 !important;
    +    }
     
    -  /* lisp open bracket */
    +    /* variable name */
    +    .var {
    +        color: #008080 !important;
    +    }
     
    -  .opn {
    -    color: #333;
    -  }
    -
    -  /* lisp close bracket */
    -
    -  .clo {
    -    color: #333;
    -  }
    -
    -  /* markup tag name */
    -
    -  .tag {
    -    color: #000080;
    -  }
    -
    -  /* markup attribute name */
    -
    -  .atn {
    -    color: #795da3;
    -  }
    -
    -  /* markup attribute value */
    -
    -  .atv {
    -    color: #183691;
    -  }
    -
    -  /* declaration */
    -
    -  .dec {
    -    color: #333;
    -  }
    -
    -  /* variable name */
    -
    -  .var {
    -    color: #008080;
    -  }
    -
    -  /* function name */
    -
    -  .fun {
    -    color: #900;
    -  }
    +    /* function name */
    +    .fun {
    +        color: #900 !important;
    +    }
     }
    \ No newline at end of file
    diff --git a/app/src/main/assets/highlight/styles/prettify_dark.css b/app/src/main/assets/highlight/styles/prettify_dark.css
    index 032fd28f..fdb6687f 100644
    --- a/app/src/main/assets/highlight/styles/prettify_dark.css
    +++ b/app/src/main/assets/highlight/styles/prettify_dark.css
    @@ -1,33 +1,33 @@
     .prettyprint {
    -  font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace;
    -  border: 0 !important;
    +    font-family: Menlo, "Bitstream Vera Sans Mono", "DejaVu Sans Mono", Monaco, Consolas, monospace !important;
    +    border: 0 !important;
     }
     
     .pln {
    -  color: #e6e9ed;
    +    color: #e6e9ed !important;
     }
     
     /* Specify class=linenums on a pre to get line numbering */
     ol.linenums {
    -  margin-top: 0;
    -  margin-bottom: 0;
    -  color: #656d78;
    +    margin-top: 0 !important;
    +    margin-bottom: 0 !important;
    +    color: #656d78 !important;
     }
     
     ol {
    -    margin-left: 0;
    -    padding-left: 0.2em;
    +    margin-left: 0 !important;
    +    padding-left: 0.2em !important;
         counter-reset: ol-item
     }
     
     ol li:nth-child(-n+9) {
    -	padding-left: 8px;
    +    padding-left: 8px !important;
     }
     
     ol li {
    -    margin-left: 0;
    -    padding-left: 0;
    -    list-style-type: none;
    +    margin-left: 0 !important;
    +    padding-left: 0 !important;
    +    list-style-type: none !important;
         counter-increment: ol-item
     }
     
    @@ -44,91 +44,79 @@ li.L5,
     li.L6,
     li.L7,
     li.L8,
    -li.L9 {}
    +li.L9 {
    +    background: transparent !important;
    +}
     
     @media screen {
     
    -  /* string content */
    +    /* string content */
    +    .str {
    +        color: #ffce54 !important;
    +    }
     
    -  .str {
    -    color: #ffce54;
    -  }
    +    /* keyword */
    +    .kwd {
    +        color: #4fc1e9 !important;
    +    }
     
    -  /* keyword */
    +    /* comment */
    +    .com {
    +        color: #656d78 !important;
    +    }
     
    -  .kwd {
    -    color: #4fc1e9;
    -  }
    +    /* type name */
    +    .typ {
    +        color: #4fc1e9 !important;
    +    }
     
    -  /* comment */
    +    /* literal value */
    +    .lit {
    +        color: #ac92ec !important;
    +    }
     
    -  .com {
    -    color: #656d78;
    -  }
    +    /* punctuation */
    +    .pun {
    +        color: #e6e9ed !important;
    +    }
     
    -  /* type name */
    +    /* lisp open bracket */
    +    .opn {
    +        color: #e6e9ed !important;
    +    }
     
    -  .typ {
    -    color: #4fc1e9;
    -  }
    +    /* lisp close bracket */
    +    .clo {
    +        color: #e6e9ed !important;
    +    }
     
    -  /* literal value */
    +    /* markup tag name */
    +    .tag {
    +        color: #ed5565 !important;
    +    }
     
    -  .lit {
    -    color: #ac92ec;
    -  }
    +    /* markup attribute name */
    +    .atn {
    +        color: #a0d468 !important;
    +    }
     
    -  /* punctuation */
    +    /* markup attribute value */
    +    .atv {
    +        color: #ffce54 !important;
    +    }
     
    -  .pun {
    -    color: #e6e9ed;
    -  }
    +    /* declaration */
    +    .dec {
    +        color: #ac92ec !important;
    +    }
     
    -  /* lisp open bracket */
    +    /* variable name */
    +    .var {
    +        color: #e6e9ed !important;
    +    }
     
    -  .opn {
    -    color: #e6e9ed;
    -  }
    -
    -  /* lisp close bracket */
    -
    -  .clo {
    -    color: #e6e9ed;
    -  }
    -
    -  /* markup tag name */
    -
    -  .tag {
    -    color: #ed5565;
    -  }
    -
    -  /* markup attribute name */
    -
    -  .atn {
    -    color: #a0d468;
    -  }
    -
    -  /* markup attribute value */
    -
    -  .atv {
    -    color: #ffce54;
    -  }
    -
    -  /* declaration */
    -
    -  .dec {
    -    color: #ac92ec;
    -  }
    -
    -  /* variable name */
    -
    -  .var {
    -    color: #e6e9ed;
    -  }
    -
    -  /* function name */
    -
    -  .fun {
    -    color: #e6e9ed;
    -  }
    +    /* function name */
    +    .fun {
    +        color: #e6e9ed !important;
    +    }
     }
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java
    index fd0d1e5c..37bf4f56 100644
    --- a/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java
    +++ b/app/src/main/java/com/fastaccess/data/dao/FragmentPagerAdapterModel.java
    @@ -163,8 +163,7 @@ import lombok.Setter;
     
         public static List<FragmentPagerAdapterModel> buildForGists(@NonNull Context context) {
     
    -        return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.my_gists), ProfileGistsFragment.newInstance(Login.getUser()
    -                        .getLogin())),
    +        return Stream.of(new FragmentPagerAdapterModel(context.getString(R.string.my_gists), ProfileGistsFragment.newInstance(Login.getUser().getLogin())),
                     new FragmentPagerAdapterModel(context.getString(R.string.public_gists), GistsFragment.newInstance()))
                     .collect(Collectors.toList());
         }
    diff --git a/app/src/main/java/com/fastaccess/data/dao/GitCommitModel.java b/app/src/main/java/com/fastaccess/data/dao/GitCommitModel.java
    index 80454f87..3da841ab 100644
    --- a/app/src/main/java/com/fastaccess/data/dao/GitCommitModel.java
    +++ b/app/src/main/java/com/fastaccess/data/dao/GitCommitModel.java
    @@ -49,8 +49,8 @@ public class GitCommitModel implements Parcelable {
             this.committer = in.readParcelable(User.class.getClassLoader());
             this.tree = in.readParcelable(User.class.getClassLoader());
             this.distincted = in.readByte() != 0;
    -        in.readList(parents, parents.getClass().getClassLoader());
    -        in.readList(this.parents, GitCommitModel.class.getClassLoader());
    +        this.parents = new GitCommitListModel();
    +        in.readList(this.parents, this.parents.getClass().getClassLoader());
             this.commentCount = in.readInt();
         }
     
    diff --git a/app/src/main/java/com/fastaccess/data/dao/TrendingResponse.java b/app/src/main/java/com/fastaccess/data/dao/TrendingResponse.java
    deleted file mode 100644
    index dcab0e58..00000000
    --- a/app/src/main/java/com/fastaccess/data/dao/TrendingResponse.java
    +++ /dev/null
    @@ -1,47 +0,0 @@
    -package com.fastaccess.data.dao;
    -
    -import android.os.Parcel;
    -import android.os.Parcelable;
    -
    -import com.github.florent37.retrojsoup.annotations.JsoupText;
    -
    -import lombok.ToString;
    -
    -/**
    - * Created by Kosh on 02 Jun 2017, 1:06 PM
    - */
    -
    -@ToString public class TrendingResponse implements Parcelable { // retroSoup doesn't like it to be kolin class.
    -
    -    @JsoupText(".repo-list > li > .d-inline-block > h3 > a") public String title;
    -    @JsoupText(".repo-list > li > .py-1 > p") public String description;
    -    @JsoupText(".repo-list > li > .f6 > a[href*=/stargazers]") public String stars;
    -    @JsoupText(".repo-list > li > .f6 > a[href*=/network]") public String forks;
    -    @JsoupText(".repo-list > li > .f6 > span.float-right") public String todayStars;
    -
    -    @Override public int describeContents() { return 0; }
    -
    -    @Override public void writeToParcel(Parcel dest, int flags) {
    -        dest.writeString(this.title);
    -        dest.writeString(this.description);
    -        dest.writeString(this.stars);
    -        dest.writeString(this.forks);
    -        dest.writeString(this.todayStars);
    -    }
    -
    -    public TrendingResponse() {}
    -
    -    protected TrendingResponse(Parcel in) {
    -        this.title = in.readString();
    -        this.description = in.readString();
    -        this.stars = in.readString();
    -        this.forks = in.readString();
    -        this.todayStars = in.readString();
    -    }
    -
    -    public static final Parcelable.Creator<TrendingResponse> CREATOR = new Parcelable.Creator<TrendingResponse>() {
    -        @Override public TrendingResponse createFromParcel(Parcel source) {return new TrendingResponse(source);}
    -
    -        @Override public TrendingResponse[] newArray(int size) {return new TrendingResponse[size];}
    -    };
    -}
    diff --git a/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java b/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    index 6432b5e3..8173e716 100644
    --- a/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    +++ b/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    @@ -12,7 +12,6 @@ import com.fastaccess.data.dao.GithubFileModel;
     import com.fastaccess.data.dao.converters.GitHubFilesConverter;
     import com.fastaccess.data.dao.converters.UserConverter;
     import com.fastaccess.helper.InputHelper;
    -import com.fastaccess.helper.ObjectsCompat;
     import com.fastaccess.helper.RxHelper;
     import com.fastaccess.ui.widgets.SpannableBuilder;
     import com.google.gson.annotations.SerializedName;
    @@ -35,7 +34,7 @@ import lombok.NoArgsConstructor;
      * Created by Kosh on 16 Mar 2017, 7:32 PM
      */
     
    -@Entity @NoArgsConstructor public abstract class AbstractGist implements Parcelable {
    +@Entity() @NoArgsConstructor public abstract class AbstractGist implements Parcelable {
         @SerializedName("nooope") @Key long id;
         String url;
         String forksUrl;
    @@ -79,11 +78,11 @@ import lombok.NoArgsConstructor;
                     .single()
                     .toObservable()
                     .flatMap(integer -> Observable.fromIterable(gists))
    -                .filter(ObjectsCompat::nonNull)
    -                .flatMap(gist -> {
    +                .map(gist -> {
                         gist.setOwnerName(ownerName);
    -                    return gist.save(gist).toObservable();
    -                }));
    +                    return gist;
    +                })
    +                .flatMap(gist -> gist.save(gist).toObservable()));
         }
     
         @NonNull public static Single<List<Gist>> getMyGists(@NonNull String ownerName) {
    @@ -168,7 +167,16 @@ import lombok.NoArgsConstructor;
                 spannableBuilder.append(description);
             }
             if (InputHelper.isEmpty(spannableBuilder.toString())) {
    -            if (isFromProfile) spannableBuilder.bold("N/A");
    +            if (isFromProfile) {
    +                List<FilesListModel> files = getFilesAsList();
    +                if (!files.isEmpty()) {
    +                    FilesListModel filesListModel = files.get(0);
    +                    if (!InputHelper.isEmpty(filesListModel.getFilename()) && filesListModel.getFilename().trim().length() > 2) {
    +                        spannableBuilder.append(" ").append("/").append(" ")
    +                                .append(filesListModel.getFilename());
    +                    }
    +                }
    +            }
             }
             return spannableBuilder;
         }
    diff --git a/app/src/main/java/com/fastaccess/data/service/GistService.java b/app/src/main/java/com/fastaccess/data/service/GistService.java
    index eb6a8a70..bc088be4 100644
    --- a/app/src/main/java/com/fastaccess/data/service/GistService.java
    +++ b/app/src/main/java/com/fastaccess/data/service/GistService.java
    @@ -8,6 +8,7 @@ import com.fastaccess.data.dao.Pageable;
     import com.fastaccess.data.dao.model.Comment;
     import com.fastaccess.data.dao.model.Gist;
     
    +import io.reactivex.Observable;
     import retrofit2.Response;
     import retrofit2.http.Body;
     import retrofit2.http.DELETE;
    @@ -19,7 +20,6 @@ import retrofit2.http.PUT;
     import retrofit2.http.Path;
     import retrofit2.http.Query;
     import retrofit2.http.Url;
    -import io.reactivex.Observable;
     
     /**
      * Created by Kosh on 20 Nov 2016, 10:28 AM
    @@ -38,11 +38,10 @@ public interface GistService {
     
         @GET("gists/public") Observable<Pageable<Gist>> getPublicGists(@Query("per_page") int perPage, @Query("page") int page);
     
    -    @GET("gists") Observable<Pageable<Gist>> getMyGists(@Query("per_page") int perPage, @Query("page") int page);
    +    @GET("gists") Observable<Pageable<Gist>> getMyGists(@Query("page") int page);
     
         @GET("users/{username}/gists")
    -    Observable<Pageable<Gist>> getUserGists(@NonNull @Path("username") String username,
    -                                            @Query("per_page") int perPage, @Query("page") int page);
    +    Observable<Pageable<Gist>> getUserGists(@NonNull @Path("username") String username, @Query("page") int page);
     
         @GET("gists/{id}")
         Observable<Gist> getGist(@Path("id") String id);
    diff --git a/app/src/main/java/com/fastaccess/data/service/TrendingService.java b/app/src/main/java/com/fastaccess/data/service/TrendingService.java
    index 9e625e7a..ebec9ebe 100644
    --- a/app/src/main/java/com/fastaccess/data/service/TrendingService.java
    +++ b/app/src/main/java/com/fastaccess/data/service/TrendingService.java
    @@ -1,10 +1,12 @@
     package com.fastaccess.data.service;
     
     
    -import com.fastaccess.data.dao.TrendingResponse;
    -import com.github.florent37.retrojsoup.annotations.Select;
    +import android.support.annotation.Nullable;
     
     import io.reactivex.Observable;
    +import retrofit2.http.GET;
    +import retrofit2.http.Path;
    +import retrofit2.http.Query;
     
     /**
      * Created by Kosh on 02 Jun 2017, 12:58 PM
    @@ -12,5 +14,5 @@ import io.reactivex.Observable;
     
     public interface TrendingService {
     
    -    @Select(".repo-list") Observable<TrendingResponse> getTrending();
    +    @GET("{lan}") Observable<String> getTrending(@Path("lan") @Nullable String lan, @Nullable @Query("since") String since);
     }
    diff --git a/app/src/main/java/com/fastaccess/helper/ParseDateFormat.java b/app/src/main/java/com/fastaccess/helper/ParseDateFormat.java
    index 6b97b7d6..d828b4ba 100644
    --- a/app/src/main/java/com/fastaccess/helper/ParseDateFormat.java
    +++ b/app/src/main/java/com/fastaccess/helper/ParseDateFormat.java
    @@ -60,7 +60,7 @@ public class ParseDateFormat {
             return INSTANCE;
         }
     
    -    public static String getDateByDays(int days) {
    +    private static String getDateByDays(int days) {
             Calendar cal = Calendar.getInstance();
             SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
             cal.add(Calendar.DAY_OF_YEAR, days);
    diff --git a/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java b/app/src/main/java/com/fastaccess/provider/rest/jsoup/JsoupProvider.java
    similarity index 67%
    rename from app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java
    rename to app/src/main/java/com/fastaccess/provider/rest/jsoup/JsoupProvider.java
    index 73e36901..3bff2123 100644
    --- a/app/src/main/java/com/fastaccess/provider/rest/jsoup/RetroJsoupProvider.java
    +++ b/app/src/main/java/com/fastaccess/provider/rest/jsoup/JsoupProvider.java
    @@ -1,23 +1,22 @@
     package com.fastaccess.provider.rest.jsoup;
     
    -import android.support.annotation.NonNull;
    -import android.support.annotation.Nullable;
    -
     import com.fastaccess.BuildConfig;
     import com.fastaccess.data.service.TrendingService;
    -import com.fastaccess.helper.InputHelper;
     import com.fastaccess.helper.PrefGetter;
    +import com.fastaccess.provider.rest.converters.GithubResponseConverter;
     import com.fastaccess.provider.rest.interceptors.AuthenticationInterceptor;
    -import com.github.florent37.retrojsoup.RetroJsoup;
    +import com.google.gson.Gson;
     
     import okhttp3.OkHttpClient;
     import okhttp3.logging.HttpLoggingInterceptor;
    +import retrofit2.Retrofit;
    +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
     
     /**
      * Created by Kosh on 02 Jun 2017, 12:47 PM
      */
     
    -public class RetroJsoupProvider {
    +public class JsoupProvider {
     
         private static OkHttpClient okHttpClient;
     
    @@ -34,11 +33,12 @@ public class RetroJsoupProvider {
             return okHttpClient;
         }
     
    -    public static TrendingService getTrendingService(@NonNull String since, @Nullable String lang) {
    -        return new RetroJsoup.Builder()
    -                .url("https://github.com/trending/" + (!InputHelper.isEmpty(lang) ? lang.replaceAll(" ", "-") : "").toLowerCase()
    -                        + "?since=" + since)
    +    public static TrendingService getTrendingService() {
    +        return new Retrofit.Builder()
    +                .baseUrl("https://github.com/trending/")
                     .client(provideOkHttpClient())
    +                .addConverterFactory(new GithubResponseConverter(new Gson()))
    +                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                     .build()
                     .create(TrendingService.class);
         }
    diff --git a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    index 5f931419..13c4a92a 100644
    --- a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    +++ b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    @@ -125,7 +125,6 @@ public class NotificationSchedulerJobTask extends JobService {
             }
             Context context = getApplicationContext();
             int accentColor = ContextCompat.getColor(this, R.color.material_blue_700);
    -        String[] url = new String[1];
             Notification first = notificationThreadModels.get(0);
             Observable.fromIterable(notificationThreadModels)
                     .subscribeOn(Schedulers.io())
    @@ -134,12 +133,12 @@ public class NotificationSchedulerJobTask extends JobService {
                     .flatMap(notification -> RestProvider.getNotificationService()
                             .getComment(notification.getSubject().getLatestCommentUrl())
                             .subscribeOn(Schedulers.io()), (thread, comment) -> {
    -                    url[0] = comment.getUser().getAvatarUrl();
    +                    String url = comment.getUser().getAvatarUrl();
                         if (!InputHelper.isEmpty(thread.getSubject().getLatestCommentUrl())) {
    -                        getNotificationWithComment(context, accentColor, thread, comment, url[0]);
    +                        getNotificationWithComment(context, accentColor, thread, comment, url);
                             return null;
                         }
    -                    showNotificationWithoutComment(context, accentColor, thread, url[0]);
    +                    showNotificationWithoutComment(context, accentColor, thread, url);
                         return thread;
                     })
                     .subscribeOn(Schedulers.io())
    diff --git a/app/src/main/java/com/fastaccess/ui/adapter/TrendingAdapter.kt b/app/src/main/java/com/fastaccess/ui/adapter/TrendingAdapter.kt
    index e94be765..fe60ffb8 100644
    --- a/app/src/main/java/com/fastaccess/ui/adapter/TrendingAdapter.kt
    +++ b/app/src/main/java/com/fastaccess/ui/adapter/TrendingAdapter.kt
    @@ -2,7 +2,7 @@ package com.fastaccess.ui.adapter
     
     import android.view.ViewGroup
     import com.fastaccess.R
    -import com.fastaccess.data.dao.TrendingResponse
    +import com.fastaccess.data.dao.TrendingModel
     import com.fastaccess.ui.adapter.viewholder.TrendingViewHolder
     import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter
     import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
    @@ -11,9 +11,8 @@ import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
      * Created by Kosh on 02 Jun 2017, 1:36 PM
      */
     
    -class TrendingAdapter(data: MutableList<TrendingResponse>) : BaseRecyclerAdapter<TrendingResponse,
    -        TrendingViewHolder, BaseViewHolder.OnItemClickListener<TrendingResponse>>(data) {
    -
    +class TrendingAdapter(data: MutableList<TrendingModel>) : BaseRecyclerAdapter<TrendingModel,
    +        TrendingViewHolder, BaseViewHolder.OnItemClickListener<TrendingModel>>(data) {
         override fun viewHolder(parent: ViewGroup?, viewType: Int): TrendingViewHolder {
             return TrendingViewHolder(BaseViewHolder.getView(parent!!, R.layout.trending_row_item), this)
         }
    diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/GistsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/GistsViewHolder.java
    index 4cbbdcb6..0fbe7522 100644
    --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/GistsViewHolder.java
    +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/GistsViewHolder.java
    @@ -7,6 +7,7 @@ import android.view.ViewGroup;
     
     import com.fastaccess.R;
     import com.fastaccess.data.dao.model.Gist;
    +import com.fastaccess.helper.Logger;
     import com.fastaccess.helper.ParseDateFormat;
     import com.fastaccess.ui.widgets.AvatarLayout;
     import com.fastaccess.ui.widgets.FontTextView;
    @@ -26,6 +27,7 @@ public class GistsViewHolder extends BaseViewHolder<Gist> {
         @BindView(R.id.date) FontTextView date;
         private boolean isFromProfile;
     
    +
         private GistsViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter, boolean isFromProfile) {
             super(itemView, adapter);
             title.setMaxLines(2);
    @@ -40,7 +42,6 @@ public class GistsViewHolder extends BaseViewHolder<Gist> {
             }
         }
     
    -
         @Override public void bind(@NonNull Gist item) {
             if (!isFromProfile) {
                 if (avatar != null) {
    diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    index 0ee181cf..5544ae33 100644
    --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    @@ -2,7 +2,8 @@ package com.fastaccess.ui.adapter.viewholder
     
     import android.view.View
     import com.fastaccess.R
    -import com.fastaccess.data.dao.TrendingResponse
    +import com.fastaccess.data.dao.TrendingModel
    +import com.fastaccess.provider.colors.ColorsProvider
     import com.fastaccess.provider.emoji.EmojiParser
     import com.fastaccess.ui.widgets.FontTextView
     import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter
    @@ -12,16 +13,17 @@ import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
      * Created by Kosh on 02 Jun 2017, 1:27 PM
      */
     
    -open class TrendingViewHolder(itemView: View, adapter: BaseRecyclerAdapter<TrendingResponse,
    -        TrendingViewHolder, OnItemClickListener<TrendingResponse>>) : BaseViewHolder<TrendingResponse>(itemView, adapter) {
    +open class TrendingViewHolder(itemView: View, adapter: BaseRecyclerAdapter<TrendingModel,
    +        TrendingViewHolder, OnItemClickListener<TrendingModel>>) : BaseViewHolder<TrendingModel>(itemView, adapter) {
     
         val title by lazy { itemView.findViewById(R.id.title) as FontTextView }
         val description by lazy { itemView.findViewById(R.id.description) as FontTextView }
         val todayStars by lazy { itemView.findViewById(R.id.todayStars) as FontTextView }
         val stars by lazy { itemView.findViewById(R.id.stars) as FontTextView }
         val fork by lazy { itemView.findViewById(R.id.forks) as FontTextView }
    +    val lang by lazy { itemView.findViewById(R.id.language) as FontTextView }
     
    -    override fun bind(t: TrendingResponse) {
    +    override fun bind(t: TrendingModel) {
             title.text = t.title
             if (!t.description.isNullOrEmpty()) {
                 val descriptionValue: String = EmojiParser.parseToUnicode(t.description)
    @@ -33,6 +35,10 @@ open class TrendingViewHolder(itemView: View, adapter: BaseRecyclerAdapter<Trend
             todayStars.text = t.todayStars
             stars.text = t.stars
             fork.text = t.forks
    +        lang.text = t.language
    +        lang.tintDrawables(t.language?.let { ColorsProvider.getColorAsColor(it, itemView.context) } as Int)
    +        lang.setTextColor(t.language?.let { ColorsProvider.getColorAsColor(it, itemView.context) } as Int)
    +        lang.visibility = if (t.language.isNullOrEmpty()) View.GONE else View.VISIBLE
         }
     
     }
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
    index 5e5e8b19..9c9a8d16 100644
    --- a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
    +++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java
    @@ -161,12 +161,7 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
         @Override public void showMessage(@NonNull String titleRes, @NonNull String msgRes) {
             hideProgress();
             if (toast != null) toast.cancel();
    -        Context context;
    -        if (!isFinishing()) {
    -            context = this;
    -        } else {
    -            context = App.getInstance();
    -        }
    +        Context context = App.getInstance(); // WindowManager$BadTokenException
             toast = titleRes.equals(context.getString(R.string.error))
                     ? Toasty.error(context, msgRes, Toast.LENGTH_LONG)
                     : Toasty.info(context, msgRes, Toast.LENGTH_LONG);
    @@ -186,7 +181,7 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
             if (resId != 0) {
                 msg = getString(resId);
             }
    -        if (!isProgressShowing) {
    +        if (!isProgressShowing && !isFinishing()) {
                 ProgressDialogFragment fragment = (ProgressDialogFragment) AppHelper.getFragmentByTag(getSupportFragmentManager(),
                         ProgressDialogFragment.TAG);
                 if (fragment == null) {
    @@ -207,7 +202,7 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
         }
     
         @Override public void onRequireLogin() {
    -        Toasty.warning(this, getString(R.string.unauthorized_user), Toast.LENGTH_LONG).show();
    +        Toasty.warning(App.getInstance(), getString(R.string.unauthorized_user), Toast.LENGTH_LONG).show();
             ImageLoader.getInstance().clearDiskCache();
             ImageLoader.getInstance().clearMemoryCache();
             PrefHelper.clearKey("token");
    @@ -582,7 +577,7 @@ public abstract class BaseActivity<V extends BaseMvp.FAView, P extends BasePrese
             if (backPressTimer + 2000 > System.currentTimeMillis()) {
                 return true;
             } else {
    -            Toast.makeText(getBaseContext(), R.string.press_again_to_exit, Toast.LENGTH_SHORT).show();
    +            Toast.makeText(App.getInstance(), R.string.press_again_to_exit, Toast.LENGTH_SHORT).show();
             }
             backPressTimer = System.currentTimeMillis();
             return false;
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/gists/GistsListActivity.java b/app/src/main/java/com/fastaccess/ui/modules/gists/GistsListActivity.java
    index 0106f2d6..402b523c 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/gists/GistsListActivity.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/gists/GistsListActivity.java
    @@ -8,6 +8,7 @@ import android.support.design.widget.FloatingActionButton;
     import android.support.design.widget.TabLayout;
     import android.support.v4.app.Fragment;
     
    +import com.evernote.android.state.State;
     import com.fastaccess.R;
     import com.fastaccess.data.dao.FragmentPagerAdapterModel;
     import com.fastaccess.helper.ActivityHelper;
    @@ -24,7 +25,6 @@ import net.grandcentrix.thirtyinch.TiPresenter;
     
     import butterknife.BindView;
     import butterknife.OnClick;
    -import com.evernote.android.state.State;
     
     /**
      * Created by Kosh on 25 Mar 2017, 11:28 PM
    @@ -92,15 +92,7 @@ public class GistsListActivity extends BaseActivity {
             ActivityHelper.startReveal(this, new Intent(this, CreateGistActivity.class), fab);
         }
     
    -    private TabLayout.Tab getTab(int titleId) {
    -        return tabs.newTab().setText(titleId);
    -    }
    -
         private void setupTabs() {
    -        TabLayout.Tab tab1 = getTab(R.string.my_gists);
    -        TabLayout.Tab tab2 = getTab(R.string.public_gists);
    -        tabs.addTab(tab1);
    -        tabs.addTab(tab2);
             pager.setAdapter(new FragmentsPagerAdapter(getSupportFragmentManager(),
                     FragmentPagerAdapterModel.buildForGists(this)));
             tabs.setupWithViewPager(pager);
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    index c85ee38e..83aac22a 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    @@ -198,7 +198,6 @@ public class GistActivity extends BaseActivity<GistMvp.View, GistPresenter>
             hideProgress();
             Gist gistsModel = getPresenter().getGist();
             if (gistsModel == null) {
    -            finish();
                 return;
             }
             String url = gistsModel.getOwner() != null ? gistsModel.getOwner().getAvatarUrl() :
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistPresenter.java
    index 54ceaa07..47f61386 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistPresenter.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistPresenter.java
    @@ -8,6 +8,7 @@ import android.support.annotation.Nullable;
     import com.fastaccess.data.dao.model.Gist;
     import com.fastaccess.data.dao.model.Login;
     import com.fastaccess.helper.BundleConstant;
    +import com.fastaccess.helper.Logger;
     import com.fastaccess.helper.RxHelper;
     import com.fastaccess.provider.rest.RestProvider;
     import com.fastaccess.ui.base.mvp.presenter.BasePresenter;
    @@ -36,18 +37,18 @@ class GistPresenter extends BasePresenter<GistMvp.View> implements GistMvp.Prese
             }
             Bundle bundle = intent.getExtras();
             gistId = bundle.getString(BundleConstant.EXTRA);
    +        Logger.e(gistId);
             if (gist != null) {
                 checkStarring(gist.getGistId());
                 sendToView(GistMvp.View::onSetupDetails);
             } else if (gistId != null) {
                 checkStarring(gistId);
    -            makeRestCall(RestProvider.getGistService().getGist(gistId),
    -                    gistsModel -> {
    -                        this.gist = gistsModel;
    -                        sendToView(GistMvp.View::onSetupDetails);
    -                    });
    +            makeRestCall(RestProvider.getGistService().getGist(gistId), gistsModel -> {
    +                this.gist = gistsModel;
    +                sendToView(GistMvp.View::onSetupDetails);
    +            });
             } else {
    -            sendToView(GistMvp.View::onSetupDetails); // tell the activity to finish!
    +            sendToView(GistMvp.View::onSetupDetails);
             }
         }
     
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/files/GistFilesListFragment.java b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/files/GistFilesListFragment.java
    index 7dd9cec5..dfd4a173 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/files/GistFilesListFragment.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/files/GistFilesListFragment.java
    @@ -41,8 +41,7 @@ public class GistFilesListFragment extends BaseFragment<GistFilesListMvp.View, G
         public static GistFilesListFragment newInstance(@NonNull GithubFileModel gistsModel) {
             GistFilesListFragment view = new GistFilesListFragment();
             view.setArguments(Bundler.start()
    -                .putParcelableArrayList(BundleConstant.ITEM,
    -                        gistsModel.values() != null ? new ArrayList<>(gistsModel.values()) : new ArrayList<FilesListModel>())
    +                .putParcelableArrayList(BundleConstant.ITEM, new ArrayList<>(gistsModel.values()))
                     .end());
             return view;
         }
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/banner/BannerInfoPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/profile/banner/BannerInfoPresenter.java
    index 63766bf6..f85d6d0c 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/banner/BannerInfoPresenter.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/banner/BannerInfoPresenter.java
    @@ -2,71 +2,56 @@ package com.fastaccess.ui.modules.profile.banner;
     
     import android.support.annotation.NonNull;
     
    -import com.fastaccess.data.dao.CreateGistModel;
    -import com.fastaccess.data.dao.FilesListModel;
    -import com.fastaccess.data.dao.model.Login;
    -import com.fastaccess.helper.PrefGetter;
    -import com.fastaccess.helper.RxHelper;
    -import com.fastaccess.provider.rest.ImgurProvider;
    -import com.fastaccess.provider.rest.RestProvider;
     import com.fastaccess.ui.base.mvp.presenter.BasePresenter;
    -import com.fastaccess.ui.modules.profile.overview.ProfileOverviewMvp;
    -
    -import java.io.File;
    -import java.util.HashMap;
    -
    -import okhttp3.MediaType;
    -import okhttp3.RequestBody;
    -import io.reactivex.Observable;
     
     /**
      * Created by JediB on 5/25/2017.
      */
     
     public class BannerInfoPresenter extends BasePresenter<BannerInfoMvp.View> implements BannerInfoMvp.Presenter {
    -    @NonNull private Observable<String> getHeaderGist() {
    -        return RxHelper.getObserver(RestProvider.getGistService(true).getGistFile(ProfileOverviewMvp.HEADER_FST_URL));
    -    }
    +//    @NonNull private Observable<String> getHeaderGist() {
    +//        return RxHelper.getObserver(RestProvider.getGistService(true).getGistFile(ProfileOverviewMvp.HEADER_FST_URL));
    +//    }
     
         @Override public void onPostImage(@NonNull String path) {
    -        Login login = Login.getUser();
    -        RequestBody image = RequestBody.create(MediaType.parse("image/*"), new File(path));
    -        ImgurProvider.getImgurService().postImage("", image);
    -        makeRestCall(RxHelper.getObserver(ImgurProvider.getImgurService().postImage("", image))
    -                        .filter(imgurReponseModel -> imgurReponseModel != null && imgurReponseModel.getData() != null)
    -                        .map(imgurReponseModel -> imgurReponseModel.getData().getLink())
    -                        .flatMap(link -> getHeaderGist(), (imageLink, gistContent) -> {
    -                            boolean isReplace = false;
    -                            if (gistContent.contains(login.getLogin() + "->")) {
    -                                String[] splitByNewLine = gistContent.split("\n");
    -                                for (String s : splitByNewLine) {
    -                                    String[] splitByUser = s.split("->");
    -                                    if (login.getLogin().equalsIgnoreCase(splitByUser[0])) {
    -                                        gistContent = gistContent.replaceFirst(splitByUser[0] + "->" +
    -                                                splitByUser[1], login.getLogin() + "->" + imageLink);
    -                                        isReplace = true;
    -                                        break;
    -                                    }
    -                                }
    -                            }
    -                            PrefGetter.setProfileBackgroundUrl(imageLink);
    -                            if (!isReplace) {
    -                                gistContent += "\n" + login.getLogin() + "->" + imageLink;
    -                            }
    -                            return gistContent;
    -                        })
    -                        .map(s -> {
    -                            CreateGistModel createGistModel = new CreateGistModel();
    -                            createGistModel.setPublicGist(true);
    -                            HashMap<String, FilesListModel> modelHashMap = new HashMap<>();
    -                            FilesListModel file = new FilesListModel();
    -                            file.setFilename("header.fst");
    -                            file.setContent(s);
    -                            modelHashMap.put("header.fst", file);
    -                            createGistModel.setFiles(modelHashMap);
    -                            return createGistModel;
    -                        })
    -                        .flatMap(body -> RxHelper.getObserver(RestProvider.getGistService().editGist(body, ProfileOverviewMvp.HEADER_GIST_ID))),
    -                gist -> sendToView(BannerInfoMvp.View::onFinishedUploading));
    +//        Login login = Login.getUser();
    +//        RequestBody image = RequestBody.create(MediaType.parse("image/*"), new File(path));
    +//        ImgurProvider.getImgurService().postImage("", image);
    +//        makeRestCall(RxHelper.getObserver(ImgurProvider.getImgurService().postImage("", image))
    +//                        .filter(imgurReponseModel -> imgurReponseModel != null && imgurReponseModel.getData() != null)
    +//                        .map(imgurReponseModel -> imgurReponseModel.getData().getLink())
    +//                        .flatMap(link -> getHeaderGist(), (imageLink, gistContent) -> {
    +//                            boolean isReplace = false;
    +//                            if (gistContent.contains(login.getLogin() + "->")) {
    +//                                String[] splitByNewLine = gistContent.split("\n");
    +//                                for (String s : splitByNewLine) {
    +//                                    String[] splitByUser = s.split("->");
    +//                                    if (login.getLogin().equalsIgnoreCase(splitByUser[0])) {
    +//                                        gistContent = gistContent.replaceFirst(splitByUser[0] + "->" +
    +//                                                splitByUser[1], login.getLogin() + "->" + imageLink);
    +//                                        isReplace = true;
    +//                                        break;
    +//                                    }
    +//                                }
    +//                            }
    +//                            PrefGetter.setProfileBackgroundUrl(imageLink);
    +//                            if (!isReplace) {
    +//                                gistContent += "\n" + login.getLogin() + "->" + imageLink;
    +//                            }
    +//                            return gistContent;
    +//                        })
    +//                        .map(s -> {
    +//                            CreateGistModel createGistModel = new CreateGistModel();
    +//                            createGistModel.setPublicGist(true);
    +//                            HashMap<String, FilesListModel> modelHashMap = new HashMap<>();
    +//                            FilesListModel file = new FilesListModel();
    +//                            file.setFilename("header.fst");
    +//                            file.setContent(s);
    +//                            modelHashMap.put("header.fst", file);
    +//                            createGistModel.setFiles(modelHashMap);
    +//                            return createGistModel;
    +//                        })
    +//                        .flatMap(body -> RxHelper.getObserver(RestProvider.getGistService().editGist(body, ProfileOverviewMvp.HEADER_GIST_ID))),
    +//                gist -> sendToView(BannerInfoMvp.View::onFinishedUploading));
         }
     }
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsFragment.java
    index 11f6ed16..4ec5386f 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsFragment.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsFragment.java
    @@ -59,7 +59,6 @@ public class ProfileGistsFragment extends BaseFragment<ProfileGistsMvp.View, Pro
             adapter.setListener(getPresenter());
             getLoadMore().setCurrent_page(getPresenter().getCurrentPage(), getPresenter().getPreviousTotal());
             recycler.setAdapter(adapter);
    -        recycler.addKeyLineDivider();
             recycler.addOnScrollListener(getLoadMore());
             recycler.addDivider();
             if (getPresenter().getGists().isEmpty() && !getPresenter().isApiCalled()) {
    @@ -85,9 +84,7 @@ public class ProfileGistsFragment extends BaseFragment<ProfileGistsMvp.View, Pro
         }
     
         @Override public void showProgress(@StringRes int resId) {
    -
    -refresh.setRefreshing(true);
    -
    +        refresh.setRefreshing(true);
             stateLayout.showProgress();
         }
     
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsPresenter.java
    index db7669fe..58488ee4 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsPresenter.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/gists/ProfileGistsPresenter.java
    @@ -4,10 +4,12 @@ import android.support.annotation.NonNull;
     import android.support.annotation.Nullable;
     import android.view.View;
     
    +import com.annimon.stream.Stream;
     import com.fastaccess.data.dao.model.Gist;
     import com.fastaccess.helper.RxHelper;
     import com.fastaccess.provider.rest.RestProvider;
     import com.fastaccess.ui.base.mvp.presenter.BasePresenter;
    +import com.fastaccess.ui.modules.gists.gist.GistActivity;
     
     import java.util.ArrayList;
     
    @@ -59,13 +61,11 @@ class ProfileGistsPresenter extends BasePresenter<ProfileGistsMvp.View> implemen
                 sendToView(ProfileGistsMvp.View::hideProgress);
                 return;
             }
    -        makeRestCall(RestProvider.getGistService().getUserGists(parameter, RestProvider.PAGE_SIZE, page),
    +        makeRestCall(RestProvider.getGistService().getUserGists(parameter, page),
                     listResponse -> {
                         lastPage = listResponse.getLast();
    -                    if (getCurrentPage() == 1) {
    -                        manageObservable(Gist.save(listResponse.getItems(), parameter));
    -                    }
                         sendToView(view -> view.onNotifyAdapter(listResponse.getItems(), page));
    +                    manageObservable(Gist.save(Stream.of(listResponse.getItems()).toList()));
                     });
         }
     
    @@ -83,7 +83,7 @@ class ProfileGistsPresenter extends BasePresenter<ProfileGistsMvp.View> implemen
         }
     
         @Override public void onItemClick(int position, View v, Gist item) {
    -        if (getView() != null) getView().onStartGistView(item.getGistId());
    +        v.getContext().startActivity(GistActivity.createIntent(v.getContext(), item.getGistId()));
         }
     
         @Override public void onItemLongClick(int position, View v, Gist item) {}
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    index eecda587..d775260f 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    @@ -1,10 +1,6 @@
     package com.fastaccess.ui.modules.profile.overview;
     
    -import android.Manifest;
    -import android.app.Activity;
     import android.content.Context;
    -import android.content.Intent;
    -import android.content.pm.PackageManager;
     import android.graphics.Bitmap;
     import android.graphics.drawable.BitmapDrawable;
     import android.os.Build;
    @@ -30,10 +26,8 @@ import com.fastaccess.data.dao.model.User;
     import com.fastaccess.helper.ActivityHelper;
     import com.fastaccess.helper.BundleConstant;
     import com.fastaccess.helper.Bundler;
    -import com.fastaccess.helper.FileHelper;
     import com.fastaccess.helper.InputHelper;
     import com.fastaccess.helper.ParseDateFormat;
    -import com.fastaccess.helper.PrefGetter;
     import com.fastaccess.provider.emoji.EmojiParser;
     import com.fastaccess.ui.adapter.ProfileOrgsAdapter;
     import com.fastaccess.ui.base.BaseFragment;
    @@ -52,7 +46,6 @@ import java.util.List;
     
     import butterknife.BindView;
     import butterknife.OnClick;
    -import es.dmoral.toasty.Toasty;
     
     import static android.view.Gravity.TOP;
     import static android.view.View.GONE;
    @@ -91,8 +84,6 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
         @BindView(R.id.contributionCard) CardView contributionCard;
         private ProfilePagerMvp.View profileCallback;
     
    -    private static int READ_REQUEST_CODE = 256;
    -
         public static ProfileOverviewFragment newInstance(@NonNull String login) {
             ProfileOverviewFragment view = new ProfileOverviewFragment();
             view.setArguments(Bundler.start().put(BundleConstant.EXTRA, login).end());
    @@ -114,12 +105,6 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
             if (userModel != null) ActivityHelper.startCustomTab(getActivity(), userModel.getAvatarUrl());
         }
     
    -    @OnClick({R.id.chooseBanner, R.id.banner_edit}) public void chooseBanner() {
    -        if (ActivityHelper.checkAndRequestReadWritePermission(getActivity())) {
    -            showFileChooser();
    -        }
    -    }
    -
         @Override public void onAttach(Context context) {
             super.onAttach(context);
             if (getParentFragment() instanceof ProfilePagerMvp.View) {
    @@ -154,11 +139,6 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
             if (isMeOrOrganization()) {
                 followBtn.setVisibility(GONE);
             }
    -//        if (getPresenter().getLogin().equals(Login.getUser().getLogin()) && PrefHelper.getBoolean("banner_learned"))
    -//            chooseBanner.setVisibility(VISIBLE);
    -//        if (Login.getUser().getLogin().equalsIgnoreCase(getPresenter().getLogin())) {
    -//            onImagePosted(PrefGetter.getProfileBackgroundUrl());
    -//        }
         }
     
         @NonNull @Override public ProfileOverviewPresenter providePresenter() {
    @@ -178,12 +158,12 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
                 description.setVisibility(GONE);
             }
             avatarLayout.setUrl(userModel.getAvatarUrl(), null);
    -        avatarLayout.setOnAvatarClick(v -> userInformation.callOnClick());
    +        avatarLayout.setOnClickListener(v -> userInformation.performClick());
             organization.setText(InputHelper.toNA(userModel.getCompany()));
             location.setText(InputHelper.toNA(userModel.getLocation()));
             email.setText(InputHelper.toNA(userModel.getEmail()));
             link.setText(InputHelper.toNA(userModel.getBlog()));
    -        joined.setText(userModel.getCreatedAt() != null ? ParseDateFormat.getTimeAgo(userModel.getCreatedAt()) : "N/A");
    +        joined.setText(ParseDateFormat.getTimeAgo(userModel.getCreatedAt()));
             ViewGroup parent = (ViewGroup) organization.getParent();
             if (organization.getText().equals("N/A")) {
                 int i = parent.indexOfChild(organization);
    @@ -216,18 +196,6 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
                     .append(getString(R.string.following))
                     .append("\n")
                     .bold(String.valueOf(userModel.getFollowing())));
    -//        if (userModel.getLogin().equals(Login.getUser().getLogin()))
    -//            if (headerImage.getVisibility() == GONE) {
    -//                if (PrefHelper.getBoolean("banner_learned")) return;
    -//                headerImage.setBackground(getResources().getDrawable(R.drawable.header));
    -//                headerImage.setVisibility(VISIBLE);
    -//                headerImage.setOnClickListener(view -> {
    -//                    PrefHelper.set("banner_learned", true);
    -//                    Intent intent = new Intent(getContext(), BannerInfoActivity.class);
    -//                    startActivityForResult(intent, BundleConstant.REVIEW_REQUEST_CODE);
    -//                });
    -//            }
    -
         }
     
         @Override public void invalidateFollowBtn() {
    @@ -259,8 +227,8 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
                 orgsList.setAdapter(adapter);
                 orgsCard.setVisibility(VISIBLE);
                 organizationsCaption.setVisibility(VISIBLE);
    -            ((GridManager) orgsList.getLayoutManager()).setIconSize(getResources().getDimensionPixelSize(R.dimen.header_icon_zie) +
    -                    getResources().getDimensionPixelSize(R.dimen.spacing_xs_large));
    +            ((GridManager) orgsList.getLayoutManager()).setIconSize(getResources().getDimensionPixelSize(R.dimen.header_icon_zie) + getResources()
    +                    .getDimensionPixelSize(R.dimen.spacing_xs_large));
             } else {
                 organizationsCaption.setVisibility(GONE);
                 orgsCard.setVisibility(GONE);
    @@ -335,36 +303,6 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
             }
         }
     
    -    @Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
    -        if (resultCode == Activity.RESULT_OK) {
    -            if (requestCode == BundleConstant.REQUEST_CODE) {
    -                if (data != null) {
    -                    String path = FileHelper.getPath(getContext(), data.getData());
    -                    if (path == null) {
    -                        showMessage(R.string.error, R.string.image_error);
    -                        return;
    -                    }
    -                    getPresenter().onPostImage(path);
    -                }
    -            } else {
    -                onImagePosted(PrefGetter.getProfileBackgroundUrl());
    -            }
    -        }
    -    }
    -
    -    @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    -        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    -        if (requestCode == READ_REQUEST_CODE) {
    -            if (permissions[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE)) {
    -                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    -                    showFileChooser();
    -                } else {
    -                    Toasty.error(getContext(), getString(R.string.permission_failed)).show();
    -                }
    -            }
    -        }
    -    }
    -
         private void onHideProgress() {
             hideProgress();
         }
    @@ -374,11 +312,4 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
                     (userModel != null && userModel.getType() != null && !userModel.getType().equalsIgnoreCase("user"));
         }
     
    -    private void showFileChooser() {
    -        Intent intent = new Intent();
    -        intent.setType("image/*");
    -        intent.setAction(Intent.ACTION_GET_CONTENT);
    -        startActivityForResult(Intent.createChooser(intent, getString(R.string.select_picture)), BundleConstant.REQUEST_CODE);
    -    }
    -
     }
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewMvp.java b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewMvp.java
    index e316c415..9af84dd0 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewMvp.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewMvp.java
    @@ -18,10 +18,6 @@ import java.util.List;
     
     public interface ProfileOverviewMvp {
     
    -    String HEADER_FST_URL = "https://gist.githubusercontent" +
    -            ".com/k0shk0sh/44c5d0ba29d179c9e78bc892e8573138/raw/4d443b23dda00c568fc6905b3c28103d55d00b51/header.fst";
    -    String HEADER_GIST_ID = "44c5d0ba29d179c9e78bc892e8573138";
    -
         interface View extends BaseMvp.FAView {
             void onInitViews(@Nullable User userModel);
     
    @@ -59,7 +55,5 @@ public interface ProfileOverviewMvp {
             @NonNull ArrayList<ContributionsDay> getContributions();
     
             @NonNull String getLogin();
    -
    -        void onPostImage(@NonNull String path);
         }
     }
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewPresenter.java
    index 61f69e77..b4d0e62e 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewPresenter.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewPresenter.java
    @@ -5,26 +5,18 @@ import android.support.annotation.NonNull;
     import android.support.annotation.Nullable;
     import android.text.TextUtils;
     
    -import com.fastaccess.data.dao.CreateGistModel;
    -import com.fastaccess.data.dao.FilesListModel;
     import com.fastaccess.data.dao.model.Login;
     import com.fastaccess.data.dao.model.User;
     import com.fastaccess.helper.BundleConstant;
     import com.fastaccess.helper.InputHelper;
    -import com.fastaccess.helper.PrefGetter;
     import com.fastaccess.helper.RxHelper;
    -import com.fastaccess.provider.rest.ImgurProvider;
     import com.fastaccess.provider.rest.RestProvider;
     import com.fastaccess.ui.base.mvp.presenter.BasePresenter;
     import com.fastaccess.ui.widgets.contributions.ContributionsDay;
     import com.fastaccess.ui.widgets.contributions.ContributionsProvider;
     
    -import java.io.File;
     import java.util.ArrayList;
    -import java.util.HashMap;
     
    -import okhttp3.MediaType;
    -import okhttp3.RequestBody;
     import io.reactivex.Observable;
     
     /**
    @@ -126,52 +118,6 @@ class ProfileOverviewPresenter extends BasePresenter<ProfileOverviewMvp.View> im
             return login;
         }
     
    -    @Override public void onPostImage(@NonNull String path) {
    -        Login login = Login.getUser();
    -        RequestBody image = RequestBody.create(MediaType.parse("image/*"), new File(path));
    -        ImgurProvider.getImgurService().postImage("", image);
    -        makeRestCall(RxHelper.getObserver(ImgurProvider.getImgurService().postImage("", image))
    -                        .filter(imgurReponseModel -> imgurReponseModel != null && imgurReponseModel.getData() != null)
    -                        .map(imgurReponseModel -> imgurReponseModel.getData().getLink())
    -                        .flatMap(link -> getHeaderGist(), (imageLink, gistContent) -> {
    -                            boolean isReplace = false;
    -                            if (gistContent.contains(login.getLogin() + "->")) {
    -                                String[] splitByNewLine = gistContent.split("\n");
    -                                for (String s : splitByNewLine) {
    -                                    String[] splitByUser = s.split("->");
    -                                    if (login.getLogin().equalsIgnoreCase(splitByUser[0])) {
    -                                        gistContent = gistContent.replaceFirst(splitByUser[0] + "->" +
    -                                                splitByUser[1], login.getLogin() + "->" + imageLink);
    -                                        isReplace = true;
    -                                        break;
    -                                    }
    -                                }
    -                            }
    -                            PrefGetter.setProfileBackgroundUrl(imageLink);
    -                            if (!isReplace) {
    -                                gistContent += "\n" + login.getLogin() + "->" + imageLink;
    -                            }
    -                            return gistContent;
    -                        })
    -                        .map(s -> {
    -                            CreateGistModel createGistModel = new CreateGistModel();
    -                            createGistModel.setPublicGist(true);
    -                            HashMap<String, FilesListModel> modelHashMap = new HashMap<>();
    -                            FilesListModel file = new FilesListModel();
    -                            file.setFilename("header.fst");
    -                            file.setContent(s);
    -                            modelHashMap.put("header.fst", file);
    -                            createGistModel.setFiles(modelHashMap);
    -                            return createGistModel;
    -                        })
    -                        .flatMap(body -> RxHelper.getObserver(RestProvider.getGistService().editGist(body, ProfileOverviewMvp.HEADER_GIST_ID))),
    -                gist -> sendToView(view -> view.onImagePosted(PrefGetter.getProfileBackgroundUrl())));
    -    }
    -
    -    @NonNull private Observable<String> getHeaderGist() {
    -        return RxHelper.getObserver(RestProvider.getGistService(true).getGistFile(ProfileOverviewMvp.HEADER_FST_URL));
    -    }
    -
         private void loadContributions() {
             String url = String.format(URL, login);
             manageDisposable(RxHelper.getObserver(RestProvider.getContribution().getContributions(url))
    @@ -194,26 +140,4 @@ class ProfileOverviewPresenter extends BasePresenter<ProfileOverviewMvp.View> im
                         sendToView(view -> view.onInitOrgs(userOrgs));
                     }, Throwable::printStackTrace));
         }
    -
    -    private void loadUrlBackgroundImage() {
    -        if (Login.getUser().getLogin().equalsIgnoreCase(login)) {
    -            if (PrefGetter.getProfileBackgroundUrl() == null) {
    -                manageDisposable(getHeaderGist()
    -                        .flatMap(s -> RxHelper.getObserver(Observable.fromArray(s.split("\n"))))
    -                        .flatMap(s -> RxHelper.getObserver(Observable.just(s.split("->"))))
    -                        .filter(strings -> strings != null && strings[0].equalsIgnoreCase(login))
    -                        .map(strings -> strings[1])
    -                        .subscribe(s -> sendToView(view -> view.onImagePosted(s)), Throwable::printStackTrace));
    -            } else {
    -                sendToView(view -> view.onImagePosted(PrefGetter.getProfileBackgroundUrl()));
    -            }
    -        } else {
    -            manageDisposable(getHeaderGist()
    -                    .flatMap(s -> RxHelper.getObserver(Observable.fromArray(s.split("\n"))))
    -                    .flatMap(s -> RxHelper.getObserver(Observable.just(s.split("->"))))
    -                    .filter(strings -> strings != null && strings[0].equalsIgnoreCase(login))
    -                    .map(strings -> strings[1])
    -                    .subscribe(s -> sendToView(view -> view.onImagePosted(s)), Throwable::printStackTrace));
    -        }
    -    }
     }
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt
    index 908fa28c..8b8db048 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt
    +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragment.kt
    @@ -5,7 +5,7 @@ import android.support.v4.widget.SwipeRefreshLayout
     import android.view.View
     import com.evernote.android.state.State
     import com.fastaccess.R
    -import com.fastaccess.data.dao.TrendingResponse
    +import com.fastaccess.data.dao.TrendingModel
     import com.fastaccess.ui.adapter.TrendingAdapter
     import com.fastaccess.ui.base.BaseFragment
     import com.fastaccess.ui.widgets.StateLayout
    @@ -42,7 +42,7 @@ class TrendingFragment : BaseFragment<TrendingFragmentMvp.View, TrendingFragment
             recycler.adapter = adapter
         }
     
    -    override fun onNotifyAdapter(items: TrendingResponse) {
    +    override fun onNotifyAdapter(items: TrendingModel) {
             adapter.addItem(items)
         }
     
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentMvp.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentMvp.kt
    index f9123ea6..735d27d4 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentMvp.kt
    +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentMvp.kt
    @@ -1,7 +1,7 @@
     package com.fastaccess.ui.modules.trending.fragment
     
     
    -import com.fastaccess.data.dao.TrendingResponse
    +import com.fastaccess.data.dao.TrendingModel
     import com.fastaccess.ui.base.mvp.BaseMvp
     import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
     
    @@ -11,13 +11,12 @@ import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder
     
     interface TrendingFragmentMvp {
         interface View : BaseMvp.FAView {
    -        fun onNotifyAdapter(items: TrendingResponse)
    +        fun onNotifyAdapter(items: TrendingModel)
             fun onSetQuery(lang: String, since: String)
         }
     
    -    interface Presenter : BaseViewHolder.OnItemClickListener<TrendingResponse> {
    +    interface Presenter : BaseViewHolder.OnItemClickListener<TrendingModel> {
             fun onCallApi(lang: String, since: String)
    -
    -        fun getTendingList(): ArrayList<TrendingResponse>
    +        fun getTendingList(): ArrayList<TrendingModel>
         }
     }
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentPresenter.kt b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentPresenter.kt
    index 46979792..337f334d 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentPresenter.kt
    +++ b/app/src/main/java/com/fastaccess/ui/modules/trending/fragment/TrendingFragmentPresenter.kt
    @@ -1,11 +1,16 @@
     package com.fastaccess.ui.modules.trending.fragment
     
     import android.view.View
    -import com.fastaccess.data.dao.TrendingResponse
    +import com.fastaccess.data.dao.TrendingModel
    +import com.fastaccess.helper.InputHelper
    +import com.fastaccess.helper.Logger
     import com.fastaccess.helper.RxHelper
    -import com.fastaccess.provider.rest.jsoup.RetroJsoupProvider
    +import com.fastaccess.provider.rest.jsoup.JsoupProvider
     import com.fastaccess.ui.base.mvp.presenter.BasePresenter
     import com.fastaccess.ui.modules.repos.RepoPagerActivity
    +import io.reactivex.Observable
    +import org.jsoup.Jsoup
    +import org.jsoup.nodes.Document
     
     /**
      * Created by Kosh on 30 May 2017, 11:04 PM
    @@ -13,25 +18,49 @@ import com.fastaccess.ui.modules.repos.RepoPagerActivity
     
     class TrendingFragmentPresenter : BasePresenter<TrendingFragmentMvp.View>(), TrendingFragmentMvp.Presenter {
     
    -    private val trendingList: ArrayList<TrendingResponse> = ArrayList()
    +    private val trendingList: ArrayList<TrendingModel> = ArrayList()
     
    -    override fun getTendingList(): ArrayList<TrendingResponse> {
    +    override fun getTendingList(): ArrayList<TrendingModel> {
             return trendingList
         }
     
    -    override fun onItemLongClick(position: Int, v: View?, item: TrendingResponse?) {}
    +    override fun onItemLongClick(position: Int, v: View?, item: TrendingModel?) {}
     
    -    override fun onItemClick(position: Int, v: View?, item: TrendingResponse?) {
    +    override fun onItemClick(position: Int, v: View?, item: TrendingModel?) {
             val split = item?.title?.trim()?.split("/")!!
             v?.context!!.startActivity(RepoPagerActivity.createIntent(v.context!!, split[1].trim(), split[0].trim()))
         }
     
         override fun onCallApi(lang: String, since: String) {
    -        manageViewDisposable(RxHelper.getObserver(RetroJsoupProvider.getTrendingService(since, lang).trending)
    +        manageViewDisposable(RxHelper.getObserver(JsoupProvider.getTrendingService().getTrending(
    +                (if (!InputHelper.isEmpty(lang)) lang.replace(" ".toRegex(), "-") else "").toLowerCase(), since))
    +                .flatMap { s -> RxHelper.getObserver(getTrendingObservable(s)) }
                     .doOnSubscribe { sendToView { it.showProgress(0) } }
                     .subscribe({ response -> sendToView { view -> view.onNotifyAdapter(response) } },
                             { throwable -> onError(throwable) }, { sendToView({ it.hideProgress() }) }))
         }
     
     
    +    fun getTrendingObservable(response: String): Observable<TrendingModel> {
    +        return Observable.fromPublisher { s ->
    +            val document: Document = Jsoup.parse(response, "")
    +            val repoList = document.select(".repo-list")
    +            if (repoList.isNotEmpty()) {
    +                val list = repoList.select("li")
    +                if (list.isNotEmpty()) {
    +                    list.onEach {
    +                        val title = it.select(".d-inline-block > h3 > a").text()
    +                        val description = it.select(".py-1 > p").text()
    +                        val stars = it.select(".f6 > a[href*=/stargazers]").text()
    +                        val forks = it.select(".f6 > a[href*=/network]").text()
    +                        val todayStars = it.select(".f6 > span.float-right").text()
    +                        val language = it.select(".f6 > span[itemprop=programmingLanguage]").text()
    +                        Logger.e(title, description, stars, forks, todayStars, language)
    +                        s.onNext(TrendingModel(title, description, language, stars, forks, todayStars))
    +                    }
    +                }
    +            }
    +            s.onComplete()
    +        }
    +    }
     }
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java b/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java
    index ca2638cd..09ca6eb6 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/user/UserPagerActivity.java
    @@ -1,5 +1,7 @@
     package com.fastaccess.ui.modules.user;
     
    +import android.app.Application;
    +import android.app.Service;
     import android.content.Context;
     import android.content.Intent;
     import android.os.Bundle;
    @@ -68,6 +70,9 @@ public class UserPagerActivity extends BaseActivity<UserPagerMvp.View, UserPager
                     .put(BundleConstant.EXTRA, login)
                     .put(BundleConstant.EXTRA_TYPE, isOrg)
                     .end());
    +        if (context instanceof Service || context instanceof Application) {
    +            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    +        }
             return intent;
         }
     
    diff --git a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java
    index 1374aae5..c9dd2463 100644
    --- a/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java
    +++ b/app/src/main/java/com/fastaccess/ui/widgets/AvatarLayout.java
    @@ -37,10 +37,8 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener {
         @Nullable private String login;
         private boolean isOrg;
         private Toast toast;
    -    private OnClickListener onClickListener;
     
         @OnClick(R.id.avatar) void onClick(@NonNull View view) {
    -        if (onClickListener != null) onClickListener.onClick(view);
             if (InputHelper.isEmpty(login)) return;
             UserPagerActivity.startActivity(view.getContext(), login, isOrg);
         }
    @@ -74,11 +72,6 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener {
             super.onAttachedToWindow();
         }
     
    -    @Override protected void onDetachedFromWindow() {
    -        onClickListener = null;
    -        super.onDetachedFromWindow();
    -    }
    -
         @Override protected void onFinishInflate() {
             super.onFinishInflate();
             inflate(getContext(), R.layout.avatar_layout, this);
    @@ -130,8 +123,4 @@ public class AvatarLayout extends FrameLayout implements ImageLoadingListener {
                 }
             }
         }
    -
    -    public void setOnAvatarClick(OnClickListener onClickListener) {
    -        this.onClickListener = onClickListener;
    -    }
     }
    diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/scroll/RecyclerFastScroller.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/scroll/RecyclerFastScroller.java
    index fc28a3d8..7eb75884 100755
    --- a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/scroll/RecyclerFastScroller.java
    +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/scroll/RecyclerFastScroller.java
    @@ -56,8 +56,7 @@ public class RecyclerFastScroller extends FrameLayout {
         private boolean mHideOverride;
         private RecyclerView.Adapter mAdapter;
         private RecyclerView.AdapterDataObserver mAdapterObserver = new RecyclerView.AdapterDataObserver() {
    -        @Override
    -        public void onChanged() {
    +        @Override public void onChanged() {
                 super.onChanged();
                 requestLayout();
             }
    @@ -98,7 +97,7 @@ public class RecyclerFastScroller extends FrameLayout {
             }
     
             mBar.setLayoutParams(new LayoutParams(mTouchTargetWidth, ViewGroup.LayoutParams.MATCH_PARENT, GravityCompat.END));
    -        mHandle.setLayoutParams(new LayoutParams(mTouchTargetWidth, ViewGroup.LayoutParams.MATCH_PARENT, GravityCompat.END));
    +        mHandle.setLayoutParams(new LayoutParams(mTouchTargetWidth, convertDpToPx(context, 80), GravityCompat.END));
     
             updateHandleColorsAndInset();
             updateBarColorAndInset();
    @@ -126,26 +125,18 @@ public class RecyclerFastScroller extends FrameLayout {
                 private float mInitialBarHeight;
                 private float mLastPressedYAdjustedToInitial;
     
    -            @Override
    -            public boolean onTouch(View v, MotionEvent event) {
    +            @Override public boolean onTouch(View v, MotionEvent event) {
                     if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
                         mHandle.setPressed(true);
                         mRecyclerView.stopScroll();
    -
                         mInitialBarHeight = mBar.getHeight();
                         mLastPressedYAdjustedToInitial = event.getY() + mHandle.getY() + mBar.getY();
                     } else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
                         float newHandlePressedY = event.getY() + mHandle.getY() + mBar.getY();
                         int barHeight = mBar.getHeight();
    -                    float newHandlePressedYAdjustedToInitial =
    -                            newHandlePressedY + (mInitialBarHeight - barHeight);
    -
    -                    float deltaPressedYFromLastAdjustedToInitial =
    -                            newHandlePressedYAdjustedToInitial - mLastPressedYAdjustedToInitial;
    -
    -                    int dY = (int) ((deltaPressedYFromLastAdjustedToInitial / mInitialBarHeight) *
    -                            (mRecyclerView.computeVerticalScrollRange()));
    -
    +                    float newHandlePressedYAdjustedToInitial = newHandlePressedY + (mInitialBarHeight - barHeight);
    +                    float deltaPressedYFromLastAdjustedToInitial = newHandlePressedYAdjustedToInitial - mLastPressedYAdjustedToInitial;
    +                    int dY = (int) ((deltaPressedYFromLastAdjustedToInitial / mInitialBarHeight) * (mRecyclerView.computeVerticalScrollRange()));
                         if (mRecyclerView != null) {
                             try {
                                 mRecyclerView.scrollBy(0, dY);
    @@ -156,17 +147,14 @@ public class RecyclerFastScroller extends FrameLayout {
                                 t.printStackTrace();
                             }
                         }
    -
                         mLastPressedYAdjustedToInitial = newHandlePressedYAdjustedToInitial;
                     } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                         mLastPressedYAdjustedToInitial = -1;
    -
                         mRecyclerView.stopNestedScroll();
    -
                         mHandle.setPressed(false);
                         postAutoHide();
                     }
    -
    +                updateScroller();
                     return true;
                 }
             });
    @@ -176,8 +164,11 @@ public class RecyclerFastScroller extends FrameLayout {
     
         @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
             super.onLayout(changed, left, top, right, bottom);
    -        if (mRecyclerView == null) return;
    +        updateScroller();
    +    }
     
    +    private void updateScroller() {
    +        if (mRecyclerView == null) return;
             int scrollOffset = mRecyclerView.computeVerticalScrollOffset();
             int verticalScrollRange = mRecyclerView.computeVerticalScrollRange() + mRecyclerView.getPaddingBottom();
     
    @@ -198,8 +189,7 @@ public class RecyclerFastScroller extends FrameLayout {
             mHideOverride = false;
     
             float y = ratio * (barHeight - calculatedHandleHeight);
    -
    -        mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight);
    +        mHandle.setY(y);
         }
     
         private void updateHandleColorsAndInset() {
    @@ -290,18 +280,18 @@ public class RecyclerFastScroller extends FrameLayout {
             }
         }
     
    -    @ColorInt public static int resolveColor(Context context, @AttrRes int color) {
    +    @ColorInt private int resolveColor(Context context, @AttrRes int color) {
             TypedArray a = context.obtainStyledAttributes(new int[]{color});
             int resId = a.getColor(0, 0);
             a.recycle();
             return resId;
         }
     
    -    private static boolean isRTL(Context context) {
    +    private boolean isRTL(Context context) {
             return context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
         }
     
    -    public static int convertDpToPx(Context context, float dp) {
    +    private int convertDpToPx(Context context, float dp) {
             return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f);
         }
     
    diff --git a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
    index ea303608..480c2d46 100755
    --- a/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
    +++ b/app/src/main/java/com/prettifier/pretty/helper/PrettifyHelper.java
    @@ -8,7 +8,7 @@ import android.support.annotation.NonNull;
     
     public class PrettifyHelper {
     
    -    @NonNull private static String getHtmlContent(@NonNull String css, @NonNull String text, @NonNull String wrapStyle) {
    +    @NonNull private static String getHtmlContent(@NonNull String css, @NonNull String text, @NonNull String wrapStyle, boolean isDark) {
             return "<!DOCTYPE html>\n" +
                     "<html>\n" +
                     "<head>\n" +
    @@ -16,7 +16,7 @@ public class PrettifyHelper {
                     "    <link rel=\"stylesheet\" href=\"./styles/" + css + "\">\n" +
                     "    " + wrapStyle + "\n" +
                     "</head>\n" +
    -                "<body onload=\"" + (textTooLarge(text) ? "" : "prettyPrint()") + "\">\n" +
    +                "<body style=\"" + (isDark && textTooLarge(text) ? "color:white;" : "") + "\" onload=\"" + (textTooLarge(text) ? "" : "PR.prettyPrint()") + "\">\n" +
                     "<pre class=\"prettyprint linenums\">" + text + "</pre>\n" +
                     "<script src=\"./js/prettify.js\"></script>\n" +
                     "<script>\n" +
    @@ -56,7 +56,7 @@ public class PrettifyHelper {
     
     
         @NonNull public static String generateContent(@NonNull String source, boolean isDark, boolean wrap) {
    -        return getHtmlContent(getStyle(isDark), getFormattedSource(source), wrap ? WRAPPED_STYLE : "");
    +        return getHtmlContent(getStyle(isDark), getFormattedSource(source), wrap ? WRAPPED_STYLE : "", isDark);
         }
     
         @NonNull private static String getFormattedSource(@NonNull String source) {
    diff --git a/app/src/main/res/drawable/ic_language_small.xml b/app/src/main/res/drawable/ic_language_small.xml
    new file mode 100644
    index 00000000..73171be3
    --- /dev/null
    +++ b/app/src/main/res/drawable/ic_language_small.xml
    @@ -0,0 +1,11 @@
    +<vector xmlns:android="http://schemas.android.com/apk/res/android"
    +        xmlns:tools="http://schemas.android.com/tools"
    +        android:width="16dp"
    +        android:height="16dp"
    +        android:viewportHeight="24.0"
    +        android:viewportWidth="24.0">
    +    <path
    +        android:fillColor="?icon_color"
    +        android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"
    +        tools:ignore="VectorPath"/>
    +</vector>
    diff --git a/app/src/main/res/layouts/row_layouts/layout/trending_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/trending_row_item.xml
    index c0dbf2d0..e3bb8b99 100644
    --- a/app/src/main/res/layouts/row_layouts/layout/trending_row_item.xml
    +++ b/app/src/main/res/layouts/row_layouts/layout/trending_row_item.xml
    @@ -46,10 +46,10 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_micro"
    +            android:orientation="horizontal"
                 app:auto_gravity="start"
                 app:auto_orientation="horizontal">
     
    -
                 <com.fastaccess.ui.widgets.FontTextView
                     android:id="@+id/stars"
                     style="@style/TextAppearance.AppCompat.Small"
    @@ -91,6 +91,20 @@
                     app:drawableColor="?android:attr/textColorSecondary"
                     tools:text="50 minutes ago"/>
     
    +            <com.fastaccess.ui.widgets.FontTextView
    +                android:id="@+id/language"
    +                style="@style/TextAppearance.AppCompat.Caption"
    +                android:layout_width="wrap_content"
    +                android:layout_height="wrap_content"
    +                android:layout_gravity="center"
    +                android:layout_marginStart="@dimen/spacing_normal"
    +                android:drawablePadding="@dimen/spacing_micro"
    +                android:drawableStart="@drawable/ic_language_small"
    +                android:gravity="start|center"
    +                android:textColor="?android:attr/textColorSecondary"
    +                android:visibility="gone"
    +                app:drawableColor="?android:attr/textColorSecondary"
    +                tools:text="10000"/>
     
             </com.fastaccess.ui.widgets.AutoLinearLayout>
         </LinearLayout>
    diff --git a/build.gradle b/build.gradle
    index 8148fdba..87117820 100644
    --- a/build.gradle
    +++ b/build.gradle
    @@ -15,7 +15,6 @@ buildscript {
             espresseVersion = '2.2.2'
             requery = '1.3.2'
             kotlin_version = '1.1.2-4'
    -        retrojsoupVersion = '1.0.3'
         }
     
         repositories {
    
    From 6e1c2e5489b64acbb281e8e3feddefca44ac667f Mon Sep 17 00:00:00 2001
    From: Kosh <kosh20111@gmail.com>
    Date: Tue, 6 Jun 2017 22:53:57 +0800
    Subject: [PATCH 12/14] this commit fixes notifications not all loaded.
    
    ---
     .../com/fastaccess/data/dao/TrendingModel.kt  |  39 +++
     .../NotificationSchedulerJobTask.java         | 233 ++++++++----------
     2 files changed, 144 insertions(+), 128 deletions(-)
     create mode 100644 app/src/main/java/com/fastaccess/data/dao/TrendingModel.kt
    
    diff --git a/app/src/main/java/com/fastaccess/data/dao/TrendingModel.kt b/app/src/main/java/com/fastaccess/data/dao/TrendingModel.kt
    new file mode 100644
    index 00000000..db430dbc
    --- /dev/null
    +++ b/app/src/main/java/com/fastaccess/data/dao/TrendingModel.kt
    @@ -0,0 +1,39 @@
    +package com.fastaccess.data.dao
    +
    +import android.os.Parcel
    +import android.os.Parcelable
    +
    +data class TrendingModel(
    +        val title: String? = null,
    +        val description: String? = null,
    +        val language: String? = null,
    +        val stars: String? = null,
    +        val forks: String? = null,
    +        val todayStars: String? = null) : Parcelable {
    +    companion object {
    +        @JvmField val CREATOR: Parcelable.Creator<TrendingModel> = object : Parcelable.Creator<TrendingModel> {
    +            override fun createFromParcel(source: Parcel): TrendingModel = TrendingModel(source)
    +            override fun newArray(size: Int): Array<TrendingModel?> = arrayOfNulls(size)
    +        }
    +    }
    +
    +    constructor(source: Parcel) : this(
    +            source.readString(),
    +            source.readString(),
    +            source.readString(),
    +            source.readString(),
    +            source.readString(),
    +            source.readString()
    +    )
    +
    +    override fun describeContents() = 0
    +
    +    override fun writeToParcel(dest: Parcel, flags: Int) {
    +        dest.writeString(title)
    +        dest.writeString(description)
    +        dest.writeString(language)
    +        dest.writeString(stars)
    +        dest.writeString(forks)
    +        dest.writeString(todayStars)
    +    }
    +}
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    index 13c4a92a..be0f2c4b 100644
    --- a/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    +++ b/app/src/main/java/com/fastaccess/provider/tasks/notification/NotificationSchedulerJobTask.java
    @@ -5,6 +5,7 @@ import android.app.PendingIntent;
     import android.content.Context;
     import android.content.Intent;
     import android.graphics.Bitmap;
    +import android.graphics.BitmapFactory;
     import android.support.annotation.NonNull;
     import android.support.annotation.Nullable;
     import android.support.v4.app.NotificationCompat;
    @@ -18,6 +19,7 @@ import com.fastaccess.data.dao.model.Login;
     import com.fastaccess.data.dao.model.Notification;
     import com.fastaccess.helper.AppHelper;
     import com.fastaccess.helper.InputHelper;
    +import com.fastaccess.helper.Logger;
     import com.fastaccess.helper.ParseDateFormat;
     import com.fastaccess.helper.PrefGetter;
     import com.fastaccess.helper.RxHelper;
    @@ -33,6 +35,7 @@ import com.firebase.jobdispatcher.RetryStrategy;
     import com.firebase.jobdispatcher.Trigger;
     import com.nostra13.universalimageloader.core.ImageLoader;
     import com.nostra13.universalimageloader.core.assist.FailReason;
    +import com.nostra13.universalimageloader.core.assist.ImageSize;
     import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
     
     import java.util.List;
    @@ -130,19 +133,37 @@ public class NotificationSchedulerJobTask extends JobService {
                     .subscribeOn(Schedulers.io())
                     .filter(notification -> notification.isUnread() && first.getId() != notification.getId())
                     .take(10)
    -                .flatMap(notification -> RestProvider.getNotificationService()
    -                        .getComment(notification.getSubject().getLatestCommentUrl())
    -                        .subscribeOn(Schedulers.io()), (thread, comment) -> {
    -                    String url = comment.getUser().getAvatarUrl();
    -                    if (!InputHelper.isEmpty(thread.getSubject().getLatestCommentUrl())) {
    -                        getNotificationWithComment(context, accentColor, thread, comment, url);
    -                        return null;
    +                .flatMap(notification -> {
    +                    Logger.e(notification.getSubject().getTitle());
    +                    if (notification.getSubject() != null && notification.getSubject().getLatestCommentUrl() != null) {
    +                        return RestProvider.getNotificationService()
    +                                .getComment(notification.getSubject().getLatestCommentUrl())
    +                                .subscribeOn(Schedulers.io());
    +                    } else {
    +                        return Observable.empty();
                         }
    -                    showNotificationWithoutComment(context, accentColor, thread, url);
    -                    return thread;
    +                }, (thread, comment) -> {
    +                    CustomNotificationModel customNotificationModel = new CustomNotificationModel();
    +                    String url;
    +                    if (comment != null && comment.getUser() != null) {
    +                        url = comment.getUser().getAvatarUrl();
    +                        if (!InputHelper.isEmpty(thread.getSubject().getLatestCommentUrl())) {
    +                            customNotificationModel.comment = comment;
    +                            customNotificationModel.url = url;
    +                        }
    +                    }
    +                    customNotificationModel.notification = thread;
    +                    return customNotificationModel;
                     })
                     .subscribeOn(Schedulers.io())
    -                .subscribe(thread -> {/*do nothing in here*/}, throwable -> jobFinished(job, true), () -> {
    +                .subscribe(custom -> {
    +                    if (custom.comment != null) {
    +                        getNotificationWithComment(context, accentColor, custom.notification, custom.comment, custom.url);
    +                    } else {
    +                        showNotificationWithoutComment(context, accentColor, custom.notification, custom.url);
    +                    }
    +
    +                }, throwable -> finishJob(job), () -> {
                         android.app.Notification grouped = getSummaryGroupNotification(first, accentColor);
                         showNotification(first.getId(), grouped);
                         finishJob(job);
    @@ -154,134 +175,84 @@ public class NotificationSchedulerJobTask extends JobService {
         }
     
         private void showNotificationWithoutComment(Context context, int accentColor, Notification thread, String iconUrl) {
    -        ImageLoader.getInstance().loadImage(iconUrl, new ImageLoadingListener() {
    -            @Override
    -            public void onLoadingStarted(String s, View view) {
    +        if (!InputHelper.isEmpty(iconUrl)) {
    +            withoutComments(null, thread, context, accentColor);
    +        } else {
    +            ImageLoader.getInstance().loadImage(iconUrl, new ImageSize(50, 50), new ImageLoadingListener() {
    +                @Override public void onLoadingStarted(String s, View view) {}
     
    -            }
    +                @Override public void onLoadingFailed(String s, View view, FailReason failReason) {
    +                    withoutComments(null, thread, context, accentColor);
    +                }
     
    -            @Override
    -            public void onLoadingFailed(String s, View view, FailReason failReason) {
    -                android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
    -                        .setShowWhen(true)
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -            }
    +                @Override public void onLoadingComplete(String s, View view, Bitmap bitmap) {
    +                    withoutComments(bitmap, thread, context, accentColor);
    +                }
     
    -            @Override
    -            public void onLoadingComplete(String s, View view, Bitmap bitmap) {
    -                android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
    -                        .setLargeIcon(bitmap)
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
    -                        .setShowWhen(true)
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -            }
    +                @Override public void onLoadingCancelled(String s, View view) {
    +                    withoutComments(null, thread, context, accentColor);
     
    -            @Override
    -            public void onLoadingCancelled(String s, View view) {
    -
    -                android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
    -                                .getSubject().getUrl()))
    -                        .setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
    -                        .setShowWhen(true)
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -
    -            }
    -        });
    +                }
    +            });
    +        }
         }
     
    -    private void getNotificationWithComment(Context context, int accentColor,
    -                                            Notification thread, Comment comment, String url) {
    -        ImageLoader.getInstance().loadImage(url, new ImageLoadingListener() {
    -            @Override
    -            public void onLoadingStarted(String s, View view) {
    +    private void withoutComments(Bitmap bitmap, Notification thread, Context context, int accentColor) {
    +        android.app.Notification toAdd = getNotification(thread.getSubject().getTitle(), thread.getRepository().getFullName())
    +                .setLargeIcon(bitmap == null ? BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher) : bitmap)
    +                .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    +                .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(), thread
    +                        .getSubject().getUrl()))
    +                .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(), thread
    +                        .getSubject().getUrl()))
    +                .setWhen(thread.getUpdatedAt() != null ? thread.getUpdatedAt().getTime() : System.currentTimeMillis())
    +                .setShowWhen(true)
    +                .setColor(accentColor)
    +                .setGroup(NOTIFICATION_GROUP_ID)
    +                .build();
    +        showNotification(thread.getId(), toAdd);
    +    }
     
    -            }
    +    private void getNotificationWithComment(Context context, int accentColor, Notification thread, Comment comment, String url) {
    +        if (!InputHelper.isEmpty(url)) {
    +            ImageLoader.getInstance().loadImage(url, new ImageSize(50, 50), new ImageLoadingListener() {
    +                @Override public void onLoadingStarted(String s, View view) {}
     
    -            @Override
    -            public void onLoadingFailed(String s, View view, FailReason failReason) {
    -                android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
    -                        .setSmallIcon(R.drawable.ic_notification)
    -                        .setStyle(new NotificationCompat.BigTextStyle()
    -                                .setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
    -                                .bigText(comment.getBody()))
    -                        .setWhen(comment.getCreatedAt().getTime())
    -                        .setShowWhen(true)
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -            }
    +                @Override public void onLoadingFailed(String s, View view, FailReason failReason) {
    +                    withComments(null, comment, context, thread, accentColor);
    +                }
     
    -            @Override
    -            public void onLoadingComplete(String s, View view, Bitmap bitmap) {
    -                android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
    -                        .setLargeIcon(bitmap)
    -                        .setSmallIcon(R.drawable.ic_notification)
    -                        .setStyle(new NotificationCompat.BigTextStyle()
    -                                .setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
    -                                .bigText(comment.getBody()))
    -                        .setWhen(comment.getCreatedAt().getTime())
    -                        .setShowWhen(true)
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -            }
    +                @Override public void onLoadingComplete(String s, View view, Bitmap bitmap) {
    +                    withComments(bitmap, comment, context, thread, accentColor);
    +                }
     
    -            @Override
    -            public void onLoadingCancelled(String s, View view) {
    -                android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
    -                        .setSmallIcon(R.drawable.ic_notification)
    -                        .setStyle(new NotificationCompat.BigTextStyle()
    -                                .setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
    -                                .bigText(comment.getBody()))
    -                        .setWhen(comment.getCreatedAt().getTime())
    -                        .setShowWhen(true)
    -                        .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
    -                                thread.getSubject().getUrl()))
    -                        .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    -                        .setColor(accentColor)
    -                        .setGroup(NOTIFICATION_GROUP_ID)
    -                        .build();
    -                showNotification(thread.getId(), toAdd);
    -            }
    -        });
    +                @Override public void onLoadingCancelled(String s, View view) {
    +                    withComments(null, comment, context, thread, accentColor);
    +                }
    +            });
    +        } else {
    +            withComments(null, comment, context, thread, accentColor);
    +        }
    +    }
    +
    +    private void withComments(Bitmap bitmap, Comment comment, Context context, Notification thread, int accentColor) {
    +        android.app.Notification toAdd = getNotification(comment.getUser() != null ? comment.getUser().getLogin() : "", comment.getBody())
    +                .setLargeIcon(bitmap == null ? BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher) : bitmap)
    +                .setSmallIcon(R.drawable.ic_notification)
    +                .setStyle(new NotificationCompat.BigTextStyle()
    +                        .setBigContentTitle(comment.getUser() != null ? comment.getUser().getLogin() : "")
    +                        .bigText(comment.getBody()))
    +                .setWhen(comment.getCreatedAt().getTime())
    +                .setShowWhen(true)
    +                .addAction(R.drawable.ic_github, context.getString(R.string.open), getPendingIntent(thread.getId(),
    +                        thread.getSubject().getUrl()))
    +                .addAction(R.drawable.ic_eye_off, context.getString(R.string.mark_as_read), getReadOnlyPendingIntent(thread.getId(),
    +                        thread.getSubject().getUrl()))
    +                .setContentIntent(getPendingIntent(thread.getId(), thread.getSubject().getUrl()))
    +                .setColor(accentColor)
    +                .setGroup(NOTIFICATION_GROUP_ID)
    +                .build();
    +        showNotification(thread.getId(), toAdd);
         }
     
         private android.app.Notification getSummaryGroupNotification(@NonNull Notification thread, int accentColor) {
    @@ -324,4 +295,10 @@ public class NotificationSchedulerJobTask extends JobService {
             return PendingIntent.getService(getApplicationContext(), InputHelper.getSafeIntId(id), intent,
                     PendingIntent.FLAG_UPDATE_CURRENT);
         }
    +
    +    private static class CustomNotificationModel {
    +        public String url;
    +        public Notification notification;
    +        public Comment comment;
    +    }
     }
    \ No newline at end of file
    
    From b0cdb7eeaf60f551bbab313c138bf0ea69d3af1d Mon Sep 17 00:00:00 2001
    From: Kosh <kosh20111@gmail.com>
    Date: Tue, 6 Jun 2017 23:29:40 +0800
    Subject: [PATCH 13/14] this commit fixes #558 and adds open in browser.
    
    ---
     .../data/dao/model/AbstractGist.java          |  2 +-
     .../ui/modules/gists/gist/GistActivity.java   | 26 ++++++++++---------
     .../com/prettifier/pretty/NestedWebView.java  |  3 ++-
     .../prettifier/pretty/PrettifyWebView.java    | 11 +++++++-
     .../MarkDownInterceptorInterface.java         |  2 ++
     .../layout/gists_pager_layout.xml             | 11 ++++++++
     6 files changed, 40 insertions(+), 15 deletions(-)
    
    diff --git a/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java b/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    index 8173e716..ffc64f35 100644
    --- a/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    +++ b/app/src/main/java/com/fastaccess/data/dao/model/AbstractGist.java
    @@ -172,7 +172,7 @@ import lombok.NoArgsConstructor;
                     if (!files.isEmpty()) {
                         FilesListModel filesListModel = files.get(0);
                         if (!InputHelper.isEmpty(filesListModel.getFilename()) && filesListModel.getFilename().trim().length() > 2) {
    -                        spannableBuilder.append(" ").append("/").append(" ")
    +                        spannableBuilder.append(" ")
                                     .append(filesListModel.getFilename());
                         }
                     }
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    index 83aac22a..cc8efeb0 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/gists/gist/GistActivity.java
    @@ -78,22 +78,24 @@ public class GistActivity extends BaseActivity<GistMvp.View, GistPresenter>
                         .show(getSupportFragmentManager(), MessageDialogView.TAG);
         }
     
    -    @OnClick({R.id.startGist, R.id.forkGist}) public void onGistActions(View view) {
    -        view.setEnabled(false);
    +    @OnClick({R.id.startGist, R.id.forkGist, R.id.browser}) public void onGistActions(View view) {
    +        if (getPresenter().getGist() == null) return;
    +        if (view.getId() != R.id.browser) {
    +            view.setEnabled(false);
    +        }
             switch (view.getId()) {
                 case R.id.startGist:
    -                if (getPresenter().getGist() != null) {
    -                    GithubActionService.startForGist(this, getPresenter().getGist().getGistId(),
    -                            getPresenter().isStarred() ? GithubActionService.UNSTAR_GIST : GithubActionService.STAR_GIST);
    -                    getPresenter().onStarGist();
    -                }
    +                GithubActionService.startForGist(this, getPresenter().getGist().getGistId(),
    +                        getPresenter().isStarred() ? GithubActionService.UNSTAR_GIST : GithubActionService.STAR_GIST);
    +                getPresenter().onStarGist();
                     break;
                 case R.id.forkGist:
    -                if (getPresenter().getGist() != null) {
    -                    GithubActionService.startForGist(this, getPresenter().getGist().getGistId(),
    -                            GithubActionService.FORK_GIST);
    -                    getPresenter().onForkGist();
    -                }
    +                GithubActionService.startForGist(this, getPresenter().getGist().getGistId(),
    +                        GithubActionService.FORK_GIST);
    +                getPresenter().onForkGist();
    +                break;
    +            case R.id.browser:
    +                ActivityHelper.startCustomTab(this, getPresenter().getGist().getHtmlUrl());
                     break;
             }
         }
    diff --git a/app/src/main/java/com/prettifier/pretty/NestedWebView.java b/app/src/main/java/com/prettifier/pretty/NestedWebView.java
    index c6c3d800..500f3023 100644
    --- a/app/src/main/java/com/prettifier/pretty/NestedWebView.java
    +++ b/app/src/main/java/com/prettifier/pretty/NestedWebView.java
    @@ -42,7 +42,6 @@ public class NestedWebView extends WebView implements NestedScrollingChild {
     
         @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent ev) {
             boolean returnValue;
    -
             MotionEvent event = MotionEvent.obtain(ev);
             final int action = MotionEventCompat.getActionMasked(event);
             if (action == MotionEvent.ACTION_DOWN) {
    @@ -125,5 +124,7 @@ public class NestedWebView extends WebView implements NestedScrollingChild {
             return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
         }
     
    +
    +
     }
     
    diff --git a/app/src/main/java/com/prettifier/pretty/PrettifyWebView.java b/app/src/main/java/com/prettifier/pretty/PrettifyWebView.java
    index 99c1985f..bb4dd7df 100755
    --- a/app/src/main/java/com/prettifier/pretty/PrettifyWebView.java
    +++ b/app/src/main/java/com/prettifier/pretty/PrettifyWebView.java
    @@ -32,6 +32,7 @@ import com.prettifier.pretty.helper.PrettifyHelper;
     public class PrettifyWebView extends NestedWebView {
         private OnContentChangedListener onContentChangedListener;
         private boolean interceptTouch;
    +    private boolean enableNestedScrolling;
     
         public interface OnContentChangedListener {
             void onContentChanged(int progress);
    @@ -53,7 +54,7 @@ public class PrettifyWebView extends NestedWebView {
             initView(attrs);
         }
     
    -    @Override public boolean onInterceptTouchEvent(MotionEvent p_event) {
    +    @Override public boolean onInterceptTouchEvent(MotionEvent p) {
             return true;
         }
     
    @@ -174,6 +175,14 @@ public class PrettifyWebView extends NestedWebView {
             this.interceptTouch = interceptTouch;
         }
     
    +    public void setEnableNestedScrolling(boolean enableNestedScrolling) {
    +        if (this.enableNestedScrolling != enableNestedScrolling) {
    +            Logger.e(enableNestedScrolling);
    +            setNestedScrollingEnabled(enableNestedScrolling);
    +            this.enableNestedScrolling = enableNestedScrolling;
    +        }
    +    }
    +
         private void startActivity(@Nullable Uri url) {
             if (url == null) return;
             Logger.e(url);
    diff --git a/app/src/main/java/com/prettifier/pretty/callback/MarkDownInterceptorInterface.java b/app/src/main/java/com/prettifier/pretty/callback/MarkDownInterceptorInterface.java
    index 21d403c0..a328c003 100644
    --- a/app/src/main/java/com/prettifier/pretty/callback/MarkDownInterceptorInterface.java
    +++ b/app/src/main/java/com/prettifier/pretty/callback/MarkDownInterceptorInterface.java
    @@ -18,12 +18,14 @@ public class MarkDownInterceptorInterface {
         @JavascriptInterface public void startIntercept() {
             if (prettifyWebView != null) {
                 prettifyWebView.setInterceptTouch(true);
    +            prettifyWebView.setEnableNestedScrolling(false);
             }
         }
     
         @JavascriptInterface public void stopIntercept() {
             if (prettifyWebView != null) {
                 prettifyWebView.setInterceptTouch(false);
    +            prettifyWebView.setEnableNestedScrolling(true);
             }
         }
     }
    diff --git a/app/src/main/res/layouts/main_layouts/layout/gists_pager_layout.xml b/app/src/main/res/layouts/main_layouts/layout/gists_pager_layout.xml
    index 957a9ddf..4d69d563 100644
    --- a/app/src/main/res/layouts/main_layouts/layout/gists_pager_layout.xml
    +++ b/app/src/main/res/layouts/main_layouts/layout/gists_pager_layout.xml
    @@ -34,6 +34,17 @@
                         android:layout_height="?actionBarSize"
                         android:background="?colorPrimary">
     
    +                    <com.fastaccess.ui.widgets.ForegroundImageView
    +                        android:id="@+id/browser"
    +                        android:layout_width="wrap_content"
    +                        android:layout_height="wrap_content"
    +                        android:layout_gravity="center|end"
    +                        android:background="?selectableItemBackgroundBorderless"
    +                        android:contentDescription="@string/open_in_browser"
    +                        android:padding="@dimen/spacing_normal"
    +                        android:scaleType="centerCrop"
    +                        android:src="@drawable/ic_brower"/>
    +
                         <com.fastaccess.ui.widgets.ForegroundImageView
                             android:id="@+id/startGist"
                             android:layout_width="wrap_content"
    
    From 7153974ea5a0734e476603af5edfb922808bd92a Mon Sep 17 00:00:00 2001
    From: Kosh <kosh20111@gmail.com>
    Date: Wed, 7 Jun 2017 00:38:59 +0800
    Subject: [PATCH 14/14] releasing 3.0.1
    
    ---
     app/build.gradle                              |  4 ++--
     app/proguard-rules.pro                        | 22 ++++++++++++++-----
     .../adapter/viewholder/TrendingViewHolder.kt  |  6 +++--
     .../overview/ProfileOverviewFragment.java     | 12 ++++++++--
     app/src/main/res/raw/changelog.html           | 21 ++++++++++++++----
     5 files changed, 49 insertions(+), 16 deletions(-)
    
    diff --git a/app/build.gradle b/app/build.gradle
    index 1e2d0cea..d6283337 100644
    --- a/app/build.gradle
    +++ b/app/build.gradle
    @@ -28,8 +28,8 @@ android {
             applicationId "com.fastaccess.github"
             minSdkVersion 21
             targetSdkVersion 26
    -        versionCode 300
    -        versionName "3.0.0"
    +        versionCode 301
    +        versionName "3.0.1"
             signingConfig signingConfigs.signing
             buildConfigString "GITHUB_CLIENT_ID", (buildProperties.secrets['github_client_id'] | buildProperties.notThere['github_client_id']).string
             buildConfigString "GITHUB_SECRET", (buildProperties.secrets['github_secret'] | buildProperties.notThere['github_secret']).string
    diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
    index ece94ad6..943bb4f1 100644
    --- a/app/proguard-rules.pro
    +++ b/app/proguard-rules.pro
    @@ -32,8 +32,7 @@
     }
     -keep class com.google.gson.** { *; }
     -keep class com.google.inject.** { *; }
    --keep class org.apache.http.** { *; }
    --keep class org.apache.james.mime4j.** { *; }
    +-keep class org.apache.** { *; }
     -keep class javax.inject.** { *; }
     -keep class retrofit.** { *; }
     -keep class com.squareup.okhttp.** { *; }
    @@ -45,7 +44,7 @@
     -dontwarn rx.**
     -dontwarn com.squareup.okhttp.**
     -dontwarn okio.**
    --dontwarn org.apache.http.**
    +-dontwarn org.apache.**
     -dontwarn android.net.http.AndroidHttpClient
     -dontwarn retrofit.**
     -dontwarn retrofit2.adapter.rxjava.CompletableHelper$**
    @@ -81,6 +80,7 @@
     -dontwarn java.util.**
     -dontwarn java.time.**
     -dontwarn javax.annotation.**
    +-dontwarn org.jetbrains.annotations.**
     -dontwarn javax.cache.**
     -dontwarn javax.naming.**
     -dontwarn javax.transaction.**
    @@ -109,6 +109,16 @@
     -dontwarn com.google.android.gms.**
     -dontwarn android.animation.**
     -dontwarn java.io.**
    --dontwarn  android.content.**
    --keeppackagenames org.jsoup.nodes
    --keep class com.github.florent37.** { *; }
    +-dontwarn android.content.**
    +-dontwarn org.jdom.**
    +-dontwarn kotlin.reflect.jvm.internal.impl.descriptors.CallableDescriptor
    +-dontwarn kotlin.reflect.jvm.internal.impl.descriptors.ClassDescriptor
    +-dontwarn kotlin.reflect.jvm.internal.impl.descriptors.ClassifierDescriptorWithTypeParameters
    +-dontwarn kotlin.reflect.jvm.internal.impl.descriptors.annotations.AnnotationDescriptor
    +-dontwarn kotlin.reflect.jvm.internal.impl.descriptors.impl.PropertyDescriptorImpl
    +-dontwarn kotlin.reflect.jvm.internal.impl.load.java.JavaClassFinder
    +-dontwarn kotlin.reflect.jvm.internal.impl.resolve.OverridingUtil
    +-dontwarn kotlin.reflect.jvm.internal.impl.types.DescriptorSubstitutor
    +-dontwarn kotlin.reflect.jvm.internal.impl.types.DescriptorSubstitutor
    +-dontwarn kotlin.reflect.jvm.internal.impl.types.TypeConstructor
    +-keeppackagenames org.jsoup.nodes
    \ No newline at end of file
    diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    index 5544ae33..5d9e5bc0 100644
    --- a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/TrendingViewHolder.kt
    @@ -36,8 +36,10 @@ open class TrendingViewHolder(itemView: View, adapter: BaseRecyclerAdapter<Trend
             stars.text = t.stars
             fork.text = t.forks
             lang.text = t.language
    -        lang.tintDrawables(t.language?.let { ColorsProvider.getColorAsColor(it, itemView.context) } as Int)
    -        lang.setTextColor(t.language?.let { ColorsProvider.getColorAsColor(it, itemView.context) } as Int)
    +        if (!t.language.isNullOrEmpty()) {
    +            lang.tintDrawables(ColorsProvider.getColorAsColor(t.language!!, itemView.context))
    +            lang.setTextColor(ColorsProvider.getColorAsColor(t.language, itemView.context))
    +        }
             lang.visibility = if (t.language.isNullOrEmpty()) View.GONE else View.VISIBLE
         }
     
    diff --git a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    index d775260f..1da21478 100644
    --- a/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    +++ b/app/src/main/java/com/fastaccess/ui/modules/profile/overview/ProfileOverviewFragment.java
    @@ -1,5 +1,6 @@
     package com.fastaccess.ui.modules.profile.overview;
     
    +import android.annotation.SuppressLint;
     import android.content.Context;
     import android.graphics.Bitmap;
     import android.graphics.drawable.BitmapDrawable;
    @@ -11,6 +12,7 @@ import android.support.annotation.StringRes;
     import android.support.v4.widget.NestedScrollView;
     import android.support.v7.widget.CardView;
     import android.util.DisplayMetrics;
    +import android.view.MotionEvent;
     import android.view.View;
     import android.view.ViewGroup;
     import android.widget.Button;
    @@ -145,7 +147,7 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
             return new ProfileOverviewPresenter();
         }
     
    -    @Override public void onInitViews(@Nullable User userModel) {
    +    @SuppressLint("ClickableViewAccessibility") @Override public void onInitViews(@Nullable User userModel) {
             progress.setVisibility(GONE);
             if (userModel == null) return;
             this.userModel = userModel;
    @@ -158,7 +160,13 @@ public class ProfileOverviewFragment extends BaseFragment<ProfileOverviewMvp.Vie
                 description.setVisibility(GONE);
             }
             avatarLayout.setUrl(userModel.getAvatarUrl(), null);
    -        avatarLayout.setOnClickListener(v -> userInformation.performClick());
    +        avatarLayout.findViewById(R.id.avatar).setOnTouchListener((v, event) -> {
    +            if (event.getAction() == MotionEvent.ACTION_UP) {
    +                ActivityHelper.startCustomTab(getActivity(), userModel.getAvatarUrl());
    +                return true;
    +            }
    +            return false;
    +        });
             organization.setText(InputHelper.toNA(userModel.getCompany()));
             location.setText(InputHelper.toNA(userModel.getLocation()));
             email.setText(InputHelper.toNA(userModel.getEmail()));
    diff --git a/app/src/main/res/raw/changelog.html b/app/src/main/res/raw/changelog.html
    index 86c5d9d0..ebdcc6d0 100644
    --- a/app/src/main/res/raw/changelog.html
    +++ b/app/src/main/res/raw/changelog.html
    @@ -8,18 +8,31 @@
         <body id="preview">
             <h2><a id="FastHub_changelog_0"></a>FastHub changelog
             </h2>
    -        <h3><a id="Version__300_Trending_Finally_2"></a>Version 3.0.0 (Trending Finally!)
    +        <h3><a id="Version__301_Trending_Improvement_2"></a>Version 3.0.1 (Trending Improvement!)
             </h3>
             <blockquote>
                 <p>
    -                <strong>P.S: I’m not asking for to much, if you are facing a problem in FastHub please report it at the issue tracker in GitHub either
    -                    via FastHub app or from GitHub website, your reviews are what keeps me motivated to further improve FastHub.
    +                <strong>P.S: I’m not asking for too much, if you are facing a problem in FastHub please report it at the issue tracker in GitHub
    +                    either via FastHub or from GitHub website, your reviews are what keeps me motivated to further improve FastHub.
                         <br>
                         Keep in mind that FastHub is free &amp; open source.
                     </strong>
                 </p>
             </blockquote>
    -        <h4><a id="Bugs__Enhancements__new_Features_7"></a>Bugs , Enhancements &amp; new Features
    +        <h4><a id="Bugs__Enhancements__new_Features_301_7"></a>Bugs , Enhancements &amp; new Features (3.0.1)
    +        </h4>
    +        <ul>
    +            <li>(New) Showing language to Trending list items.</li>
    +            <li>(Fix) Description in Trending might show in wrong place.</li>
    +            <li>(Fix) Large files in dark theme text color.</li>
    +            <li>(Fix) Notifications after migrating to RxJava2 only one were showing.</li>
    +            <li>(Fix) Scrolling code in Readme causes header to move as well.</li>
    +            <li>(Fix) Personal Gists were opening some random gist.</li>
    +            <li>(Enhancement) Clicking on user avatar from profile will open their image in full screen.</li>
    +            <li>(Enhancement) No new line shows in wrong position.</li>
    +        </ul>
    +        <hr>
    +        <h4><a id="Bugs__Enhancements__new_Features_300_16"></a>Bugs , Enhancements &amp; new Features (3.0.0)
             </h4>
             <ul>
                 <li>(New) Explore trending with all the languages out there.</li>