├── .gitignore
├── Image_Placeholder.png
├── VeXplore
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── 1.png
│ │ ├── 1 3.png
│ │ ├── 1 4.png
│ │ ├── 1-2.png
│ │ ├── 1 copy.png
│ │ ├── 1 copy-2.png
│ │ ├── VeXplore.png
│ │ ├── VeXplore-1.png
│ │ ├── VeXplore-2.png
│ │ ├── VeXplore-3.png
│ │ ├── VeXplore-4.png
│ │ ├── VeXplore-5.png
│ │ ├── VeXplore-6.png
│ │ ├── VeXplore-7.png
│ │ └── Contents.json
│ ├── Close.imageset
│ │ ├── 1461865222_cross.png
│ │ ├── 1461865222_cross 2.png
│ │ └── Contents.json
│ ├── Safari.imageset
│ │ ├── safari-logo (1).png
│ │ ├── safari-logo (1) 2.png
│ │ └── Contents.json
│ ├── Send.imageset
│ │ ├── 1463765393_send 2.png
│ │ ├── 1463765393_send.png
│ │ └── Contents.json
│ ├── Setting.imageset
│ │ ├── 1462719337_cog.png
│ │ ├── 1462719337_cog 2.png
│ │ └── Contents.json
│ ├── Time.imageset
│ │ ├── 1464770510_time 2.png
│ │ ├── 1464770510_time.png
│ │ └── Contents.json
│ ├── Topics.imageset
│ │ ├── 1464770499_note.png
│ │ ├── 1464770499_note 2.png
│ │ └── Contents.json
│ ├── Write.imageset
│ │ ├── 1462719383_pen 2.png
│ │ ├── 1462719383_pen.png
│ │ └── Contents.json
│ ├── Github.imageset
│ │ ├── 1461856003_github.png
│ │ ├── 1461855896_github copy.png
│ │ └── Contents.json
│ ├── Logout.imageset
│ │ ├── MainMenu_Logout-1.png
│ │ ├── MainMenu_Logout-2.png
│ │ └── Contents.json
│ ├── Twitch.imageset
│ │ ├── 1461859235_twitch.png
│ │ ├── 1461859235_twitch copy.png
│ │ └── Contents.json
│ ├── Login_Background.imageset
│ │ ├── background.png
│ │ └── Contents.json
│ ├── Psn.imageset
│ │ ├── 1461864196_playstation.png
│ │ ├── 1461864196_playstation copy.png
│ │ └── Contents.json
│ ├── Replies.imageset
│ │ ├── 1462719153_back 2.png
│ │ ├── 1462719153_back 3.png
│ │ └── Contents.json
│ ├── Thank.imageset
│ │ ├── 1462719369_heart copy.png
│ │ ├── 1462719369_heart copy 2.png
│ │ └── Contents.json
│ ├── More.imageset
│ │ ├── 1464181620_MoreVertical.png
│ │ ├── 1464181620_MoreVertical copy.png
│ │ └── Contents.json
│ ├── Reply.imageset
│ │ ├── 1462719371_return copy.png
│ │ ├── 1462719371_return copy 2.png
│ │ └── Contents.json
│ ├── Search_Nav.imageset
│ │ ├── 1462719360_search.png
│ │ ├── 1462719360_search copy.png
│ │ └── Contents.json
│ ├── Twitter.imageset
│ │ ├── 1461864512_Twitter-1.png
│ │ ├── 1461864512_Twitter-1 copy.png
│ │ └── Contents.json
│ ├── Image.imageset
│ │ ├── 1463995674_18.Pictures-Day.png
│ │ ├── 1463995674_18.Pictures-Day 2.png
│ │ └── Contents.json
│ ├── Onepassword.imageset
│ │ ├── onepassword-navbar.png
│ │ ├── onepassword-navbar@2x.png
│ │ ├── onepassword-navbar@3x.png
│ │ └── Contents.json
│ ├── Sort.imageset
│ │ ├── 1462719426_preferences copy.png
│ │ ├── 1462719426_preferences copy 2-1.png
│ │ └── Contents.json
│ ├── Arrow_Right.imageset
│ │ ├── 1464770037_Arrow-Right.png
│ │ ├── 1464770037_Arrow-Right (1).png
│ │ └── Contents.json
│ ├── Lock.imageset
│ │ ├── 1461862337_icon-114-lock copy.png
│ │ ├── 1461862337_icon-114-lock copy 2.png
│ │ └── Contents.json
│ ├── Avatar_Placeholder.imageset
│ │ ├── 1461865203_photo.png
│ │ ├── 1461865203_photo copy.png
│ │ └── Contents.json
│ ├── Delete.imageset
│ │ ├── 1463681808_icon-27-trash-can 2.png
│ │ ├── 1463681808_icon-27-trash-can.png
│ │ └── Contents.json
│ ├── Report_Activity.imageset
│ │ ├── 1465632015_like copy.png
│ │ ├── 1465632015_like copy 2.png
│ │ └── Contents.json
│ ├── Hide.imageset
│ │ ├── 1462747113_icon-21-eye-hidden copy.png
│ │ ├── 1462747113_icon-21-eye-hidden copy 2.png
│ │ └── Contents.json
│ ├── Home.imageset
│ │ ├── 1462732243_home_house_real_estate.png
│ │ ├── 1462732243_home_house_real_estate 2.png
│ │ └── Contents.json
│ ├── Invisible.imageset
│ │ ├── 1461862272_icon-21-eye-hidden copy.png
│ │ ├── 1461862272_icon-21-eye-hidden copy 2.png
│ │ └── Contents.json
│ ├── Location.imageset
│ │ ├── 1461861830_gps_location_map_marker.png
│ │ ├── 1461861830_gps_location_map_marker copy.png
│ │ └── Contents.json
│ ├── Like.imageset
│ │ ├── 1461861842_heart_like_love_vote.png copy 2.png
│ │ ├── 1461861842_heart_like_love_vote.png copy.png
│ │ └── Contents.json
│ ├── Nodes.imageset
│ │ ├── 1462736683_compass_direction_navigation 2.png
│ │ ├── 1462736683_compass_direction_navigation.png
│ │ └── Contents.json
│ ├── Owner_View.imageset
│ │ ├── 1464780510_eye_preview_see_seen_view.png
│ │ ├── 1464780512_eye_preview_see_seen_view.png
│ │ └── Contents.json
│ ├── Cross.imageset
│ │ ├── 1461861579_close_delete_remove_icon copy 3.png
│ │ ├── 1461861579_close_delete_remove_icon copy 2-1.png
│ │ └── Contents.json
│ ├── Dribbble.imageset
│ │ ├── 1461860642_dribbble_online_social_media.png
│ │ ├── 1461860642_dribbble_online_social_media copy.png
│ │ └── Contents.json
│ ├── Favorite.imageset
│ │ ├── 1461861842_heart_like_love_vote.png copy.png
│ │ ├── 1461861842_heart_like_love_vote.png copy 2.png
│ │ └── Contents.json
│ ├── Tabar_Homepage.imageset
│ │ ├── 1461861739_home_house_real_estate.png
│ │ ├── 1461861739_home_house_real_estate copy.png
│ │ └── Contents.json
│ ├── Reply_Activity.imageset
│ │ ├── 1465279341_SubdirectoryArrowUpLeft-1.png
│ │ ├── 1465279341_SubdirectoryArrowUpLeft copy.png
│ │ └── Contents.json
│ ├── Wang_Xizhi.imageset
│ │ ├── 0eb30f2442a7d9330262cc43af4bd11373f00133.jpg
│ │ ├── 0eb30f2442a7d9330262cc43af4bd11373f00133 2.jpg
│ │ └── Contents.json
│ ├── Favorite_Activity.imageset
│ │ ├── 1461861842_heart_like_love_vote.png.png
│ │ ├── 1461861842_heart_like_love_vote.png 2.png
│ │ └── Contents.json
│ ├── Tick.imageset
│ │ ├── 1462731373_accept_check_ok_outline_tick_yes copy.png
│ │ ├── 1462731373_accept_check_ok_outline_tick_yes copy 2.png
│ │ └── Contents.json
│ ├── Node_Placeholder.imageset
│ │ ├── 1463740612_smiley-face-emoticon-avatar-brand.png
│ │ ├── 1463740612_smiley-face-emoticon-avatar-brand copy.png
│ │ └── Contents.json
│ ├── Comment.imageset
│ │ ├── 1461861747_bubble_chat_comment_message_outline_talk copy.png
│ │ ├── 1461861747_bubble_chat_comment_message_outline_talk copy 2.png
│ │ └── Contents.json
│ ├── Recent.imageset
│ │ ├── 1462732316_alarm_alert_clock_event_history_schedule_time_watch.png
│ │ ├── 1462732316_alarm_alert_clock_event_history_schedule_time_watch copy.png
│ │ └── Contents.json
│ ├── Search.imageset
│ │ ├── 1462732255_find_in_magnifier_magnifying_research_search_view_zoom.png
│ │ ├── 1462732255_find_in_magnifier_magnifying_research_search_view_zoom copy.png
│ │ └── Contents.json
│ ├── Homepage.imageset
│ │ ├── 1461858344_house-home-export-real_estate-property-outline-stroke.png
│ │ ├── 1461858344_house-home-export-real_estate-property-outline-stroke copy.png
│ │ └── Contents.json
│ ├── Profile.imageset
│ │ ├── 1461861750_account_friend_human_man_member_person_profile_user_users.png
│ │ ├── 1461861750_account_friend_human_man_member_person_profile_user_users 2.png
│ │ └── Contents.json
│ ├── Tabar_Search.imageset
│ │ ├── 1464775278_find_in_magnifier_magnifying_research_search_view_zoom.png
│ │ ├── 1464775281_find_in_magnifier_magnifying_research_search_view_zoom.png
│ │ └── Contents.json
│ ├── Notification.imageset
│ │ ├── 1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule 2.png
│ │ ├── 1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule.png
│ │ └── Contents.json
│ └── Ignore.imageset
│ │ ├── 1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash.png
│ │ ├── 1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash copy.png
│ │ └── Contents.json
├── VeXplore-Bridging-Header.h
├── ImageClickScript.js
├── libxml_header.h
├── Define.swift
├── font.css
├── ImageClick.js
├── ModelResponse.swift
├── ImageReplace.js
├── RichTextRunDelegate.swift
├── NotificationModel.swift
├── Protocols.swift
├── WebImageUtils.swift
├── LoginPageTextField.swift
├── NodeSelectViewController.swift
├── WebImage.swift
├── CommentImageView.swift
├── SectionHeaderView.swift
├── AboutMeCell.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── RichTextUtils.swift
├── AvatarImageView.swift
├── baseStyle.css
├── Info.plist
├── ProfileSectionHeaderCell.swift
├── NodeModel.swift
├── User.swift
├── PlaceholderTextView.swift
├── ProfileActionView.swift
├── SearchBoxView.swift
├── TopicListViewController.swift
├── Utils.swift
├── BaseCenterLoadingViewController.swift
├── MyFollowingCell.swift
├── CSSStyle.swift
├── TopicDetailModel.swift
├── MemberFollowBlockCell.swift
├── TopicSearchResultCell.swift
├── URLAnalyzer.swift
├── PersonalInfoCell.swift
├── TextLine.swift
├── MyFollowingsViewController.swift
├── FavoriteNodesViewController.swift
├── TopicReplyingViewController.swift
├── RecentListViewController.swift
├── libxml
│ └── HTMLtree.h
├── SearchViewController.swift
├── MyFavoriteCell.swift
└── MainViewController.swift
├── VeXplore.xcodeproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SharedKit
├── SharedKit.h
├── Info.plist
├── SharedUtils.swift
├── Response.swift
├── URLAnalysisResult.swift
├── Session.swift
├── JSON.swift
├── NetworkingUtils.swift
└── SharedR.swift
├── Scripts
└── gitVersion.sh
├── TodayExtension
├── Info.plist
└── TodayViewController.swift
├── ActionExtension
└── Info.plist
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | xcuserdata
2 | xcshareddata
3 | .DS_Store
4 | Version.xcconfig
5 |
--------------------------------------------------------------------------------
/Image_Placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/Image_Placeholder.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1 3.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1 4.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1-2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1 copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1 copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/1 copy-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/1 copy-2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-3.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-4.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-5.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-6.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/AppIcon.appiconset/VeXplore-7.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Close.imageset/1461865222_cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Close.imageset/1461865222_cross.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Safari.imageset/safari-logo (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Safari.imageset/safari-logo (1).png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Send.imageset/1463765393_send 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Send.imageset/1463765393_send 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Send.imageset/1463765393_send.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Send.imageset/1463765393_send.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Setting.imageset/1462719337_cog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Setting.imageset/1462719337_cog.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Time.imageset/1464770510_time 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Time.imageset/1464770510_time 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Time.imageset/1464770510_time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Time.imageset/1464770510_time.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Topics.imageset/1464770499_note.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Topics.imageset/1464770499_note.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Write.imageset/1462719383_pen 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Write.imageset/1462719383_pen 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Write.imageset/1462719383_pen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Write.imageset/1462719383_pen.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Close.imageset/1461865222_cross 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Close.imageset/1461865222_cross 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Github.imageset/1461856003_github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Github.imageset/1461856003_github.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Logout.imageset/MainMenu_Logout-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Logout.imageset/MainMenu_Logout-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Logout.imageset/MainMenu_Logout-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Logout.imageset/MainMenu_Logout-2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Safari.imageset/safari-logo (1) 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Safari.imageset/safari-logo (1) 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Setting.imageset/1462719337_cog 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Setting.imageset/1462719337_cog 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Topics.imageset/1464770499_note 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Topics.imageset/1464770499_note 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitch.imageset/1461859235_twitch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Twitch.imageset/1461859235_twitch.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Login_Background.imageset/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Login_Background.imageset/background.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Psn.imageset/1461864196_playstation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Psn.imageset/1461864196_playstation.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Replies.imageset/1462719153_back 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Replies.imageset/1462719153_back 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Replies.imageset/1462719153_back 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Replies.imageset/1462719153_back 3.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Thank.imageset/1462719369_heart copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Thank.imageset/1462719369_heart copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Github.imageset/1461855896_github copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Github.imageset/1461855896_github copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/More.imageset/1464181620_MoreVertical.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/More.imageset/1464181620_MoreVertical.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply.imageset/1462719371_return copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Reply.imageset/1462719371_return copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search_Nav.imageset/1462719360_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Search_Nav.imageset/1462719360_search.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Thank.imageset/1462719369_heart copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Thank.imageset/1462719369_heart copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitch.imageset/1461859235_twitch copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Twitch.imageset/1461859235_twitch copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitter.imageset/1461864512_Twitter-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Twitter.imageset/1461864512_Twitter-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Image.imageset/1463995674_18.Pictures-Day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Image.imageset/1463995674_18.Pictures-Day.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Psn.imageset/1461864196_playstation copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Psn.imageset/1461864196_playstation copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply.imageset/1462719371_return copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Reply.imageset/1462719371_return copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Sort.imageset/1462719426_preferences copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Sort.imageset/1462719426_preferences copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Arrow_Right.imageset/1464770037_Arrow-Right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Arrow_Right.imageset/1464770037_Arrow-Right.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Image.imageset/1463995674_18.Pictures-Day 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Image.imageset/1463995674_18.Pictures-Day 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Lock.imageset/1461862337_icon-114-lock copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Lock.imageset/1461862337_icon-114-lock copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/More.imageset/1464181620_MoreVertical copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/More.imageset/1464181620_MoreVertical copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar@2x.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Onepassword.imageset/onepassword-navbar@3x.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search_Nav.imageset/1462719360_search copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Search_Nav.imageset/1462719360_search copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitter.imageset/1461864512_Twitter-1 copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Twitter.imageset/1461864512_Twitter-1 copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Avatar_Placeholder.imageset/1461865203_photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Avatar_Placeholder.imageset/1461865203_photo.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Delete.imageset/1463681808_icon-27-trash-can 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Delete.imageset/1463681808_icon-27-trash-can 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Delete.imageset/1463681808_icon-27-trash-can.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Delete.imageset/1463681808_icon-27-trash-can.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Lock.imageset/1461862337_icon-114-lock copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Lock.imageset/1461862337_icon-114-lock copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Report_Activity.imageset/1465632015_like copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Report_Activity.imageset/1465632015_like copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Sort.imageset/1462719426_preferences copy 2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Sort.imageset/1462719426_preferences copy 2-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Arrow_Right.imageset/1464770037_Arrow-Right (1).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Arrow_Right.imageset/1464770037_Arrow-Right (1).png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Hide.imageset/1462747113_icon-21-eye-hidden copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Hide.imageset/1462747113_icon-21-eye-hidden copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Home.imageset/1462732243_home_house_real_estate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Home.imageset/1462732243_home_house_real_estate.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Report_Activity.imageset/1465632015_like copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Report_Activity.imageset/1465632015_like copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Avatar_Placeholder.imageset/1461865203_photo copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Avatar_Placeholder.imageset/1461865203_photo copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Hide.imageset/1462747113_icon-21-eye-hidden copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Hide.imageset/1462747113_icon-21-eye-hidden copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Home.imageset/1462732243_home_house_real_estate 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Home.imageset/1462732243_home_house_real_estate 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Invisible.imageset/1461862272_icon-21-eye-hidden copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Invisible.imageset/1461862272_icon-21-eye-hidden copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Location.imageset/1461861830_gps_location_map_marker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Location.imageset/1461861830_gps_location_map_marker.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Invisible.imageset/1461862272_icon-21-eye-hidden copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Invisible.imageset/1461862272_icon-21-eye-hidden copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Like.imageset/1461861842_heart_like_love_vote.png copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Like.imageset/1461861842_heart_like_love_vote.png copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Like.imageset/1461861842_heart_like_love_vote.png copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Like.imageset/1461861842_heart_like_love_vote.png copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Nodes.imageset/1462736683_compass_direction_navigation 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Nodes.imageset/1462736683_compass_direction_navigation 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Nodes.imageset/1462736683_compass_direction_navigation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Nodes.imageset/1462736683_compass_direction_navigation.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Owner_View.imageset/1464780510_eye_preview_see_seen_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Owner_View.imageset/1464780510_eye_preview_see_seen_view.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Owner_View.imageset/1464780512_eye_preview_see_seen_view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Owner_View.imageset/1464780512_eye_preview_see_seen_view.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Cross.imageset/1461861579_close_delete_remove_icon copy 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Cross.imageset/1461861579_close_delete_remove_icon copy 3.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Dribbble.imageset/1461860642_dribbble_online_social_media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Dribbble.imageset/1461860642_dribbble_online_social_media.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite.imageset/1461861842_heart_like_love_vote.png copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Favorite.imageset/1461861842_heart_like_love_vote.png copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Location.imageset/1461861830_gps_location_map_marker copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Location.imageset/1461861830_gps_location_map_marker copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Homepage.imageset/1461861739_home_house_real_estate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tabar_Homepage.imageset/1461861739_home_house_real_estate.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Cross.imageset/1461861579_close_delete_remove_icon copy 2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Cross.imageset/1461861579_close_delete_remove_icon copy 2-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite.imageset/1461861842_heart_like_love_vote.png copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Favorite.imageset/1461861842_heart_like_love_vote.png copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply_Activity.imageset/1465279341_SubdirectoryArrowUpLeft-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Reply_Activity.imageset/1465279341_SubdirectoryArrowUpLeft-1.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Wang_Xizhi.imageset/0eb30f2442a7d9330262cc43af4bd11373f00133.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Wang_Xizhi.imageset/0eb30f2442a7d9330262cc43af4bd11373f00133.jpg
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Dribbble.imageset/1461860642_dribbble_online_social_media copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Dribbble.imageset/1461860642_dribbble_online_social_media copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite_Activity.imageset/1461861842_heart_like_love_vote.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Favorite_Activity.imageset/1461861842_heart_like_love_vote.png.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply_Activity.imageset/1465279341_SubdirectoryArrowUpLeft copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Reply_Activity.imageset/1465279341_SubdirectoryArrowUpLeft copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Homepage.imageset/1461861739_home_house_real_estate copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tabar_Homepage.imageset/1461861739_home_house_real_estate copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tick.imageset/1462731373_accept_check_ok_outline_tick_yes copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tick.imageset/1462731373_accept_check_ok_outline_tick_yes copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Wang_Xizhi.imageset/0eb30f2442a7d9330262cc43af4bd11373f00133 2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Wang_Xizhi.imageset/0eb30f2442a7d9330262cc43af4bd11373f00133 2.jpg
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite_Activity.imageset/1461861842_heart_like_love_vote.png 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Favorite_Activity.imageset/1461861842_heart_like_love_vote.png 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tick.imageset/1462731373_accept_check_ok_outline_tick_yes copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tick.imageset/1462731373_accept_check_ok_outline_tick_yes copy 2.png
--------------------------------------------------------------------------------
/VeXplore.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Node_Placeholder.imageset/1463740612_smiley-face-emoticon-avatar-brand.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Node_Placeholder.imageset/1463740612_smiley-face-emoticon-avatar-brand.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Comment.imageset/1461861747_bubble_chat_comment_message_outline_talk copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Comment.imageset/1461861747_bubble_chat_comment_message_outline_talk copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Comment.imageset/1461861747_bubble_chat_comment_message_outline_talk copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Comment.imageset/1461861747_bubble_chat_comment_message_outline_talk copy 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Node_Placeholder.imageset/1463740612_smiley-face-emoticon-avatar-brand copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Node_Placeholder.imageset/1463740612_smiley-face-emoticon-avatar-brand copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Recent.imageset/1462732316_alarm_alert_clock_event_history_schedule_time_watch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Recent.imageset/1462732316_alarm_alert_clock_event_history_schedule_time_watch.png
--------------------------------------------------------------------------------
/VeXplore/VeXplore-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "OnePasswordExtension.h"
6 | #import "libxml_header.h"
7 | #import
8 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search.imageset/1462732255_find_in_magnifier_magnifying_research_search_view_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Search.imageset/1462732255_find_in_magnifier_magnifying_research_search_view_zoom.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Homepage.imageset/1461858344_house-home-export-real_estate-property-outline-stroke.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Homepage.imageset/1461858344_house-home-export-real_estate-property-outline-stroke.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Recent.imageset/1462732316_alarm_alert_clock_event_history_schedule_time_watch copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Recent.imageset/1462732316_alarm_alert_clock_event_history_schedule_time_watch copy.png
--------------------------------------------------------------------------------
/VeXplore/ImageClickScript.js:
--------------------------------------------------------------------------------
1 |
2 | document.addEventListener('click', function (e)
3 | {
4 | if (e.target.nodeName.toUpperCase() == 'IMG')
5 | {
6 | var href = location.href;
7 | href += encodeURI(e.target.src);
8 | location.href = href;
9 | }
10 | });
11 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Profile.imageset/1461861750_account_friend_human_man_member_person_profile_user_users.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Profile.imageset/1461861750_account_friend_human_man_member_person_profile_user_users.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search.imageset/1462732255_find_in_magnifier_magnifying_research_search_view_zoom copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Search.imageset/1462732255_find_in_magnifier_magnifying_research_search_view_zoom copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Homepage.imageset/1461858344_house-home-export-real_estate-property-outline-stroke copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Homepage.imageset/1461858344_house-home-export-real_estate-property-outline-stroke copy.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Profile.imageset/1461861750_account_friend_human_man_member_person_profile_user_users 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Profile.imageset/1461861750_account_friend_human_man_member_person_profile_user_users 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Search.imageset/1464775278_find_in_magnifier_magnifying_research_search_view_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tabar_Search.imageset/1464775278_find_in_magnifier_magnifying_research_search_view_zoom.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Search.imageset/1464775281_find_in_magnifier_magnifying_research_search_view_zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Tabar_Search.imageset/1464775281_find_in_magnifier_magnifying_research_search_view_zoom.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Notification.imageset/1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Notification.imageset/1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule 2.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Notification.imageset/1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Notification.imageset/1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Ignore.imageset/1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Ignore.imageset/1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash.png
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Ignore.imageset/1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsxsxszs/VeXplore/HEAD/VeXplore/Assets.xcassets/Ignore.imageset/1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash copy.png
--------------------------------------------------------------------------------
/VeXplore/libxml_header.h:
--------------------------------------------------------------------------------
1 | //
2 | // libxml_header.h
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | #ifndef libxml_header_h
9 | #define libxml_header_h
10 |
11 | #import "tree.h"
12 | #import "parser.h"
13 | #import "HTMLtree.h"
14 | #import "HTMLparser.h"
15 | #import "xpath.h"
16 |
17 | #endif /* libxml_header_h */
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/VeXplore/Define.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Define.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | // Closure
9 | typealias CompletionTask = (_ success: Bool) -> Void
10 | typealias IgnoreHandler = (_ topicId: String) -> Void
11 | typealias UnfavoriteHandler = (_ topicId: String) -> Void
12 | typealias UnfollowingHandler = (_ username: String) -> Void
13 |
14 | let isProEnabled = true
15 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Login_Background.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "background.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/VeXplore/font.css:
--------------------------------------------------------------------------------
1 | /* font-size */
2 | h1 {
3 | font-size: px;
4 | }
5 |
6 | h2 {
7 | font-size: px;
8 | }
9 |
10 | h3 {
11 | font-size: px;
12 | }
13 |
14 | pre {
15 | font-size: px;
16 | }
17 |
18 | body {
19 | font-size: px;
20 | }
21 |
22 | .subtle {
23 | font-size : px;
24 | }
25 |
26 | .subtle .fade {
27 | font-size : px;
28 | }
29 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Write.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719383_pen.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719383_pen 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Close.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461865222_cross.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461865222_cross 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Logout.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "MainMenu_Logout-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "MainMenu_Logout-2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Safari.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "safari-logo (1).png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "safari-logo (1) 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Send.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1463765393_send.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1463765393_send 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Setting.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719337_cog 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719337_cog.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Time.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464770510_time.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464770510_time 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Topics.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464770499_note.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464770499_note 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Github.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461855896_github copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461856003_github.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Replies.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719153_back 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719153_back 3.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitch.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461859235_twitch.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461859235_twitch copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search_Nav.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719360_search copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719360_search.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Thank.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719369_heart copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719369_heart copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Avatar_Placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461865203_photo copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461865203_photo.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/More.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464181620_MoreVertical copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464181620_MoreVertical.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Psn.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461864196_playstation.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461864196_playstation copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719371_return copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719371_return copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Twitter.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461864512_Twitter-1 copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461864512_Twitter-1.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/ImageClick.js:
--------------------------------------------------------------------------------
1 | document.addEventListener('click', function (e)
2 | {
3 | if (e.target.nodeName.toUpperCase() == 'IMG')
4 | {
5 | location.href = 'https://' + '/special_tag_for_image_tap_vexplore/' + e.target.offsetLeft + '/special_tag_for_image_tap_vexplore/' + e.target.offsetTop + '/special_tag_for_image_tap_vexplore/' + e.target.clientWidth + '/special_tag_for_image_tap_vexplore/' + e.target.clientHeight + '/special_tag_for_image_tap_vexplore/' + e.target.attributes['image_cache_key'].value
6 | }
7 | });
8 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Arrow_Right.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464770037_Arrow-Right.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464770037_Arrow-Right (1).png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1463995674_18.Pictures-Day.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1463995674_18.Pictures-Day 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Report_Activity.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1465632015_like copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1465632015_like copy 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Delete.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1463681808_icon-27-trash-can 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1463681808_icon-27-trash-can.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Lock.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461862337_icon-114-lock copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461862337_icon-114-lock copy 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Sort.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462719426_preferences copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462719426_preferences copy 2-1.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Hide.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462747113_icon-21-eye-hidden copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462747113_icon-21-eye-hidden copy 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Home.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462732243_home_house_real_estate.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462732243_home_house_real_estate 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Invisible.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461862272_icon-21-eye-hidden copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461862272_icon-21-eye-hidden copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Location.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861830_gps_location_map_marker copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861830_gps_location_map_marker.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Nodes.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462736683_compass_direction_navigation.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462736683_compass_direction_navigation 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Owner_View.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464780512_eye_preview_see_seen_view.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464780510_eye_preview_see_seen_view.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Homepage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861739_home_house_real_estate.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861739_home_house_real_estate copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861842_heart_like_love_vote.png copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861842_heart_like_love_vote.png copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Favorite_Activity.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861842_heart_like_love_vote.png.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861842_heart_like_love_vote.png 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Like.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861842_heart_like_love_vote.png copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861842_heart_like_love_vote.png copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Reply_Activity.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1465279341_SubdirectoryArrowUpLeft-1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1465279341_SubdirectoryArrowUpLeft copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Cross.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861579_close_delete_remove_icon copy 3.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861579_close_delete_remove_icon copy 2-1.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Dribbble.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461860642_dribbble_online_social_media copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461860642_dribbble_online_social_media.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Wang_Xizhi.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "0eb30f2442a7d9330262cc43af4bd11373f00133.jpg",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "0eb30f2442a7d9330262cc43af4bd11373f00133 2.jpg",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Onepassword.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "onepassword-navbar.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "onepassword-navbar@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "onepassword-navbar@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tick.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462731373_accept_check_ok_outline_tick_yes copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462731373_accept_check_ok_outline_tick_yes copy 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Node_Placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1463740612_smiley-face-emoticon-avatar-brand copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1463740612_smiley-face-emoticon-avatar-brand.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/SharedKit/SharedKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // SharedKit.h
3 | // SharedKit
4 | //
5 | // Created by Jing Chen on 1/4/17.
6 | // Copyright © 2017 Jimmy. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for SharedKit.
12 | FOUNDATION_EXPORT double SharedKitVersionNumber;
13 |
14 | //! Project version string for SharedKit.
15 | FOUNDATION_EXPORT const unsigned char SharedKitVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Comment.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861747_bubble_chat_comment_message_outline_talk copy 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861747_bubble_chat_comment_message_outline_talk copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Recent.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462732316_alarm_alert_clock_event_history_schedule_time_watch.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462732316_alarm_alert_clock_event_history_schedule_time_watch copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Homepage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461858344_house-home-export-real_estate-property-outline-stroke.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461858344_house-home-export-real_estate-property-outline-stroke copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Profile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1461861750_account_friend_human_man_member_person_profile_user_users 2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1461861750_account_friend_human_man_member_person_profile_user_users.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Search.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462732255_find_in_magnifier_magnifying_research_search_view_zoom.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462732255_find_in_magnifier_magnifying_research_search_view_zoom copy.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Tabar_Search.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464775281_find_in_magnifier_magnifying_research_search_view_zoom.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464775278_find_in_magnifier_magnifying_research_search_view_zoom.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Notification.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1462732261_bell_sound_notification_remind_reminder_ring_ringing_schedule 2.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/Assets.xcassets/Ignore.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash copy.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "1464180871_bin_cancel_close_cross_delete_empty_exit_garbage_minus_out_recycle_remove_trash.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/VeXplore/ModelResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ModelResponse.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class CommonResponse
10 | {
11 | var success = false
12 | var message = [String]()
13 |
14 | init(success: Bool, message: [String] = [String]())
15 | {
16 | self.success = success
17 | self.message = message
18 | }
19 |
20 | }
21 |
22 | class ValueResponse: CommonResponse
23 | {
24 | var value: T?
25 |
26 | init(value: T? = nil, success: Bool, message: [String] = [String]())
27 | {
28 | super.init(success: success, message: message)
29 | self.value = value
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/VeXplore/ImageReplace.js:
--------------------------------------------------------------------------------
1 | var imageNodes = document.getElementsByTagName("img")
2 | for (var i = 0; i < imageNodes.length; i++)
3 | {
4 | try {
5 | var $el = imageNodes[i].parentNode;
6 | var href = $el.getAttribute('href').split('/').pop();
7 | var src = imageNodes[i].getAttribute('src').split('/').pop();
8 | var imageCacheKey = imageNodes[i].getAttribute('image_cache_key').split('/').pop();
9 | if (href === src || href === imageCacheKey)
10 | {
11 | $el.setAttribute('href', 'javscript:void();');
12 | }
13 | } catch (err) {}
14 |
15 | if (imageNodes[i].getAttribute("image_cache_key") == "%@")
16 | {
17 | imageNodes[i].setAttribute("src", "%@")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/SharedKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Scripts/gitVersion.sh:
--------------------------------------------------------------------------------
1 | # !/bin/bash
2 |
3 |
4 | ANNOTATED_TAG_NAME="baseVersioningTag"
5 | VERSIONING_XCCONFIG_FILE_PATH="${SRCROOT}/Version.xcconfig"
6 |
7 | if [ ! -e $VERSIONING_XCCONFIG_FILE_PATH ]; then
8 | touch $VERSIONING_XCCONFIG_FILE_PATH
9 | fi
10 |
11 | pushd "$SRCROOT" >/dev/null
12 | # Fetch Git Output
13 | GIT_DESCRIBE_OUTPUT=$(git describe --match $ANNOTATED_TAG_NAME) # e.g., baseVersioningTag-423-g41438bd
14 |
15 | # Use Internal Field Separator to split on "-"
16 | IFS='-'
17 | GIT_DESCRIBE_COMPONENTS=($GIT_DESCRIBE_OUTPUT) # baseVersioningTag 423 g41438bd
18 | unset IFS
19 | VERSION_NUMBER=${GIT_DESCRIBE_COMPONENTS[1]}
20 |
21 | ## Write Build Configuration Values to Xcode Versioning file
22 | if [ -z $VERSION_NUMBER ]
23 | then
24 | echo "BUILD_NUMBER = 1" > $VERSIONING_XCCONFIG_FILE_PATH # set default value if VERSION_NUMBER is empty
25 | else
26 | echo "BUILD_NUMBER = ${VERSION_NUMBER}" > $VERSIONING_XCCONFIG_FILE_PATH
27 | fi
28 |
29 | # Finish file with a Touch
30 | touch $VERSIONING_XCCONFIG_FILE_PATH
31 | popd >/dev/null
32 |
--------------------------------------------------------------------------------
/TodayExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | 今日热议
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | ${RELEASE_VERSION}
21 | CFBundleVersion
22 | ${BUILD_NUMBER}
23 | NSExtension
24 |
25 | NSExtensionPointIdentifier
26 | com.apple.widget-extension
27 | NSExtensionPrincipalClass
28 | $(PRODUCT_NAME).TodayViewController
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/SharedKit/SharedUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | //////////////////////////
10 | ////// Thread Utils //////
11 | //////////////////////////
12 |
13 | public func dispatch_async_safely_to_main_queue(block: @escaping ()->())
14 | {
15 | dispatch_async_safely_to_queue(DispatchQueue.main, block)
16 | }
17 |
18 | public func dispatch_async_to_background_queue(block: @escaping () -> ())
19 | {
20 | dispatch_async_safely_to_queue(DispatchQueue.global(qos: .default), block)
21 | }
22 |
23 | public func dispatch_async_safely_to_queue(_ queue: DispatchQueue, _ block: @escaping ()->())
24 | {
25 | if queue === DispatchQueue.main, Thread.isMainThread
26 | {
27 | block()
28 | }
29 | else
30 | {
31 | queue.async {
32 | block()
33 | }
34 | }
35 | }
36 |
37 | public func dispatch_delay_in_main_queue(delay: TimeInterval, block: @escaping ()->())
38 | {
39 | if Thread.isMainThread
40 | {
41 | DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: block)
42 | }
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/VeXplore/RichTextRunDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RichTextRunDelegate.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | class RichTextRunDelegate
11 | {
12 | var ascent: CGFloat = 0.0
13 | var descent: CGFloat = 0.0
14 | var width: CGFloat = 0.0
15 |
16 | var ctRunDelegate: CTRunDelegate? {
17 | var callbacks = CTRunDelegateCallbacks(version: kCTRunDelegateCurrentVersion, dealloc: { (refCon) -> Void in
18 | }, getAscent: { (refCon) -> CGFloat in
19 | let ref = unsafeBitCast(refCon, to: RichTextRunDelegate.self)
20 | return ref.ascent
21 | }, getDescent: { (refCon) -> CGFloat in
22 | let ref = unsafeBitCast(refCon, to: RichTextRunDelegate.self)
23 | return ref.descent
24 | }, getWidth: { (refCon) -> CGFloat in
25 | let ref = unsafeBitCast(refCon, to: RichTextRunDelegate.self)
26 | return ref.width
27 | })
28 |
29 | let selfPtr = UnsafeMutableRawPointer(Unmanaged.passRetained(self).toOpaque())
30 | return CTRunDelegateCreate(&callbacks, selfPtr)
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/VeXplore/NotificationModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NotificationModel.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | struct NotificationModel: Codable
11 | {
12 | private(set) var avatar: String?
13 | private(set) var username: String?
14 | private(set) var title: String?
15 | private(set) var date: String?
16 | private(set) var comment: String?
17 | private(set) var notificationId: String?
18 | private(set) var topicId: String?
19 |
20 | init(rootNode: HTMLNode)
21 | {
22 | avatar = rootNode.xPath(".//img[@class='avatar']").first?["src"]
23 | username = rootNode.xPath("./table/tr/td[2]/span[1]/a[1]/strong").first?.content
24 | title = rootNode.xPath("./table/tr/td[2]/span[1]").first?.content
25 | date = rootNode.xPath("./table/tr/td[2]/span[2]").first?.content
26 | comment = rootNode.xPath("./table/tr/td[2]/div[@class='payload']").first?.content
27 | notificationId = rootNode["id"]?.replacingOccurrences(of: "n_", with: SharedR.String.Empty)
28 | let topicIdUrl = rootNode.xPath("./table/tr/td[2]/span[1]/a[2]").first?["href"]
29 | topicId = topicIdUrl?.extractId()
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/VeXplore/Protocols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Protocols.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | protocol AvatarTappedDelegate: class
9 | {
10 | func avatarTapped(withUsername username: String)
11 | }
12 |
13 |
14 | protocol TopicCellDelegate: AvatarTappedDelegate
15 | {
16 | func nodeTapped(withNodeId nodeId: String, nodeName: String?)
17 | }
18 |
19 |
20 | protocol TopicDetailDelegate: TopicCellDelegate
21 | {
22 | func favoriteBtnTapped()
23 | }
24 |
25 |
26 | protocol SwipeCellDelegate: class
27 | {
28 | func cellWillBeginSwipe(at indexPath: IndexPath)
29 | func cellShouldBeginSwpipe() -> Bool
30 | }
31 | // optional func in SwipeCellDelegate
32 | extension SwipeCellDelegate
33 | {
34 | func cellShouldBeginSwpipe() -> Bool
35 | {
36 | return true
37 | }
38 | }
39 |
40 |
41 | protocol NotificationCellDelegate: AvatarTappedDelegate, SwipeCellDelegate
42 | {
43 | func deleteNotification(withId notificationId: String)
44 | }
45 |
46 |
47 | protocol CommentCellDelegate: AvatarTappedDelegate, SwipeCellDelegate
48 | {
49 | func thankBtnTapped(withReplyId replyId: String, indexPath: IndexPath)
50 | func ignoreBtnTapped(withReplyId replyId: String)
51 | func replyBtnTapped(withUsername username: String, index: String)
52 | }
53 |
--------------------------------------------------------------------------------
/VeXplore/WebImageUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebImageUtils.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | extension String
11 | {
12 | var md5: String {
13 | let context = UnsafeMutablePointer.allocate(capacity: 1)
14 | var digest = Array(repeating:0, count:Int(CC_MD5_DIGEST_LENGTH))
15 | CC_MD5_Init(context)
16 | CC_MD5_Update(context, self, CC_LONG(lengthOfBytes(using: String.Encoding.utf8)))
17 | CC_MD5_Final(&digest, context)
18 | context.deallocate(capacity: 1)
19 | var hexString = ""
20 | for byte in digest
21 | {
22 | hexString += String(format:"%02x", byte)
23 | }
24 | return hexString
25 | }
26 |
27 | }
28 |
29 |
30 | extension Data
31 | {
32 | static var gifHeader: [UInt8] = [0x47, 0x49, 0x46]
33 |
34 | var isGifFormat: Bool {
35 | var buffer = [UInt8](repeating: 0, count: 3)
36 | (self as NSData).getBytes(&buffer, length: 3)
37 | if buffer == Data.gifHeader
38 | {
39 | return true
40 | }
41 | return false
42 | }
43 |
44 | }
45 |
46 | extension Int
47 | {
48 | func isValidStatusCode() -> Bool
49 | {
50 | return (200..<400).contains(self)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ActionExtension/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | VeXplore
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | ${RELEASE_VERSION}
21 | CFBundleVersion
22 | ${BUILD_NUMBER}
23 | NSExtension
24 |
25 | NSExtensionAttributes
26 |
27 | NSExtensionActivationRule
28 |
29 | NSExtensionActivationSupportsWebURLWithMaxCount
30 | 1
31 |
32 |
33 | NSExtensionPointIdentifier
34 | com.apple.ui-services
35 | NSExtensionPrincipalClass
36 | $(PRODUCT_NAME).ActionNavigationController
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/VeXplore/LoginPageTextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginPageTextField.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | //import SharedKit
9 | //
10 | //class LoginPageTextField: UITextField
11 | //{
12 | // lazy var bendingLine: BendingLine = {
13 | // let view = BendingLine()
14 | // view.translatesAutoresizingMaskIntoConstraints = false
15 | // view.backgroundColor = .clear
16 | // view.lineColor = .desc
17 | //
18 | // return view
19 | // }()
20 | //
21 | // override init(frame: CGRect)
22 | // {
23 | // super.init(frame: frame)
24 | //
25 | // addSubview(bendingLine)
26 | // let bindings = ["bendingLine": bendingLine]
27 | // addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[bendingLine]|", metrics: nil, views: bindings))
28 | // bendingLine.topAnchor.constraint(equalTo: bottomAnchor).isActive = true
29 | // bendingLine.heightAnchor.constraint(equalToConstant: 8.0).isActive = true
30 | //
31 | // font = SharedR.Font.Medium
32 | // clipsToBounds = false
33 | // autocorrectionType = .no
34 | // autocapitalizationType = .none
35 | // }
36 | //
37 | // required init?(coder aDecoder: NSCoder)
38 | // {
39 | // fatalError("init(coder:) has not been implemented")
40 | // }
41 | //
42 | //}
43 |
44 |
--------------------------------------------------------------------------------
/VeXplore/NodeSelectViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NodeSelectViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | protocol NodeSelectDelegate: class
10 | {
11 | func didSelectNode(_ node: NodeModel)
12 | }
13 |
14 | class NodeSelectViewController: NodeSearchViewController
15 | {
16 | weak var delegate: NodeSelectDelegate?
17 |
18 | override func viewDidLoad()
19 | {
20 | super.viewDidLoad()
21 | title = R.String.NodeChoose
22 |
23 | let closeBtn = UIBarButtonItem(image: R.Image.Close, style: .plain, target: self, action: #selector(closeBtnTapped))
24 | navigationItem.leftBarButtonItem = closeBtn
25 | tableView.estimatedRowHeight = R.Constant.EstimatedRowHeight
26 | }
27 |
28 | @objc
29 | private func closeBtnTapped()
30 | {
31 | dismiss(animated: true, completion: nil)
32 | }
33 |
34 | // MARK: - UITableViewDelegate
35 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
36 | {
37 | let nodeModel = isSearching ? searchResultNodes[indexPath.section].nodes[indexPath.row] : groupedNodes[indexPath.section].nodes[indexPath.row]
38 | DispatchQueue.main.async {
39 | self.delegate?.didSelectNode(nodeModel)
40 | self.dismiss(animated: true, completion: nil)
41 | }
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/SharedKit/Response.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Response.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | public struct DataResponse
11 | {
12 | public let request: URLRequest?
13 | public let response: HTTPURLResponse?
14 | public let data: Data?
15 | public let result: Result
16 |
17 | }
18 |
19 | public enum Result
20 | {
21 | case success(Value)
22 | case failure(Error?)
23 |
24 | public var isSuccess: Bool {
25 | switch self {
26 | case .success:
27 | return true
28 | case .failure:
29 | return false
30 | }
31 | }
32 |
33 | public var value: Value? {
34 | switch self
35 | {
36 | case .success(let value):
37 | return value
38 | case .failure:
39 | return nil
40 | }
41 | }
42 |
43 | public var error: Error? {
44 | switch self
45 | {
46 | case .success:
47 | return nil
48 | case .failure(let error):
49 | return error
50 | }
51 | }
52 | }
53 |
54 | public struct DataResponseSerializer
55 | {
56 | public typealias SerializeResponse = (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result
57 | var serializeResponse: SerializeResponse
58 | public init(serializeResponse: @escaping SerializeResponse)
59 | {
60 | self.serializeResponse = serializeResponse
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/VeXplore/WebImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebImage.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | typealias ImageRetrieveCompletionHandler = ((_ image: UIImage?, _ originalData: Data?, _ error: NSError?) -> Void)
11 |
12 | struct WebImage
13 | {
14 | static func retrieveImage(with url: URL, completionHandler: ImageRetrieveCompletionHandler?)
15 | {
16 | ImageCache.default.retrieveImageData(forKey: url.cacheKey, completionHandler: { originalData in
17 | if originalData != nil
18 | {
19 | completionHandler?(nil, originalData, nil)
20 | }
21 | else
22 | {
23 | WebImage.downloadAndCacheImage(with: url, forKey: url.cacheKey, completionHandler: completionHandler)
24 | }
25 | })
26 | }
27 |
28 | private static func downloadAndCacheImage(with url: URL,
29 | forKey key: String,
30 | completionHandler: ImageRetrieveCompletionHandler?)
31 | {
32 | ImageDownloader.default.downloadImage(with: url, completionHandler: { image, originalData, error in
33 | if let image = image, let originalData = originalData
34 | {
35 | ImageCache.default.cache(image: image, originalData: originalData, forKey: key)
36 | }
37 | completionHandler?(image, originalData, error)
38 | })
39 | }
40 |
41 | }
42 |
43 | extension URL
44 | {
45 | var cacheKey: String {
46 | return absoluteString
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/VeXplore/CommentImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommentImageView.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | protocol CommentImageTapDelegate: class
10 | {
11 | func commentImageSingleTap(_ imageView: CommentImageView)
12 | }
13 |
14 | class CommentImageView: AnimatableImageView
15 | {
16 | var imageURL: String?
17 | weak var delegate: CommentImageTapDelegate?
18 |
19 | init()
20 | {
21 | super.init(frame: CGRect(x: 0, y: 0, width: R.Constant.CommentImageSize, height: R.Constant.CommentImageSize))
22 | contentMode = .scaleAspectFill
23 | clipsToBounds = true
24 | isUserInteractionEnabled = true
25 | }
26 |
27 | required init?(coder aDecoder: NSCoder)
28 | {
29 | fatalError("init(coder:) has not been implemented")
30 | }
31 |
32 | override func willMove(toSuperview newSuperview: UIView?)
33 | {
34 | super.willMove(toSuperview: newSuperview)
35 | guard image == nil else {
36 | return
37 | }
38 |
39 | if let imageURL = imageURL, let URL = URL(string: imageURL)
40 | {
41 | setImageOriginalData(with: URL, placeholder: R.Image.ImagePlaceholder)
42 | }
43 | }
44 |
45 | override func touchesEnded(_ touches: Set, with event: UIEvent?)
46 | {
47 | if let touch = touches.first, touch.tapCount == 1
48 | {
49 | handleSingleTap()
50 | }
51 | next?.touchesCancelled(touches, with: event)
52 | }
53 |
54 | func handleSingleTap()
55 | {
56 | delegate?.commentImageSingleTap(self)
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/VeXplore/SectionHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SectionHeaderView.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class SectionHeaderView: BaseTableViewHeaderFooterView
11 | {
12 | lazy var contentLabel: UILabel = {
13 | let label = UILabel()
14 | label.translatesAutoresizingMaskIntoConstraints = false
15 | label.font = SharedR.Font.VeryLarge
16 | label.textColor = .body
17 |
18 | return label
19 | }()
20 |
21 | override init(reuseIdentifier: String?)
22 | {
23 | super.init(reuseIdentifier: reuseIdentifier)
24 |
25 | contentView.addSubview(contentLabel)
26 | let bindings = ["contentLabel": contentLabel]
27 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-14-[contentLabel]-12-|", metrics: nil, views: bindings))
28 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-2-[contentLabel]-2-|", metrics: nil, views: bindings))
29 |
30 | backgroundView = {
31 | let view = UIView(frame: bounds)
32 | view.backgroundColor = UIColor.border.withAlphaComponent(0.8)
33 |
34 | return view
35 | }()
36 | }
37 |
38 | required init?(coder aDecoder: NSCoder)
39 | {
40 | fatalError("init(coder:) has not been implemented")
41 | }
42 |
43 | @objc
44 | override func refreshColorScheme()
45 | {
46 | super.refreshColorScheme()
47 | contentLabel.textColor = .body
48 | backgroundView?.backgroundColor = UIColor.border.withAlphaComponent(0.8)
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/VeXplore/AboutMeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AboutMeCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class AboutMeCell: BaseTableViewCell
11 | {
12 | lazy var contentLabel: UILabel = {
13 | let label = UILabel()
14 | label.translatesAutoresizingMaskIntoConstraints = false
15 | label.numberOfLines = 0
16 | label.font = SharedR.Font.Medium
17 | label.textColor = UIColor.desc
18 |
19 | return label
20 | }()
21 |
22 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
23 | {
24 | super.init(style: style, reuseIdentifier: reuseIdentifier)
25 |
26 | contentView.addSubview(contentLabel)
27 | let bindings = ["contentLabel": contentLabel]
28 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-12-[contentLabel]-12-|", metrics: nil, views: bindings))
29 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[contentLabel]-8-|", metrics: nil, views: bindings))
30 |
31 | preservesSuperviewLayoutMargins = false
32 | layoutMargins = .zero
33 | selectionStyle = .none
34 | }
35 |
36 | required init?(coder aDecoder: NSCoder)
37 | {
38 | fatalError("init(coder:) has not been implemented")
39 | }
40 |
41 | override func prepareForReuse()
42 | {
43 | super.prepareForReuse()
44 | contentLabel.font = SharedR.Font.Medium
45 | }
46 |
47 | @objc
48 | override func refreshColorScheme()
49 | {
50 | super.refreshColorScheme()
51 | contentLabel.textColor = .desc
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/VeXplore/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/VeXplore/RichTextUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RichTextUtils.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 |
11 | let AttachmentAttributeName = "vexplore.textAttributeName.attachment"
12 | let HighlightAttributeName = "vexplore.textAttributeName.highlight"
13 |
14 | extension NSMutableAttributedString
15 | {
16 | class func attachmentString(with imageView: UIImageView, size: CGSize, alignTo font: UIFont) -> NSMutableAttributedString
17 | {
18 | let attrs = NSMutableAttributedString(string: " ")
19 | attrs.addAttribute(AttachmentAttributeName, value: imageView)
20 | let delegate = RichTextRunDelegate()
21 | delegate.width = size.width
22 | delegate.ascent = max(size.height + font.descender, 0)
23 | delegate.descent = size.height - delegate.ascent
24 | if let ctRunDelegate = delegate.ctRunDelegate
25 | {
26 | attrs.addAttribute(kCTRunDelegateAttributeName as String, value: ctRunDelegate)
27 | }
28 | return attrs
29 | }
30 |
31 | func setHighlightText(withColor color: UIColor, url: String)
32 | {
33 | addAttribute(NSAttributedStringKey.foregroundColor.rawValue, value: color)
34 | addAttribute(HighlightAttributeName, value: url)
35 | }
36 |
37 | func set(lineSpacing: CGFloat)
38 | {
39 | let style = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
40 | style.lineSpacing = lineSpacing
41 | addAttribute(NSAttributedStringKey.paragraphStyle.rawValue, value: style)
42 | }
43 |
44 | func addAttribute(_ name: String, value: Any)
45 | {
46 | addAttribute(NSAttributedStringKey(rawValue: name), value: value, range: NSMakeRange(0, length))
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VeXplore
2 | Third-party app for V2EX. Developed using Swift.
3 |
4 | ## Download
5 | [VeXplore (Charged)](https://itunes.apple.com/us/app/vexplore/id1119508407?ls=1&mt=8)
6 | [VeXplore Lite (Free)](https://itunes.apple.com/us/app/vexplore-free/id1191058321?ls=1&mt=8)
7 |
8 | ## Screenshots
9 |  | 
10 | ---
11 |  | 
12 | ---
13 |  | 
14 | ---
15 |  | 
16 | ---
17 |  | 
18 | ---
19 |  | 
20 | ---
21 |  | 
22 | ---
23 |  | 
24 | ---
25 |  | 
26 | ---
27 |  | 
28 | ---
29 |  | 
30 | ---
31 |  | 
32 |
33 | ## Build Requirements
34 | * Xcode 8.3+
35 | * iOS 9.0+
36 | * Open "VeXplore.xcodeproj"
37 | * Run
38 |
39 | ## Contact
40 | Any questions about this project, feel free to contact me via wmywbyt.cj@gmail.com.
41 |
42 |
43 |
--------------------------------------------------------------------------------
/VeXplore/AvatarImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AvatarImageView.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class AvatarImageView: UIImageView
11 | {
12 | private var imageDownloadId: ImageDownloadId?
13 |
14 | func avatarImage(withURL url: URL)
15 | {
16 | setImage(withURL: url, placeholderImage: R.Image.AvatarPlaceholder, imageProcessing: { (image) -> UIImage in
17 | return image.roundCornerImage()
18 | })
19 | }
20 |
21 | func cancelImageDownloadTaskIfNeed()
22 | {
23 | guard let imageDownloadId = imageDownloadId else {
24 | return
25 | }
26 | ImageDownloader.default.cancelImageDownloadTask(for: imageDownloadId)
27 | self.imageDownloadId = nil
28 | }
29 |
30 | private func setImage(withURL url: URL, placeholderImage: UIImage?, imageProcessing:((_ image: UIImage) -> UIImage)?)
31 | {
32 | image = placeholderImage
33 | ImageCache.default.retrieveImage(forKey: url.cacheKey) { image in
34 | if image != nil
35 | {
36 | dispatch_async_safely_to_main_queue {
37 | self.image = image
38 | }
39 | }
40 | else
41 | {
42 | self.cancelImageDownloadTaskIfNeed()
43 | self.imageDownloadId = ImageDownloader.default.downloadImage(with: url, completionHandler: { (image, originalData, error) in
44 | if let image = image, let roundedImage = imageProcessing?(image)
45 | {
46 | // cache image
47 | ImageCache.default.cache(image: roundedImage, forKey: url.cacheKey)
48 | dispatch_async_safely_to_main_queue {
49 | self.image = roundedImage
50 | }
51 | }
52 | })
53 | }
54 | }
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/VeXplore/baseStyle.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | font-weight: 500;
3 | line-height: 1.4;
4 | margin: 5px 0px 15px 0px;
5 | padding: 0px;
6 | }
7 |
8 | h2 {
9 | font-weight: 500;
10 | line-height: 1.2;
11 | margin: 20px 0px 20px 0px;
12 | padding: 0px 0px 8px 0px;
13 | border-bottom: 1px solid H2_BORDER_BOTTOM_COLOR_PLACEHOLDER;
14 | }
15 |
16 | h3 {
17 | font-weight: 500;
18 | line-height: 100%;
19 | margin: 5px 0px 20px 0px;
20 | padding: 0px 0px 8px 0px;
21 | }
22 |
23 | hr {
24 | border: none;
25 | height: 1px;
26 | margin-bottom: 1em;
27 | }
28 |
29 | pre {
30 | letter-spacing: 0.015em;
31 | line-height: 120%;
32 | padding: 0.5em;
33 | margin: 0px;
34 | white-space: pre;
35 | overflow-x: auto;
36 | overflow-y: auto;
37 | }
38 |
39 | pre a {
40 | color: inherit;
41 | text-decoration: underline;
42 | }
43 |
44 | code {
45 | padding: 1px 2px 1px 2px;
46 | border-radius: 2px;
47 | }
48 |
49 |
50 | ul {
51 | list-style: square;
52 | margin: 1em 0px 1em 1em;
53 | padding: 0px;
54 | }
55 |
56 | ul li, ol li {
57 | padding: 0px;
58 | margin: 0px;
59 | }
60 |
61 | ol {
62 | margin: 1em 0px 0em 2em;
63 | padding: 0px;
64 | }
65 |
66 | img {
67 | max-width: 100%;
68 | }
69 |
70 | .imgly {
71 | max-width: 100%;
72 | }
73 |
74 | p {
75 | margin-top: 3px;
76 | margin-bottom: 3px;
77 | }
78 |
79 |
80 | a:link, a:visited, a:active {
81 | text-decoration: none;
82 | word-break: break-all;
83 | color: HREF_COLOR_PLACEHOLDER;
84 | text-decoration: underline;
85 | }
86 |
87 | body {
88 | font-family: 'Helvetica', monospace;
89 | -webkit-text-size-adjust: none;
90 | line-height: 1.7;
91 | word-wrap: break-word;
92 | max-height: 20em;
93 | padding: 5px;
94 | color: BODY_COLOR_PLACEHOLDER;
95 | background-color: BODY_BACKGROUND_COLOR_PLACEHOLDER;
96 | }
97 |
98 | .subtle {
99 | padding: 5px;
100 | color: BODY_COLOR_PLACEHOLDER;
101 | background-color: SUBTITLE_BACKGROUND_COLOR_PLACEHOLDER;
102 | }
103 |
104 | .subtle .fade {
105 | color: SUBTITLE_FADE_COLOR_PLACEHOLDER;
106 | background-color: SUBTITLE_FADE_BACKGROUND_COLOR_PLACEHOLDER;
107 | }
108 |
--------------------------------------------------------------------------------
/VeXplore/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | ${RELEASE_VERSION}
19 | CFBundleSignature
20 | ????
21 | CFBundleURLTypes
22 |
23 |
24 | CFBundleTypeRole
25 | Editor
26 | CFBundleURLName
27 | in.jimmyis.vexplore
28 | CFBundleURLSchemes
29 |
30 | vexplore
31 |
32 |
33 |
34 | CFBundleVersion
35 | ${BUILD_NUMBER}
36 | LSApplicationQueriesSchemes
37 |
38 | org-appextension-feature-password-management
39 |
40 | LSRequiresIPhoneOS
41 |
42 | NSAppTransportSecurity
43 |
44 | NSAllowsArbitraryLoads
45 |
46 |
47 | NSPhotoLibraryUsageDescription
48 |
49 | UIApplicationShortcutWidget
50 | in.jimmyis.vexplore.TodayExtension
51 | UILaunchStoryboardName
52 | LaunchScreen
53 | UIRequiredDeviceCapabilities
54 |
55 | armv7
56 |
57 | UIRequiresFullScreen
58 |
59 | UISupportedInterfaceOrientations
60 |
61 | UIInterfaceOrientationPortrait
62 |
63 | UISupportedInterfaceOrientations~ipad
64 |
65 | UIInterfaceOrientationPortrait
66 | UIInterfaceOrientationPortraitUpsideDown
67 | UIInterfaceOrientationLandscapeLeft
68 | UIInterfaceOrientationLandscapeRight
69 |
70 | UIViewControllerBasedStatusBarAppearance
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/VeXplore/ProfileSectionHeaderCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileSectionHeaderCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class ProfileSectionHeaderCell: BaseTableViewCell
11 | {
12 | lazy var titleLabel: UILabel = {
13 | let label = UILabel()
14 | label.translatesAutoresizingMaskIntoConstraints = false
15 | label.font = SharedR.Font.Small
16 | label.textColor = .desc
17 | label.textAlignment = .center
18 |
19 | return label
20 | }()
21 |
22 | private lazy var bottomLine: UIView = {
23 | let view = UIView()
24 | view.translatesAutoresizingMaskIntoConstraints = false
25 | view.backgroundColor = .border
26 |
27 | return view
28 | }()
29 |
30 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
31 | {
32 | super.init(style: style, reuseIdentifier: reuseIdentifier)
33 |
34 | contentView.addSubview(titleLabel)
35 | contentView.addSubview(bottomLine)
36 | let bindings = [
37 | "titleLabel": titleLabel,
38 | "bottomLine": bottomLine
39 | ]
40 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[titleLabel]|", metrics: nil, views: bindings))
41 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[titleLabel]-8-|", metrics: nil, views: bindings))
42 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[bottomLine]|", metrics: nil, views: bindings))
43 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[bottomLine(0.5)]|", metrics: nil, views: bindings))
44 |
45 | preservesSuperviewLayoutMargins = false
46 | layoutMargins = .zero
47 | selectionStyle = .none
48 | }
49 |
50 | required init?(coder aDecoder: NSCoder)
51 | {
52 | fatalError("init(coder:) has not been implemented")
53 | }
54 |
55 | override func prepareForReuse()
56 | {
57 | super.prepareForReuse()
58 | titleLabel.font = SharedR.Font.Small
59 | }
60 |
61 | @objc
62 | override func refreshColorScheme()
63 | {
64 | super.refreshColorScheme()
65 | titleLabel.textColor = .desc
66 | bottomLine.backgroundColor = .border
67 | contentView.backgroundColor = .subBackground
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/VeXplore/NodeModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NodeModel.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 |
11 | class NodeModel: NSObject, Codable
12 | {
13 | private(set) var nodeId: String?
14 | private(set) var nodeName: String?
15 | private(set) var avatar: String? = nil
16 |
17 | //sort
18 | @objc var initialLetter: String? = nil
19 | @objc var allLetter: String?
20 | var allLetterWithoutSpace: String?
21 |
22 | private enum CodingKeys: String, CodingKey {
23 | case nodeId
24 | case nodeName
25 | case allLetter
26 | case allLetterWithoutSpace
27 | }
28 |
29 | init(json: JSON)
30 | {
31 | nodeId = json["name"].string
32 | nodeName = json["title"].string
33 | }
34 |
35 | init(rootNode: HTMLNode)
36 | {
37 | nodeName = rootNode.content
38 | if var href = rootNode["href"], let range = href.range(of: "/go/")
39 | {
40 | href.replaceSubrange(range, with: SharedR.String.Empty)
41 | nodeId = href
42 | }
43 | avatar = rootNode.xPath(".//img").first?["src"]
44 | }
45 |
46 | init(favoriteNode: HTMLNode)
47 | {
48 | nodeName = favoriteNode.xPath("./div/text()").first?.content
49 | if var href = favoriteNode["href"], let range = href.range(of: "/go/")
50 | {
51 | href.replaceSubrange(range, with: SharedR.String.Empty)
52 | nodeId = href
53 | }
54 | avatar = favoriteNode.xPath(".//img").first?["src"]
55 | }
56 |
57 | }
58 |
59 |
60 | struct NodeGroupModel: Codable
61 | {
62 | private(set) var childNodes = [NodeModel]()
63 | private(set) var groupName: String?
64 |
65 | init(rootNode: HTMLNode)
66 | {
67 | groupName = rootNode.xPath("./td[1]/span").first?.content
68 | for node in rootNode.xPath("./td[2]/a")
69 | {
70 | childNodes.append(NodeModel(rootNode: node))
71 | }
72 | }
73 |
74 | }
75 |
76 | extension NodeGroupModel: Equatable {}
77 |
78 | func ==(lhs: NodeGroupModel, rhs: NodeGroupModel) -> Bool
79 | {
80 | guard lhs.groupName == rhs.groupName, lhs.childNodes.count == rhs.childNodes.count else {
81 | return false
82 | }
83 |
84 | for i in 0.. 0 else {
65 | return
66 | }
67 | if let startIndex = notificationString.index(notificationString.startIndex, offsetBy: range.location + 1, limitedBy: notificationString.endIndex),
68 | let endIndex = notificationString.index(notificationString.startIndex, offsetBy: range.location + range.length - 1, limitedBy: notificationString.endIndex)
69 | {
70 | let countString = notificationString[startIndex.. 0
29 | {
30 | type = URLType(rawValue: index)!
31 | switch type
32 | {
33 | case .topic:
34 | if let range = url.range(of: "/t/")
35 | {
36 | var topicId = url[range.upperBound...]
37 | if let range = topicId.range(of: "?")
38 | {
39 | topicId = topicId[.. Request? {
18 | get
19 | {
20 | lock.lock()
21 | defer { lock.unlock() }
22 | return requests[task.taskIdentifier]
23 | }
24 | set
25 | {
26 | lock.lock()
27 | defer { lock.unlock() }
28 | requests[task.taskIdentifier] = newValue
29 | }
30 | }
31 |
32 | private override init()
33 | {
34 | super.init()
35 | session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
36 | }
37 |
38 | deinit
39 | {
40 | session.invalidateAndCancel()
41 | }
42 |
43 | func request(_ url: String,
44 | method: HTTPMethod = .get,
45 | parameters: [String: String]? = nil,
46 | headers: [String: String]? = nil) -> Request
47 | {
48 | do {
49 | let urlRequest = try URLRequest(url: url, method: method, headers: headers)
50 | let encodedUrlRequest = try urlRequest.encode(with: parameters)
51 | return request(with: encodedUrlRequest)
52 | } catch {
53 | return request(with: error)
54 | }
55 | }
56 |
57 | private func request(with urlRequest: URLRequest) -> Request
58 | {
59 | let task = session.dataTask(with: urlRequest)
60 | let request = Request(session: session, task: task)
61 | self[task] = request
62 | request.resume()
63 | return request
64 | }
65 |
66 | private func request(with error: Error) -> Request
67 | {
68 | let request = Request(session: session, task: nil, error: error)
69 | request.resume()
70 | return request
71 | }
72 |
73 | // MARK: - URLSessionDataDelegate
74 | public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
75 | {
76 | if let request = self[task]
77 | {
78 | request.didComplete(withError: error)
79 | }
80 | self[task] = nil
81 | }
82 |
83 | // MARK: - URLSessionDataDelegate
84 | public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
85 | {
86 | if let request = self[dataTask]
87 | {
88 | request.didReceive(data: data)
89 | }
90 | }
91 |
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/VeXplore/TopicListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TopicListViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class TopicListViewController: SwipeTableViewController
10 | {
11 | override func viewDidLoad()
12 | {
13 | super.viewDidLoad()
14 | tableView.register(TopicCell.self, forCellReuseIdentifier: String(describing: TopicCell.self))
15 | NotificationCenter.default.addObserver(self, selector: #selector(handleFontsizeDidChanged), name: Notification.Name.Setting.FontsizeDidChange, object: nil)
16 | }
17 |
18 | @objc
19 | func handleFontsizeDidChanged()
20 | {
21 | tableView.reloadData()
22 | }
23 |
24 | // MARK: - UITableViewDataSource
25 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
26 | {
27 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TopicCell.self), for: indexPath) as! TopicCell
28 | let topicItem = topicList[indexPath.row]
29 | cell.topicItemModel = topicItem
30 | cell.topicTitleLabel.text = topicItem.topicTitle
31 | cell.userNameLabel.text = topicItem.username
32 | cell.nodeNameBtn.setTitle(topicItem.nodeName, for: .normal)
33 | if let repliesNumberString = topicItem.repliesNumber, repliesNumberString.isEmpty == false
34 | {
35 | cell.repliesNumberLabel.text = repliesNumberString
36 | }
37 | if let avatar = topicItem.avatar, let url = URL(string: R.String.Https + avatar)
38 | {
39 | cell.avatarImageView.avatarImage(withURL: url)
40 | }
41 | cell.lastReplayDateAndUserLabel.text = R.String.NoRepliesNow
42 | if let lastReplyDate = topicItem.lastReplyDate
43 | {
44 | if topicItem.lastReplyUserName != nil
45 | {
46 | cell.lastReplayDateAndUserLabel.text = lastReplyDate
47 | }
48 | else
49 | {
50 | cell.lastReplayDateAndUserLabel.text = String(format: R.String.PublicDate, lastReplyDate)
51 | }
52 | }
53 | cell.delegate = self
54 | return cell
55 | }
56 |
57 | // MARK: - UITableViewDelegate
58 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
59 | {
60 | if let topicId = topicList[indexPath.row].topicId
61 | {
62 | let topicVC = TopicViewController(topicId: topicId)
63 | topicVC.ignoreHandler = { topicId -> Void in
64 | self.removeTopic(withId: topicId)
65 | }
66 | DispatchQueue.main.async(execute: {
67 | self.bouncePresent(navigationVCWith: topicVC, completion: nil)
68 | })
69 | }
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/VeXplore/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | //////////////////////////////
9 | ////// Data Persistence //////
10 | //////////////////////////////
11 |
12 | func pageCacheDirPath() -> String?
13 | {
14 | if let sysCacheDirPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first
15 | {
16 | let pageCacheDirPath = (sysCacheDirPath as NSString).appendingPathComponent("pagesData")
17 | var isDir: ObjCBool = false
18 | let fileManager = FileManager.default
19 | var dirExists = fileManager.fileExists(atPath: pageCacheDirPath, isDirectory: &isDir)
20 | do {
21 | if dirExists == true && isDir.boolValue == false
22 | {
23 | try fileManager.removeItem(atPath: pageCacheDirPath)
24 | dirExists = false
25 | }
26 | if dirExists == false
27 | {
28 | try fileManager.createDirectory(atPath: pageCacheDirPath, withIntermediateDirectories: false, attributes: nil)
29 | }
30 | return pageCacheDirPath
31 | } catch let error as NSError {
32 | print(error.localizedDescription)
33 | return nil
34 | }
35 | }
36 | return nil
37 | }
38 |
39 | func cachePathString(withFilename filename: String) -> String?
40 | {
41 | var filePath: String? = nil
42 | if let dirPath = pageCacheDirPath()
43 | {
44 | filePath = (dirPath as NSString).appendingPathComponent(filename)
45 | }
46 | return filePath
47 | }
48 |
49 | func clearPageCache()
50 | {
51 | if let dirPath = pageCacheDirPath()
52 | {
53 | do {
54 | try FileManager.default.removeItem(atPath: dirPath)
55 | } catch let error as NSError {
56 | print(error.localizedDescription)
57 | }
58 | }
59 | }
60 |
61 |
62 | ////////////////////
63 | ////// Others //////
64 | ////////////////////
65 |
66 | func statusBarHeight() -> CGFloat
67 | {
68 | let statusBarFrame = UIApplication.shared.statusBarFrame
69 | return min(statusBarFrame.width, statusBarFrame.height)
70 | }
71 |
72 | func versionBuild() -> String
73 | {
74 | let version = currentVersion()
75 | let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as! String
76 | return "Version \(version)(\(build))"
77 | }
78 |
79 | func currentVersion() -> String
80 | {
81 | return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
82 | }
83 |
84 | // remove user defaults, for test
85 | func removeUserDefaults()
86 | {
87 | let appDomain = Bundle.main.bundleIdentifier
88 | UserDefaults.standard.removePersistentDomain(forName: appDomain!)
89 | }
90 |
91 | let isPad = UIDevice.current.userInterfaceIdiom == .pad
92 |
93 |
--------------------------------------------------------------------------------
/VeXplore/BaseCenterLoadingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseCenterLoadingViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class BaseCenterLoadingViewController: SwipeTransitionViewController, SquareLoadingViewDelegate, UITableViewDelegate, UITableViewDataSource
10 | {
11 | lazy var tableView: UITableView = {
12 | let tableView = UITableView(frame: self.view.bounds, style: .plain)
13 | tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
14 | tableView.dataSource = self
15 | tableView.delegate = self
16 | tableView.separatorStyle = .none
17 | tableView.isHidden = true
18 | tableView.backgroundColor = .background
19 |
20 | return tableView
21 | }()
22 |
23 | lazy var centerLoadingView: SquaresLoadingView = {
24 | let view = SquaresLoadingView(loadingStyle: LoadingStyle.bottom)
25 | view.translatesAutoresizingMaskIntoConstraints = false
26 | view.delegate = self
27 |
28 | return view
29 | }()
30 |
31 | override func viewDidLoad()
32 | {
33 | super.viewDidLoad()
34 |
35 | view.addSubview(tableView)
36 | view.addSubview(centerLoadingView)
37 | centerLoadingView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
38 | centerLoadingView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
39 | centerLoadingView.widthAnchor.constraint(equalToConstant: R.Constant.LoadingViewHeight).isActive = true
40 | centerLoadingView.initSquaresPosition()
41 | beginLoading()
42 | }
43 |
44 | @objc
45 | override func refreshColorScheme()
46 | {
47 | super.refreshColorScheme()
48 | tableView.backgroundColor = .background
49 | }
50 |
51 | private func beginLoading()
52 | {
53 | centerLoadingView.beginLoading()
54 | loadingRequest()
55 | }
56 |
57 | // MARK: - Public
58 | func loadingRequest()
59 | {
60 | // override this method in subclass
61 | }
62 |
63 | func stopLoading(withSuccesse success: Bool, completion: CompletionTask?)
64 | {
65 | centerLoadingView.stopLoading(withSuccess: success, completion: completion)
66 | }
67 |
68 | // MARK: - SquareLoadingViewDelegate
69 | func didTriggeredReloading()
70 | {
71 | beginLoading()
72 | }
73 |
74 | // MARK: - UITableViewDataSource
75 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
76 | {
77 | // override this method in subclass
78 | return 0
79 | }
80 |
81 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
82 | {
83 | // override this method in subclass
84 | return UITableViewCell()
85 | }
86 |
87 | // MARK: - UITableViewDelegate
88 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
89 | {
90 | // override this method in subclass
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/VeXplore/MyFollowingCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyFollowingCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class MyFollowingCell: BaseTableViewCell
11 | {
12 | lazy var avatarImageView: AvatarImageView = {
13 | let view = AvatarImageView()
14 | view.translatesAutoresizingMaskIntoConstraints = false
15 | view.contentMode = .scaleAspectFit
16 | view.tintColor = .body
17 |
18 | return view
19 | }()
20 |
21 | lazy var contentLabel: UILabel = {
22 | let label = UILabel()
23 | label.translatesAutoresizingMaskIntoConstraints = false
24 | label.font = SharedR.Font.Medium
25 | label.textColor = .desc
26 |
27 | return label
28 | }()
29 |
30 | private lazy var bottomLine: UIView = {
31 | let view = UIView()
32 | view.translatesAutoresizingMaskIntoConstraints = false
33 | view.backgroundColor = .border
34 |
35 | return view
36 | }()
37 |
38 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
39 | {
40 | super.init(style: style, reuseIdentifier: reuseIdentifier)
41 |
42 | contentView.addSubview(avatarImageView)
43 | contentView.addSubview(contentLabel)
44 | contentView.addSubview(bottomLine)
45 | let bindings = [
46 | "avatarImageView": avatarImageView,
47 | "contentLabel": contentLabel,
48 | "bottomLine": bottomLine
49 | ]
50 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-8-[avatarImageView(40@999)]-8-[contentLabel]-12-|", metrics: nil, views: bindings))
51 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[avatarImageView]-8-[bottomLine(0.5)]|", metrics: nil, views: bindings))
52 | contentLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
53 | avatarImageView.heightAnchor.constraint(equalTo: avatarImageView.widthAnchor).isActive = true
54 | bottomLine.leadingAnchor.constraint(equalTo: contentLabel.leadingAnchor).isActive = true
55 | bottomLine.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
56 |
57 | preservesSuperviewLayoutMargins = false
58 | layoutMargins = .zero
59 | selectionStyle = .none
60 | }
61 |
62 | required init?(coder aDecoder: NSCoder)
63 | {
64 | fatalError("init(coder:) has not been implemented")
65 | }
66 |
67 | override func prepareForReuse()
68 | {
69 | avatarImageView.cancelImageDownloadTaskIfNeed()
70 | super.prepareForReuse()
71 | avatarImageView.image = nil
72 | contentLabel.font = SharedR.Font.Medium
73 | }
74 |
75 | @objc
76 | override func refreshColorScheme()
77 | {
78 | super.refreshColorScheme()
79 | avatarImageView.tintColor = .body
80 | contentLabel.textColor = .desc
81 | bottomLine.backgroundColor = .border
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/VeXplore/CSSStyle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CSSStyle.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class CSSStyle
11 | {
12 | class var `default`: String {
13 | let BASE_CSS = try! String(contentsOfFile: Bundle.main.path(forResource: "baseStyle", ofType: "css")!, encoding: .utf8)
14 | let FONT_CSS = try! String(contentsOfFile: Bundle.main.path(forResource: "font", ofType: "css")!, encoding: .utf8)
15 |
16 | let COLOR_STYLE_ARRAY = [
17 | ColorStyle(colorName: "H2_COLOR_PLACEHOLDER", colorString: UIColor.border.toHexString()),
18 | ColorStyle(colorName: "HREF_COLOR_PLACEHOLDER", colorString: UIColor.href.toHexString()),
19 | ColorStyle(colorName: "BODY_COLOR_PLACEHOLDER", colorString: UIColor.body.toHexString()),
20 | ColorStyle(colorName: "BODY_BACKGROUND_COLOR_PLACEHOLDER", colorString: UIColor.background.toHexString()),
21 | ColorStyle(colorName: "SUBTITLE_BACKGROUND_COLOR_PLACEHOLDER", colorString: UIColor.refBackground.toHexString()),
22 | ColorStyle(colorName: "SUBTITLE_FADE_COLOR_PLACEHOLDER", colorString: UIColor.note.toHexString()),
23 | ColorStyle(colorName: "SUBTITLE_FADE_BACKGROUND_COLOR_PLACEHOLDER", colorString: UIColor.subBackground.toHexString())
24 | ]
25 | var baseCss = BASE_CSS
26 | COLOR_STYLE_ARRAY.forEach { colorStyle in
27 | baseCss = baseCss.replacingOccurrences(of: colorStyle.colorName, with: colorStyle.colorString)
28 | }
29 |
30 | let FONT_SIZE_STYLE_ARRAY = [
31 | FontSizeStyle(labelName:"", defaultFontSize: Int(UIFont.preferredFont(forTextStyle: UIFontTextStyle.title1).pointSize)),
32 | FontSizeStyle(labelName:"", defaultFontSize: Int(UIFont.preferredFont(forTextStyle: UIFontTextStyle.title2).pointSize)),
33 | FontSizeStyle(labelName:"", defaultFontSize: Int(UIFont.preferredFont(forTextStyle: UIFontTextStyle.title3).pointSize)),
34 | FontSizeStyle(labelName:"", defaultFontSize: Int(UIFont.preferredFont(forTextStyle: UIFontTextStyle.subheadline).pointSize)),
35 | FontSizeStyle(labelName:"", defaultFontSize: Int(SharedR.Font.Medium.pointSize)), // 正文
36 | FontSizeStyle(labelName:"", defaultFontSize: Int(SharedR.Font.Small.pointSize)), // 附言正文
37 | FontSizeStyle(labelName:"", defaultFontSize: Int(SharedR.Font.ExtraSmall.pointSize)) // 附言标题
38 | ]
39 | var fontCss = FONT_CSS
40 | FONT_SIZE_STYLE_ARRAY.forEach { fontSizeStyle in
41 | fontCss = fontCss.replacingOccurrences(of: fontSizeStyle.labelName, with: String(fontSizeStyle.defaultFontSize))
42 | }
43 |
44 | return baseCss + fontCss
45 | }
46 |
47 | private struct FontSizeStyle
48 | {
49 | let labelName: String
50 | let defaultFontSize: Int
51 | }
52 |
53 | private struct ColorStyle
54 | {
55 | let colorName: String
56 | let colorString: String
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/VeXplore/TopicDetailModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TopicDetailModel.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class TopicDetailModel: NSObject
11 | {
12 | private(set) var avatar: String?
13 | private(set) var nodeName: String?
14 | private(set) var nodeId: String?
15 | private(set) var username: String?
16 | private(set) var topicTitle: String?
17 | private(set) var topicContent: String!
18 | private(set) var date: String?
19 | private(set) var favoriteNum: String?
20 | private(set) var token: String?
21 | private(set) var commentTotalPages = 1
22 | private(set) var isFavorite = false
23 | private(set) var topicCommentTotalCount: String?
24 | private(set) var reportUrl: String?
25 | private(set) var topicId: String?
26 |
27 | init(id: String, rootNode: HTMLNode)
28 | {
29 | self.topicId = id
30 | if let node = rootNode.xPath("./div[1]/a[2]").first
31 | {
32 | nodeName = node.content
33 | if var href = node["href"],
34 | let range = href.range(of: "/go/")
35 | {
36 | href.replaceSubrange(range, with: SharedR.String.Empty)
37 | nodeId = href
38 | }
39 | }
40 | avatar = rootNode.xPath(".//img[@class='avatar']").first?["src"]
41 | username = rootNode.xPath(".//small[contains(text(),'By')]/a").first?.content
42 | topicTitle = rootNode.xPath(".//h1").first?.content
43 | topicContent = rootNode.xPath("./div[@class='cell']/div").first?.rawContent ?? SharedR.String.Empty
44 |
45 | // Append
46 | let appendNodes = rootNode.xPath("./div[@class='subtle']")
47 | for node in appendNodes
48 | {
49 | if let content = node.rawContent
50 | {
51 | topicContent = topicContent + content
52 | }
53 | }
54 | date = rootNode.xPath("./div[1]/small/text()").last?.content
55 | favoriteNum = rootNode.xPath(".//div[@class='inner']/div/span").first?.content?.stringByRemovingNewLinesAndWhitespace()
56 | if rootNode.xPath(".//a[text()='取消收藏']").count > 0
57 | {
58 | isFavorite = true
59 | }
60 | if let token = rootNode.xPath(".//a[@class='op'][1][text()='加入收藏' or text()='取消收藏']").first?["href"]
61 | {
62 | let array = token.components(separatedBy: "?t=")
63 | if array.count == 2
64 | {
65 | self.token = array[1]
66 | }
67 | }
68 | if let topicCommentTotalCountText = rootNode.xPath("//div[@class='box']/div[@class='cell']/span").first?.content,
69 | let range = topicCommentTotalCountText.range(of: " 回复")
70 | {
71 | topicCommentTotalCount = String(topicCommentTotalCountText[.. JSON {
81 | var json = JSON.null
82 | if type == .dictionary, let value = rawDictionary[key]
83 | {
84 | json = JSON(object: value)
85 | }
86 | return json
87 | }
88 |
89 | }
90 |
91 | // using 'for...in' to access
92 | extension JSON: Swift.Sequence
93 | {
94 | public func makeIterator() -> JSONIterator
95 | {
96 | return JSON.Iterator(json: self)
97 | }
98 |
99 | }
100 |
101 | public struct JSONIterator: IteratorProtocol
102 | {
103 | private var type: Type
104 | private var arrayIterator: IndexingIterator<[Any]>?
105 | private var arrayIndex = 0
106 |
107 | init(json: JSON)
108 | {
109 | type = json.type
110 | if type == .array
111 | {
112 | arrayIterator = json.rawArray.makeIterator()
113 | }
114 | }
115 |
116 | mutating public func next() -> (String, JSON)?
117 | {
118 | if type == .array, let next = arrayIterator?.next()
119 | {
120 | let i = arrayIndex
121 | arrayIndex += 1
122 | return (String(i), JSON(object: next))
123 | }
124 | return nil
125 | }
126 |
127 | }
128 |
129 |
--------------------------------------------------------------------------------
/SharedKit/NetworkingUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkingUtils.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | public enum HTTPMethod: String
11 | {
12 | case get = "GET"
13 | case post = "POST"
14 | }
15 |
16 |
17 | public extension String
18 | {
19 | func toURL() throws -> URL
20 | {
21 | guard let url = URL(string: self) else {
22 | throw NSError()
23 | }
24 | return url
25 | }
26 | }
27 |
28 |
29 | extension URLRequest
30 | {
31 | init(url: String, method: HTTPMethod, headers: [String: String]? = nil) throws
32 | {
33 | let url = try url.toURL()
34 | self.init(url: url)
35 | httpMethod = method.rawValue
36 | if let headers = headers
37 | {
38 | for (field, value) in headers
39 | {
40 | setValue(value, forHTTPHeaderField: field)
41 | }
42 | }
43 | }
44 |
45 | func encode(with parameters: [String: String]?) throws -> URLRequest
46 | {
47 | guard let parameters = parameters, parameters.isEmpty == false else {
48 | return self
49 | }
50 |
51 | var urlRequest = self
52 | if urlRequest.httpMethod == "GET"
53 | {
54 | guard let url = urlRequest.url else {
55 | throw NSError()
56 | }
57 |
58 | if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)
59 | {
60 | let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
61 | urlComponents.percentEncodedQuery = percentEncodedQuery
62 | urlRequest.url = urlComponents.url
63 | }
64 | }
65 | else
66 | {
67 | if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil
68 | {
69 | urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
70 | }
71 | urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
72 | }
73 | return urlRequest
74 | }
75 |
76 | private func query(_ parameters: [String: String]) -> String
77 | {
78 | let escapedParameters = parameters.map { (escape($0.key), escape($0.value)) }
79 | let result = escapedParameters.map { "\($0)=\($1)" }.joined(separator: "&")
80 | return result
81 | }
82 |
83 | private func escape(_ string: String) -> String
84 | {
85 | var allowedCharacterSet = CharacterSet.urlQueryAllowed
86 | allowedCharacterSet.remove(charactersIn: "!*'();:@&=+$,#[]") // remove reserved characters
87 | return string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
88 | }
89 |
90 | }
91 |
92 |
93 | public struct Networking
94 | {
95 | @discardableResult
96 | public static func request(_ url: String,
97 | method: HTTPMethod = .get,
98 | parameters: [String: String]? = nil,
99 | headers: [String: String]? = nil) -> Request
100 | {
101 | return SessionManager.shared.request(url, method: method, parameters: parameters, headers: headers)
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/VeXplore/TopicSearchResultCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TopicSearchResultCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class TopicSearchResultCell: BaseTableViewCell
11 | {
12 | lazy var cellTitleLabel: UILabel = {
13 | let label = UILabel()
14 | label.translatesAutoresizingMaskIntoConstraints = false
15 | label.font = SharedR.Font.Small
16 | label.textColor = .desc
17 | label.textAlignment = .center
18 |
19 | return label
20 | }()
21 |
22 | lazy var topicTitleLabel: UILabel = {
23 | let label = UILabel()
24 | label.translatesAutoresizingMaskIntoConstraints = false
25 | label.font = SharedR.Font.Medium
26 | label.numberOfLines = 0
27 | label.textColor = .body
28 |
29 | return label
30 | }()
31 |
32 | lazy var separatorLine: UIView = {
33 | let view = UIView()
34 | view.translatesAutoresizingMaskIntoConstraints = false
35 | view.backgroundColor = .border
36 |
37 | return view
38 | }()
39 |
40 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
41 | {
42 | super.init(style: style, reuseIdentifier: reuseIdentifier)
43 | self.commonInit()
44 | }
45 |
46 | private func commonInit()
47 | {
48 | contentView.addSubview(topicTitleLabel)
49 | contentView.addSubview(cellTitleLabel)
50 | contentView.addSubview(separatorLine)
51 | let bindings = [
52 | "topicTitleLabel": topicTitleLabel,
53 | "separatorLine": separatorLine,
54 | "cellTitleLabel": cellTitleLabel
55 | ]
56 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-8-[cellTitleLabel(40)]-8-[topicTitleLabel]-8-|", metrics: nil, views: bindings))
57 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-12-[topicTitleLabel]-12-|", metrics: nil, views: bindings))
58 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[separatorLine(0.5)]|", metrics: nil, views: bindings))
59 | cellTitleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
60 | separatorLine.leadingAnchor.constraint(equalTo: topicTitleLabel.leadingAnchor).isActive = true
61 | separatorLine.trailingAnchor.constraint(equalTo: topicTitleLabel.trailingAnchor).isActive = true
62 |
63 | preservesSuperviewLayoutMargins = false
64 | layoutMargins = .zero
65 | selectionStyle = .none
66 | }
67 |
68 | required init?(coder aDecoder: NSCoder)
69 | {
70 | fatalError("init(coder:) has not been implemented")
71 | }
72 |
73 | override func prepareForReuse()
74 | {
75 | super.prepareForReuse()
76 | cellTitleLabel.font = SharedR.Font.Small
77 | topicTitleLabel.font = SharedR.Font.Medium
78 | }
79 |
80 | override func layoutSubviews()
81 | {
82 | super.layoutSubviews()
83 | contentView.layoutSubviews()
84 | topicTitleLabel.preferredMaxLayoutWidth = topicTitleLabel.bounds.width
85 | }
86 |
87 | @objc
88 | override func refreshColorScheme()
89 | {
90 | super.refreshColorScheme()
91 | cellTitleLabel.textColor = .desc
92 | topicTitleLabel.textColor = .body
93 | separatorLine.backgroundColor = .border
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/VeXplore/URLAnalyzer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLAnalyzer.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SafariServices
9 | import MessageUI
10 | import SharedKit
11 |
12 | struct URLAnalyzer
13 | {
14 | @discardableResult
15 | static func Analyze(url:String, handleViewController: SwipeTableViewController) -> Bool
16 | {
17 | let result = URLAnalysisResult(url: url)
18 | dispatch_async_safely_to_main_queue {
19 | switch result.type
20 | {
21 | case .url:
22 | if let urlString = result.value, let url = URL(string: urlString)
23 | {
24 | // open in safari
25 | let safariVC = SFSafariViewController(url: url, entersReaderIfAvailable: true)
26 | handleViewController.present(safariVC, animated: true, completion: nil)
27 | }
28 | case .member:
29 | if let username = result.value
30 | {
31 | let profileVC = OtherProfileViewController()
32 | profileVC.username = username
33 | handleViewController.bouncePresent(viewController: profileVC, completion: nil)
34 | }
35 | case .topic:
36 | if let topicId = result.value
37 | {
38 | let topicVC = TopicViewController(topicId: topicId)
39 | handleViewController.bouncePresent(navigationVCWith: topicVC, completion: nil)
40 | }
41 | case .node:
42 | if let nodeId = result.value
43 | {
44 | let nodeTopicListVC = NodeTopicListViewController()
45 | nodeTopicListVC.nodeId = nodeId
46 | handleViewController.bouncePresent(navigationVCWith: nodeTopicListVC, completion: {
47 | nodeTopicListVC.startLoading()
48 | })
49 | }
50 | case .email:
51 | if let recipient = result.value
52 | {
53 | if MFMailComposeViewController.canSendMail()
54 | {
55 | let mailCompose = MFMailComposeViewController()
56 | mailCompose.mailComposeDelegate = handleViewController
57 | mailCompose.setToRecipients([recipient])
58 | dispatch_async_safely_to_main_queue {
59 | handleViewController.present(mailCompose, animated: true, completion: nil)
60 | }
61 | }
62 | else
63 | {
64 | let alertController = UIAlertController(title: nil, message: R.String.EmailNotSetAlert, preferredStyle: .alert)
65 | let copyAction = UIAlertAction(title: R.String.Confirm, style: .default) { (action) in
66 | UIPasteboard.general.string = recipient
67 | }
68 | alertController.addAction(copyAction)
69 | dispatch_async_safely_to_main_queue {
70 | handleViewController.present(alertController, animated: true, completion: nil)
71 | }
72 | }
73 | }
74 | case .undefined:
75 | break
76 | }
77 | }
78 | if result.type == .undefined
79 | {
80 | return false
81 | }
82 | return true
83 | }
84 |
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/VeXplore/PersonalInfoCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PersonalInfoCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class PersonalInfoCell: BaseTableViewCell
11 | {
12 | lazy var iconImageView: UIImageView = {
13 | let view = UIImageView()
14 | view.translatesAutoresizingMaskIntoConstraints = false
15 | view.contentMode = .scaleAspectFit
16 |
17 | return view
18 | }()
19 |
20 | lazy var contentLabel: UILabel = {
21 | let label = UILabel()
22 | label.translatesAutoresizingMaskIntoConstraints = false
23 | label.font = SharedR.Font.Medium
24 | label.textColor = .desc
25 |
26 | return label
27 | }()
28 |
29 | private lazy var line: UIView = {
30 | let view = UIView()
31 | view.translatesAutoresizingMaskIntoConstraints = false
32 | view.backgroundColor = .border
33 |
34 | return view
35 | }()
36 |
37 | lazy var longLine: UIView = {
38 | let view = UIView()
39 | view.translatesAutoresizingMaskIntoConstraints = false
40 | view.backgroundColor = .border
41 | view.isHidden = true
42 |
43 | return view
44 | }()
45 |
46 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
47 | {
48 | super.init(style: style, reuseIdentifier: reuseIdentifier)
49 |
50 | contentView.addSubview(iconImageView)
51 | contentView.addSubview(contentLabel)
52 | contentView.addSubview(line)
53 | contentView.addSubview(longLine)
54 | let bindings = [
55 | "iconImageView": iconImageView,
56 | "contentLabel": contentLabel,
57 | "line": line,
58 | "longLine": longLine
59 | ]
60 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-12-[iconImageView(20)]-12-[contentLabel]-12-|", metrics: nil, views: bindings))
61 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[longLine]|", metrics: nil, views: bindings))
62 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[contentLabel]-8-|", metrics: nil, views: bindings))
63 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[line(0.5)]|", metrics: nil, views: bindings))
64 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[longLine(0.5)]|", metrics: nil, views: bindings))
65 | iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
66 | iconImageView.heightAnchor.constraint(equalTo: iconImageView.widthAnchor).isActive = true
67 | line.leadingAnchor.constraint(equalTo: contentLabel.leadingAnchor).isActive = true
68 | line.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
69 |
70 | preservesSuperviewLayoutMargins = false
71 | layoutMargins = .zero
72 | selectionStyle = .none
73 | }
74 |
75 | required init?(coder aDecoder: NSCoder)
76 | {
77 | fatalError("init(coder:) has not been implemented")
78 | }
79 |
80 | override func prepareForReuse()
81 | {
82 | super.prepareForReuse()
83 | line.isHidden = false
84 | longLine.isHidden = true
85 | contentLabel.font = SharedR.Font.Medium
86 | }
87 |
88 | @objc
89 | override func refreshColorScheme()
90 | {
91 | super.refreshColorScheme()
92 | contentLabel.textColor = .desc
93 | line.backgroundColor = .border
94 | longLine.backgroundColor = .border
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/VeXplore/TextLine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextLine.swift
3 | // YYText
4 | //
5 | // Copyright © 2016 ibireme. All rights reserved.
6 | //
7 |
8 | import Foundation
9 |
10 | class TextLine
11 | {
12 | private(set) var ctLine: CTLine!
13 | private(set) var bounds: CGRect!
14 | private(set) var ascent: CGFloat = 0.0
15 | private(set) var descent: CGFloat = 0.0
16 | private(set) var leading: CGFloat = 0.0
17 | private(set) var lineWidth: CGFloat = 0.0
18 | private(set) var attachments = [UIImageView]()
19 | private(set) var attachmentRects = [CGRect]()
20 | private var firstGlyphPosX: CGFloat = 0
21 |
22 | // baseline position
23 | var position: CGPoint! {
24 | didSet
25 | {
26 | reloadBounds()
27 | }
28 | }
29 |
30 | var size: CGSize {
31 | return bounds.size
32 | }
33 |
34 | var width: CGFloat {
35 | return bounds.width
36 | }
37 |
38 | var height: CGFloat {
39 | return bounds.height
40 | }
41 |
42 | var top: CGFloat {
43 | return bounds.minY
44 | }
45 |
46 | var bottom: CGFloat {
47 | return bounds.maxY
48 | }
49 |
50 | var left: CGFloat {
51 | return bounds.minX
52 | }
53 |
54 | var right: CGFloat {
55 | return bounds.maxX
56 | }
57 |
58 | required init(ctLine: CTLine, position: CGPoint)
59 | {
60 | self.ctLine = ctLine
61 | self.position = position
62 | commonInit()
63 | }
64 |
65 | private func commonInit()
66 | {
67 | lineWidth = CGFloat(CTLineGetTypographicBounds(ctLine, &ascent, &descent, &leading));
68 | if CTLineGetGlyphCount(ctLine) > 0
69 | {
70 | let runs = CTLineGetGlyphRuns(ctLine) as! [CTRun]
71 | let run = runs[0]
72 | var pos: CGPoint = .zero
73 | CTRunGetPositions(run, CFRangeMake(0, 1), &pos)
74 | firstGlyphPosX = pos.x
75 | reloadBounds()
76 | }
77 | }
78 |
79 | private func reloadBounds()
80 | {
81 | bounds = CGRect(x: position.x + firstGlyphPosX, y: position.y - ascent, width: lineWidth, height: ascent + descent)
82 | let runs = CTLineGetGlyphRuns(ctLine)
83 | let runsCount = CFArrayGetCount(runs)
84 | guard ctLine != nil && runsCount > 0 else {
85 | return
86 | }
87 |
88 | attachments.removeAll()
89 | attachmentRects.removeAll()
90 | for i in 0.. 0 else {
94 | continue
95 | }
96 |
97 | let attrs = CTRunGetAttributes(run) as NSDictionary
98 | if let attachment = attrs[AttachmentAttributeName] as? UIImageView
99 | {
100 | var runPos: CGPoint = .zero
101 | CTRunGetPositions(run, CFRangeMake(0, 1), &runPos)
102 | var ascent: CGFloat = 0.0
103 | var descent: CGFloat = 0.0
104 | let runWidth: CGFloat = CGFloat(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, nil))
105 | runPos.x = position.x + runPos.x
106 | runPos.y = position.y - runPos.y
107 | let runTypoBounds = CGRect(x: runPos.x, y: runPos.y - ascent, width: runWidth, height: ascent + descent)
108 | attachments.append(attachment)
109 | attachmentRects.append(runTypoBounds)
110 | }
111 | }
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/VeXplore/MyFollowingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyFollowingsViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class MyFollowingsViewController: BaseCenterLoadingViewController
10 | {
11 | private var followingList = [(String, String)]() // (url, username)
12 | override func viewDidLoad()
13 | {
14 | super.viewDidLoad()
15 | title = R.String.MyFollowings
16 |
17 | tableView.register(MyFollowingCell.self, forCellReuseIdentifier: String(describing: MyFollowingCell.self))
18 | tableView.isHidden = false
19 | tableView.estimatedRowHeight = R.Constant.EstimatedRowHeight
20 | tableView.rowHeight = UITableViewAutomaticDimension
21 | let closeBtn = UIBarButtonItem(image: R.Image.Close, style: .plain, target: self, action: #selector(closeBtnTapped))
22 | navigationItem.leftBarButtonItem = closeBtn
23 | }
24 |
25 | override func loadingRequest()
26 | {
27 | V2Request.Profile.getFollowings(completion: { [weak self] (response) in
28 | guard let weakSelf = self else {
29 | return
30 | }
31 |
32 | weakSelf.stopLoading(withSuccesse: response.success, completion: { (success) in
33 | if response.message.count > 0 && response.message[0] == R.String.NotAuthorizedError
34 | {
35 | User.shared.logout()
36 | }
37 | else if success, let value = response.value
38 | {
39 | weakSelf.followingList = value
40 | weakSelf.tableView.reloadData()
41 | weakSelf.centerLoadingView.removeFromSuperview()
42 | }
43 | })
44 | })
45 | }
46 |
47 | @objc
48 | private func closeBtnTapped()
49 | {
50 | dismiss(animated: true, completion: nil)
51 | }
52 |
53 | // MARK: - UITableViewDataSource
54 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
55 | {
56 | return followingList.count
57 | }
58 |
59 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
60 | {
61 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MyFollowingCell.self), for: indexPath) as! MyFollowingCell
62 | let followingMember = followingList[indexPath.row]
63 | cell.contentLabel.text = followingMember.1
64 | if let url = URL(string: R.String.Https + followingMember.0)
65 | {
66 | cell.avatarImageView.avatarImage(withURL: url)
67 | }
68 | return cell
69 | }
70 |
71 | // MARK: - UITableViewDelegate
72 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
73 | {
74 | let username = followingList[indexPath.row].1
75 | let profileVC = OtherProfileViewController()
76 | profileVC.username = username
77 | profileVC.unfollowingHandler = { [weak self] username -> Void in
78 | guard let weakSelf = self else{
79 | return
80 | }
81 | weakSelf.removeUser(withUsername: username)
82 | }
83 | DispatchQueue.main.async(execute: {
84 | self.bouncePresent(viewController: profileVC, completion: nil)
85 | })
86 | }
87 |
88 | func removeUser(withUsername username: String)
89 | {
90 | if let index = followingList.index(where: {$0.1 == username})
91 | {
92 | followingList.remove(at: index)
93 | tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
94 | }
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/VeXplore/FavoriteNodesViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FavoriteNodesViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class FavoriteNodesViewController: BaseCenterLoadingViewController
10 | {
11 | private var nodesList = [NodeModel]()
12 | var nodeToDelete: String?
13 |
14 | override func viewDidLoad()
15 | {
16 | super.viewDidLoad()
17 | title = R.String.MyFavoriteNodes
18 |
19 | tableView.register(MyFollowingCell.self, forCellReuseIdentifier: String(describing: MyFollowingCell.self))
20 | tableView.isHidden = false
21 | tableView.estimatedRowHeight = R.Constant.EstimatedRowHeight
22 | tableView.rowHeight = UITableViewAutomaticDimension
23 | let closeBtn = UIBarButtonItem(image: R.Image.Close, style: .plain, target: self, action: #selector(closeBtnTapped))
24 | navigationItem.leftBarButtonItem = closeBtn
25 | }
26 |
27 | override func viewDidAppear(_ animated: Bool)
28 | {
29 | super.viewDidAppear(animated)
30 | if let nodeToDelete = nodeToDelete, let index = nodesList.index(where: {$0.nodeId == nodeToDelete})
31 | {
32 | nodesList.remove(at: index)
33 | tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
34 | }
35 | }
36 |
37 | @objc
38 | private func closeBtnTapped()
39 | {
40 | dismiss(animated: true, completion: nil)
41 | }
42 |
43 | override func loadingRequest()
44 | {
45 | V2Request.Profile.getFavoriteNodes(completion: { [weak self] (response) in
46 | guard let weakSelf = self else {
47 | return
48 | }
49 |
50 | weakSelf.stopLoading(withSuccesse: response.success, completion: { (success) in
51 | if response.message.count > 0 && response.message[0] == R.String.NotAuthorizedError
52 | {
53 | User.shared.logout()
54 | }
55 | else if success, let value = response.value
56 | {
57 | weakSelf.nodesList = value
58 | weakSelf.tableView.reloadData()
59 | weakSelf.centerLoadingView.removeFromSuperview()
60 | }
61 | })
62 | })
63 | }
64 |
65 | // MARK: - UITableViewDataSource
66 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
67 | {
68 | return nodesList.count
69 | }
70 |
71 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
72 | {
73 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: MyFollowingCell.self), for: indexPath) as! MyFollowingCell
74 | let followingMember = nodesList[indexPath.row]
75 | cell.contentLabel.text = followingMember.nodeName
76 | if let avatar = followingMember.avatar, let url = URL(string: R.String.Https + avatar)
77 | {
78 | cell.avatarImageView.avatarImage(withURL: url)
79 | }
80 | return cell
81 | }
82 |
83 | // MARK: - UITableViewDelegate
84 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
85 | {
86 | let nodeModel = nodesList[indexPath.row]
87 | let nodeTopicListVC = NodeTopicListViewController()
88 | nodeTopicListVC.node = nodeModel
89 | nodeTopicListVC.title = nodeModel.nodeName
90 | nodeTopicListVC.favoriteNodesVC = self
91 | DispatchQueue.main.async {
92 | self.bouncePresent(navigationVCWith: nodeTopicListVC, completion: {
93 | nodeTopicListVC.startLoading()
94 | })
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/VeXplore/TopicReplyingViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TopicReplyingViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SafariServices
9 | import StoreKit
10 |
11 | class TopicReplyingViewController: InputViewController
12 | {
13 | var atSomeone: String? {
14 | set
15 | {
16 | if let atSomeone = newValue
17 | {
18 | inputContainerView.contentTextView.text.append(atSomeone)
19 | inputContainerView.isPostEnabled = true
20 | }
21 | }
22 | get
23 | {
24 | fatalError("You cannot read from this object.")
25 | }
26 | }
27 |
28 | var topicId: String?
29 |
30 | override func viewDidLoad()
31 | {
32 | super.viewDidLoad()
33 | inputContainerView.titleLabel.text = R.String.Reply
34 | inputContainerView.imageBtn.isEnabled = true
35 | }
36 |
37 | override func viewWillAppear(_ animated: Bool)
38 | {
39 | super.viewWillAppear(animated)
40 | inputContainerView.contentTextView.becomeFirstResponder()
41 | }
42 |
43 | override func resetTextViews()
44 | {
45 | super.resetTextViews()
46 | inputContainerView.contentTextView.placeholderText = R.String.ReplyPlaceholder
47 | inputContainerView.contentTextView.placeholderTextColor = .border
48 | }
49 |
50 | func textViewDidChange(_ textView: UITextView)
51 | {
52 | if textView == inputContainerView.contentTextView
53 | {
54 | inputContainerView.isPostEnabled = (textView.text.isEmpty == false)
55 | }
56 | }
57 |
58 | override func closeBtnTapped()
59 | {
60 | inputContainerView.contentTextView.resignFirstResponder()
61 | super.closeBtnTapped()
62 | }
63 |
64 | override func postBtnTapped()
65 | {
66 | if inputContainerView.contentTextView.text.isEmpty == false, let topicId = topicId
67 | {
68 | backgroudView.isHidden = false
69 | loadingCancelBtn.isHidden = false
70 | centerLoadingView.isHidden = false
71 | centerLoadingView.initSquaresNormalPostion()
72 | centerLoadingView.beginLoading()
73 | V2Request.Topic.reply(withTopicId: topicId, content: inputContainerView.contentTextView.text) { [weak self] (response) in
74 | guard let weakSelf = self else {
75 | return
76 | }
77 |
78 | weakSelf.centerLoadingView.stopLoading(withSuccess: response.success, completion: { (success) in
79 | if success
80 | {
81 | NotificationCenter.default.post(name: NSNotification.Name.Topic.CommentAdded, object: nil)
82 | NotificationCenter.default.post(name: NSNotification.Name.Profile.NeedRefresh, object: nil)
83 | ModalTransitioningDelegate.shared.reverseDirection = true
84 | weakSelf.closeBtnTapped()
85 | if #available(iOS 10.3, *)
86 | {
87 | SKStoreReviewController.requestReview()
88 | }
89 | }
90 | })
91 | }
92 | }
93 | }
94 |
95 | func safariViewControllerDidFinish(_ controller: SFSafariViewController)
96 | {
97 | if let newCopyString = UIPasteboard.general.string, newCopyString != copyedString, newCopyString.isValidImgUrl()
98 | {
99 | inputContainerView.contentTextView.text.append(newCopyString)
100 | inputContainerView.isPostEnabled = true
101 | }
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/VeXplore/RecentListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecentListViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class RecentListViewController: SwipeTableViewController
10 | {
11 | var page = 0
12 |
13 | override func viewDidLoad()
14 | {
15 | super.viewDidLoad()
16 | tableView.register(TopicCell.self, forCellReuseIdentifier: String(describing: TopicCell.self))
17 | tableView.estimatedRowHeight = R.Constant.EstimatedRowHeight
18 | }
19 |
20 | // MARK: - Loading request
21 | override func topLoadingRequest()
22 | {
23 | request = V2Request.Topic.getRecentList(withPage: page) { [weak self] (response) in
24 | guard let weakSelf = self else {
25 | return
26 | }
27 |
28 | weakSelf.stopLoading(withLoadingStyle: .top, success: response.success, completion: { (success) -> Void in
29 | if success, let value = response.value
30 | {
31 | weakSelf.topicList = value
32 | weakSelf.tableView.reloadData()
33 | UIView.animate(withDuration: R.Constant.InsetAnimationDuration, delay: 0, options: .beginFromCurrentState, animations: {
34 | weakSelf.tableView.contentInset = .zero
35 | }, completion: nil)
36 | weakSelf.isTopLoadingFail = false
37 | weakSelf.enableTopLoading = false
38 | weakSelf.tableView.tableHeaderView = nil
39 | }
40 | else
41 | {
42 | weakSelf.isTopLoadingFail = true
43 | }
44 | weakSelf.isTopLoading = false
45 | })
46 | }
47 | }
48 |
49 | // MARK: - UITableViewDataSource
50 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
51 | {
52 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TopicCell.self), for: indexPath) as! TopicCell
53 | let topicItem = topicList[indexPath.row]
54 | cell.topicItemModel = topicItem
55 | cell.topicTitleLabel.text = topicItem.topicTitle
56 | cell.userNameLabel.text = topicItem.username
57 | cell.nodeNameBtn.setTitle(topicItem.nodeName, for: .normal)
58 | if topicItem.repliesNumber != nil
59 | {
60 | cell.repliesNumberLabel.text = topicItem.repliesNumber
61 | }
62 | if let avatar = topicItem.avatar, let url = URL(string: R.String.Https + avatar)
63 | {
64 | cell.avatarImageView.avatarImage(withURL: url)
65 | }
66 | if let lastReplyDate = topicItem.lastReplyDate
67 | {
68 | if topicItem.lastReplyUserName != nil
69 | {
70 | cell.lastReplayDateAndUserLabel.text = lastReplyDate
71 | }
72 | else
73 | {
74 | cell.lastReplayDateAndUserLabel.text = String(format: R.String.PublicDate, lastReplyDate)
75 | }
76 | }
77 | cell.delegate = self
78 | return cell
79 | }
80 |
81 | // MARK: - UITableViewDelegate
82 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
83 | {
84 | if let topicId = topicList[indexPath.row].topicId
85 | {
86 | let topicVC = TopicViewController(topicId: topicId)
87 | topicVC.ignoreHandler = { [weak self] topicId -> Void in
88 | guard let weakSelf = self else {
89 | return
90 | }
91 | weakSelf.removeTopic(withId: topicId)
92 | }
93 | DispatchQueue.main.async(execute: {
94 | self.bouncePresent(navigationVCWith: topicVC, completion: nil)
95 | })
96 | }
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/VeXplore/libxml/HTMLtree.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Summary: specific APIs to process HTML tree, especially serialization
3 | * Description: this module implements a few function needed to process
4 | * tree in an HTML specific way.
5 | *
6 | * Copy: See Copyright for the status of this software.
7 | *
8 | * Author: Daniel Veillard
9 | */
10 |
11 | #ifndef __HTML_TREE_H__
12 | #define __HTML_TREE_H__
13 |
14 | #include
15 | #include "tree.h"
16 | #include "HTMLparser.h"
17 |
18 |
19 | #ifdef __cplusplus
20 | extern "C" {
21 | #endif
22 |
23 |
24 | /**
25 | * HTML_TEXT_NODE:
26 | *
27 | * Macro. A text node in a HTML document is really implemented
28 | * the same way as a text node in an XML document.
29 | */
30 | #define HTML_TEXT_NODE XML_TEXT_NODE
31 | /**
32 | * HTML_ENTITY_REF_NODE:
33 | *
34 | * Macro. An entity reference in a HTML document is really implemented
35 | * the same way as an entity reference in an XML document.
36 | */
37 | #define HTML_ENTITY_REF_NODE XML_ENTITY_REF_NODE
38 | /**
39 | * HTML_COMMENT_NODE:
40 | *
41 | * Macro. A comment in a HTML document is really implemented
42 | * the same way as a comment in an XML document.
43 | */
44 | #define HTML_COMMENT_NODE XML_COMMENT_NODE
45 | /**
46 | * HTML_PRESERVE_NODE:
47 | *
48 | * Macro. A preserved node in a HTML document is really implemented
49 | * the same way as a CDATA section in an XML document.
50 | */
51 | #define HTML_PRESERVE_NODE XML_CDATA_SECTION_NODE
52 | /**
53 | * HTML_PI_NODE:
54 | *
55 | * Macro. A processing instruction in a HTML document is really implemented
56 | * the same way as a processing instruction in an XML document.
57 | */
58 | #define HTML_PI_NODE XML_PI_NODE
59 |
60 | XMLPUBFUN htmlDocPtr XMLCALL
61 | htmlNewDoc (const xmlChar *URI,
62 | const xmlChar *ExternalID);
63 | XMLPUBFUN htmlDocPtr XMLCALL
64 | htmlNewDocNoDtD (const xmlChar *URI,
65 | const xmlChar *ExternalID);
66 | XMLPUBFUN const xmlChar * XMLCALL
67 | htmlGetMetaEncoding (htmlDocPtr doc);
68 | XMLPUBFUN int XMLCALL
69 | htmlSetMetaEncoding (htmlDocPtr doc,
70 | const xmlChar *encoding);
71 | XMLPUBFUN void XMLCALL
72 | htmlDocDumpMemory (xmlDocPtr cur,
73 | xmlChar **mem,
74 | int *size);
75 | XMLPUBFUN void XMLCALL
76 | htmlDocDumpMemoryFormat (xmlDocPtr cur,
77 | xmlChar **mem,
78 | int *size,
79 | int format);
80 | XMLPUBFUN int XMLCALL
81 | htmlDocDump (FILE *f,
82 | xmlDocPtr cur);
83 | XMLPUBFUN int XMLCALL
84 | htmlSaveFile (const char *filename,
85 | xmlDocPtr cur);
86 | XMLPUBFUN int XMLCALL
87 | htmlNodeDump (xmlBufferPtr buf,
88 | xmlDocPtr doc,
89 | xmlNodePtr cur);
90 | XMLPUBFUN void XMLCALL
91 | htmlNodeDumpFile (FILE *out,
92 | xmlDocPtr doc,
93 | xmlNodePtr cur);
94 | XMLPUBFUN int XMLCALL
95 | htmlNodeDumpFileFormat (FILE *out,
96 | xmlDocPtr doc,
97 | xmlNodePtr cur,
98 | const char *encoding,
99 | int format);
100 | XMLPUBFUN int XMLCALL
101 | htmlSaveFileEnc (const char *filename,
102 | xmlDocPtr cur,
103 | const char *encoding);
104 | XMLPUBFUN int XMLCALL
105 | htmlSaveFileFormat (const char *filename,
106 | xmlDocPtr cur,
107 | const char *encoding,
108 | int format);
109 |
110 | XMLPUBFUN void XMLCALL
111 | htmlNodeDumpFormatOutput(xmlOutputBufferPtr buf,
112 | xmlDocPtr doc,
113 | xmlNodePtr cur,
114 | const char *encoding,
115 | int format);
116 | XMLPUBFUN void XMLCALL
117 | htmlDocContentDumpOutput(xmlOutputBufferPtr buf,
118 | xmlDocPtr cur,
119 | const char *encoding);
120 | XMLPUBFUN void XMLCALL
121 | htmlDocContentDumpFormatOutput(xmlOutputBufferPtr buf,
122 | xmlDocPtr cur,
123 | const char *encoding,
124 | int format);
125 | XMLPUBFUN void XMLCALL
126 | htmlNodeDumpOutput (xmlOutputBufferPtr buf,
127 | xmlDocPtr doc,
128 | xmlNodePtr cur,
129 | const char *encoding);
130 |
131 |
132 | XMLPUBFUN int XMLCALL
133 | htmlIsBooleanAttr (const xmlChar *name);
134 |
135 |
136 | #ifdef __cplusplus
137 | }
138 | #endif
139 |
140 |
141 | #endif /* __HTML_TREE_H__ */
142 |
143 |
--------------------------------------------------------------------------------
/VeXplore/SearchViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 | import SharedKit
9 |
10 | class SearchViewController: BaseViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate
11 | {
12 | lazy var searchBox: SearchBoxView = {
13 | let view = SearchBoxView()
14 | view.translatesAutoresizingMaskIntoConstraints = false
15 | view.searchField.delegate = self
16 | view.searchField.addTarget(self, action: #selector(searchFieldDidChange(_:)), for: .editingChanged)
17 |
18 | return view
19 | }()
20 |
21 | private lazy var line: UIView = {
22 | let view = UIView()
23 | view.translatesAutoresizingMaskIntoConstraints = false
24 | view.backgroundColor = .border
25 |
26 | return view
27 | }()
28 |
29 | lazy var tableView: UITableView = {
30 | let tableView = UITableView(frame: CGRect.zero, style: .plain)
31 | tableView.translatesAutoresizingMaskIntoConstraints = false
32 | tableView.dataSource = self
33 | tableView.delegate = self
34 | tableView.separatorStyle = .none
35 | tableView.backgroundColor = .background
36 | tableView.sectionIndexBackgroundColor = .background
37 | tableView.sectionIndexColor = .href
38 | tableView.register(SectionHeaderView.self, forHeaderFooterViewReuseIdentifier: String(describing: SectionHeaderView.self))
39 |
40 | return tableView
41 | }()
42 |
43 | override func viewDidLoad()
44 | {
45 | super.viewDidLoad()
46 |
47 | view.addSubview(searchBox)
48 | view.addSubview(line)
49 | view.addSubview(tableView)
50 | let bindings = [
51 | "searchBox": searchBox,
52 | "line": line,
53 | "tableView": tableView
54 | ]
55 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-12-[searchBox]-12-|", metrics: nil, views: bindings))
56 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[line]|", metrics: nil, views: bindings))
57 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[tableView]|", metrics: nil, views: bindings))
58 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-4-[searchBox]-4-[line(0.5)][tableView]|", metrics: nil, views: bindings))
59 | }
60 |
61 | @objc
62 | override func refreshColorScheme()
63 | {
64 | super.refreshColorScheme()
65 | line.backgroundColor = .border
66 | tableView.backgroundColor = .background
67 | tableView.sectionIndexBackgroundColor = .background
68 | tableView.sectionIndexColor = .href
69 | view.backgroundColor = .subBackground
70 | }
71 |
72 | @objc
73 | override func handleContentSizeCategoryDidChanged()
74 | {
75 | super.handleContentSizeCategoryDidChanged()
76 | searchBox.searchField.font = SharedR.Font.Small
77 | }
78 |
79 | // override this method in subclass
80 | @objc
81 | func searchFieldDidChange(_ textField: UITextField) {}
82 |
83 | // MARK: - UITableViewDataSource
84 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
85 | {
86 | return 0
87 | }
88 |
89 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
90 | {
91 | let cell = UITableViewCell()
92 | return cell
93 | }
94 |
95 | // MARK: - UITableViewDelegate
96 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
97 | {
98 | return UITableViewAutomaticDimension
99 | }
100 |
101 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
102 | {
103 | // override this method in subclass
104 | }
105 |
106 | func scrollViewDidScroll(_ scrollView: UIScrollView)
107 | {
108 | // override this method in subclass
109 | }
110 |
111 |
112 | func scrollViewWillBeginDragging(_ scrollView: UIScrollView)
113 | {
114 | searchBox.searchField.resignFirstResponder()
115 | }
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/VeXplore/MyFavoriteCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyFavoriteCell.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | protocol MyFavoriteCellDelegate: class
10 | {
11 | func favoriteTopicsTapped()
12 | func favoriteNodesTapped()
13 | func myFollowingsTapped()
14 | }
15 |
16 | class MyFavoriteCell: BaseTableViewCell
17 | {
18 | lazy var nodesView: ProfileActionView = {
19 | let view = ProfileActionView()
20 | view.translatesAutoresizingMaskIntoConstraints = false
21 | view.textLabel.text = R.String.FavoriteNodes
22 | view.numLabel.text = R.String.Zero
23 | view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(nodesViewTapped)))
24 |
25 | return view
26 | }()
27 |
28 | lazy var topicsView: ProfileActionView = {
29 | let view = ProfileActionView()
30 | view.translatesAutoresizingMaskIntoConstraints = false
31 | view.textLabel.text = R.String.FavoriteTopics
32 | view.numLabel.text = R.String.Zero
33 | view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(topicsViewTapped)))
34 |
35 | return view
36 | }()
37 |
38 | lazy var followingView: ProfileActionView = {
39 | let view = ProfileActionView()
40 | view.translatesAutoresizingMaskIntoConstraints = false
41 | view.verticalLine.isHidden = true
42 | view.textLabel.text = R.String.Followings
43 | view.numLabel.text = R.String.Zero
44 | view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(followingsViewTapped)))
45 |
46 | return view
47 | }()
48 |
49 | private lazy var bottomLine: UIView = {
50 | let view = UIView()
51 | view.translatesAutoresizingMaskIntoConstraints = false
52 | view.backgroundColor = .border
53 |
54 | return view
55 | }()
56 |
57 | weak var delegate: MyFavoriteCellDelegate?
58 |
59 | override init(style: UITableViewCellStyle, reuseIdentifier: String?)
60 | {
61 | super.init(style: style, reuseIdentifier: reuseIdentifier)
62 |
63 | contentView.addSubview(nodesView)
64 | contentView.addSubview(topicsView)
65 | contentView.addSubview(followingView)
66 | contentView.addSubview(bottomLine)
67 | let bindings = [
68 | "nodesView": nodesView,
69 | "topicsView": topicsView,
70 | "followingView": followingView,
71 | "bottomLine": bottomLine
72 | ]
73 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[nodesView][topicsView][followingView]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: bindings))
74 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[bottomLine]|", metrics: nil, views: bindings))
75 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-8-[nodesView]-8-|", metrics: nil, views: bindings))
76 | contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[bottomLine(0.5)]|", metrics: nil, views: bindings))
77 | nodesView.widthAnchor.constraint(equalTo: followingView.widthAnchor).isActive = true
78 | topicsView.widthAnchor.constraint(equalTo: followingView.widthAnchor).isActive = true
79 |
80 | selectionStyle = .none
81 | }
82 |
83 | required init?(coder aDecoder: NSCoder)
84 | {
85 | super.init(coder: aDecoder)
86 | }
87 |
88 | override func prepareForReuse()
89 | {
90 | super.prepareForReuse()
91 | nodesView.prepareForReuse()
92 | topicsView.prepareForReuse()
93 | followingView.prepareForReuse()
94 | delegate = nil
95 | }
96 |
97 | @objc
98 | override func refreshColorScheme()
99 | {
100 | super.refreshColorScheme()
101 | bottomLine.backgroundColor = .border
102 | }
103 |
104 | // MARK: - Actions
105 | @objc
106 | private func nodesViewTapped()
107 | {
108 | delegate?.favoriteNodesTapped()
109 | }
110 |
111 | @objc
112 | private func topicsViewTapped()
113 | {
114 | delegate?.favoriteTopicsTapped()
115 | }
116 |
117 | @objc
118 | private func followingsViewTapped()
119 | {
120 | delegate?.myFollowingsTapped()
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/TodayExtension/TodayViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TodayViewController.swift
3 | // TodayExtension
4 | //
5 | // Copyright © 2017 Jimmy. All rights reserved.
6 | //
7 |
8 | import NotificationCenter
9 | import SharedKit
10 |
11 | // Due to Swift module, NSExtensionPrincipalClass should be $(PRODUCT_NAME).TodayViewController
12 | // Or you can use TodayViewController as NSExtensionPrincipalClass, and rename the class expose to Objc by using:
13 | // @objc (TodayViewController)
14 | class TodayViewController: UIViewController, NCWidgetProviding, UITableViewDataSource, UITableViewDelegate
15 | {
16 | private let rowHeight: CGFloat = 37.0
17 |
18 | lazy private var tableView: UITableView = {
19 | let tableView = UITableView(frame: .zero, style: .plain)
20 | tableView.translatesAutoresizingMaskIntoConstraints = false
21 | tableView.dataSource = self
22 | tableView.delegate = self
23 | tableView.rowHeight = self.rowHeight
24 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: String(describing: UITableViewCell.self))
25 |
26 | return tableView
27 | }()
28 |
29 | private var data = [TopicItem]()
30 |
31 | override func viewDidLoad()
32 | {
33 | super.viewDidLoad()
34 | extensionContext?.widgetLargestAvailableDisplayMode = .expanded
35 | view.addSubview(tableView)
36 | let bindings: [String : Any] = ["tableView": tableView]
37 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[tableView]|", metrics: nil, views: bindings))
38 | view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[tableView]|", metrics: nil, views: bindings))
39 | }
40 |
41 | override func viewDidAppear(_ animated: Bool)
42 | {
43 | super.viewDidAppear(animated)
44 | loadData()
45 | }
46 |
47 | func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void))
48 | {
49 | loadData()
50 | completionHandler(NCUpdateResult.newData)
51 | }
52 |
53 | func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize)
54 | {
55 | if activeDisplayMode == .expanded
56 | {
57 | preferredContentSize = CGSize(width: 0, height: rowHeight * CGFloat(data.count))
58 | }
59 | else
60 | {
61 | preferredContentSize = maxSize
62 | }
63 | }
64 |
65 | // MARK: - UITableViewDataSource
66 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
67 | {
68 | return data.count
69 | }
70 |
71 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
72 | {
73 | let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: UITableViewCell.self), for: indexPath)
74 | cell.textLabel?.font = UIFont.systemFont(ofSize: 14.0)
75 | cell.textLabel?.text = data[indexPath.row].title
76 | return cell
77 | }
78 |
79 | // MARK: - UITableViewDelegate
80 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
81 | {
82 | let topicURL = data[indexPath.row].url
83 | if let url = URL(string: "vexplore://?\(topicURL)")
84 | {
85 | extensionContext?.open(url, completionHandler: nil)
86 | }
87 | }
88 |
89 |
90 | private func loadData()
91 | {
92 | let url = "https://www.v2ex.com/api/topics/hot.json"
93 | var topics = [TopicItem]()
94 | Networking.request(url, headers: SharedR.Dict.MobileClientHeaders).responseJSON { (response) in
95 | if response.result.isSuccess, let value = response.result.value
96 | {
97 | let json = JSON(object: value)
98 | for (_, subJson) in json
99 | {
100 | if let topicURL = subJson["url"].string, let topicTitle = subJson["title"].string
101 | {
102 | let topicItem = TopicItem(url: topicURL, title: topicTitle)
103 | topics.append(topicItem)
104 | }
105 | }
106 | self.data = topics
107 | self.tableView.reloadData()
108 | }
109 | }
110 | }
111 |
112 | private struct TopicItem
113 | {
114 | let url: String
115 | let title: String
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/SharedKit/SharedR.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SharedR.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2017 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | public struct SharedR
10 | {
11 | struct Array
12 | {
13 | static let URLPatterns = [
14 | "^(http:\\/\\/|https:\\/\\/)?(www\\.)?(v2ex.com)?(/)?t/[0-9]+",
15 | "^(http:\\/\\/|https:\\/\\/)?(www\\.)?(v2ex.com)?(/)?member/[a-zA-Z0-9_]+$",
16 | "^(http:\\/\\/|https:\\/\\/)?(www\\.)?(v2ex.com)?(/)?go/[a-zA-Z0-9_]+$",
17 | "^mailto:.*@.*\\..*$",
18 | "^(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?"
19 | ]
20 |
21 | static let ValidImgUrls = [
22 | "imgur.com",
23 | "sinaimg.cn",
24 | "ooo.0o0.ooo"
25 | ]
26 | }
27 |
28 | public struct Dict
29 | {
30 | public static let MobileClientHeaders = ["user-agent": String.MobileUserAgent]
31 | public static let DesktopClientHeaders = ["user-agent": String.DesktopUserAgent]
32 | }
33 |
34 | struct Key
35 | {
36 | static let Username = "vexplore.userDefaults.key.username"
37 | static let ShowedTabs = "vexplore.userDefaults.key.showedTabs"
38 | static let HiddenTabs = "vexplore.userDefaults.key.hiddenTabs"
39 | static let CurrentTab = "vexplore.userDefaults.key.currentTab"
40 | static let EnableShake = "vexplore.userDefaults.key.enableShake"
41 | static let EnablePullReply = "vexplore.userDefaults.key.enablePullReply"
42 | static let EnableTabBarHidden = "vexplore.userDefaults.key.enableTabBarHidden"
43 | static let EnableShowReplyIndex = "vexplore.userDefaults.key.enableShowReplyIndex"
44 | static let EnableHighlightOwnerReplies = "vexplore.userDefaults.key.EnableHighlightOwnerReplies"
45 | static let DynamicTitleFontScale = "vexplore.userDefaults.key.dynamicTitleFontScale"
46 | static let AllNodesEtag = "vexplore.userDefaults.key.allNodesEtag"
47 | static let HomePageTopicList = "vexplore.topicList.homePage.key.%@"
48 | static let LastCacheVersion = "vexplore.userDefaults.key.lastCacheVersion"
49 | static let EnableNightMode = "vexplore.userDefaults.key.enableNightMode"
50 | static let AlwaysEnableNightMode = "vexplore.userDefaults.key.alwaysEnableNightMode"
51 | static let EnableSchedule = "vexplore.userDefaults.key.enableSchedule"
52 | static let ScheduleStartDate = "vexplore.userDefaults.key.scheduleStartDate"
53 | static let ScheduleEndDate = "vexplore.userDefaults.key.scheduleEndDate"
54 | static let BaiduAccessToken = "vexplore.userDefaults.key.baiduAccessToken"
55 | static let BaiduTokenExpiresDate = "vexplore.userDefaults.key.baiduTokenExpiresDate"
56 | }
57 |
58 | public struct String
59 | {
60 | public static let Empty = ""
61 | public static let V2EX = "V2EX"
62 | public static let ViewInApp = "打开"
63 | public static let InvalidTopic = "该页面不是V2EX帖子\n无法使用VeXplore查看🤷♀️"
64 | public static let ValidTopic = "该页面为V2EX帖子\n可以使用VeXplore查看👏"
65 | public static let MobileUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/600.1.3 (KHTML, like Gecko) Version/8.0 Mobile/12A4345d Safari/600.1.4"
66 | public static let DesktopUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36"
67 | }
68 |
69 | public static var Font: RFont { return RFont() }
70 | public struct RFont
71 | {
72 | fileprivate init(){}
73 | public var VeryLarge: UIFont { return UIFont.preferredFont(forTextStyle: .title3) }
74 | public var Large: UIFont { return UIFont.preferredFont(forTextStyle: .body) }
75 | public var Medium: UIFont { return UIFont.preferredFont(forTextStyle: .callout) } // topic title, comment
76 | public var Small: UIFont { return UIFont.preferredFont(forTextStyle: .footnote) }
77 | public var VerySmall: UIFont { return UIFont.preferredFont(forTextStyle: .caption1) }
78 | public var ExtraSmall: UIFont { return UIFont.preferredFont(forTextStyle: .caption2) } // date
79 |
80 | public var StaticMedium: UIFont { return UIFont.systemFont(ofSize: 14.0) }
81 | public var DynamicMedium: UIFont {
82 | let fontScaleString = UserDefaults.fontScaleString
83 | let fontScale = CGFloat(fontScaleString.doubleValue)
84 | let scaledFontSize = round(SharedR.Font.Medium.pointSize * fontScale)
85 | let font = SharedR.Font.Medium.withSize(scaledFontSize)
86 | return font
87 | }
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/VeXplore/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainViewController.swift
3 | // VeXplore
4 | //
5 | // Copyright © 2016 Jimmy. All rights reserved.
6 | //
7 |
8 |
9 | class MainViewController: UITabBarController, UITabBarControllerDelegate
10 | {
11 | static let shared = MainViewController()
12 | private let homeVC = HomePageViewController()
13 | private let nodesVC = NodesViewController()
14 | private let searchVC = SiteSearchViewController()
15 | private var notificationVC: NotificationViewController!
16 | private var profileVC: MyProfileViewController!
17 | private var notificationTabItem: UITabBarItem!
18 |
19 | private init()
20 | {
21 | super.init(nibName: nil, bundle: nil)
22 | }
23 |
24 | required init?(coder aDecoder: NSCoder)
25 | {
26 | fatalError("init(coder:) has not been implemented")
27 | }
28 |
29 | override func viewDidLoad()
30 | {
31 | super.viewDidLoad()
32 |
33 | buildUI()
34 | delegate = self
35 | }
36 |
37 | private func buildUI()
38 | {
39 | if User.shared.isLogin == true,
40 | let diskCachePath = cachePathString(withFilename: NotificationViewController.description()),
41 | let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: diskCachePath) as? Data,
42 | let unarchiveVC = try? JSONDecoder().decode(NotificationViewController.self, from: jsonData),
43 | unarchiveVC.username == User.shared.username
44 | {
45 | notificationVC = unarchiveVC
46 | }
47 | else
48 | {
49 | notificationVC = NotificationViewController()
50 | }
51 |
52 | if User.shared.isLogin == true,
53 | let diskCachePath = cachePathString(withFilename: MyProfileViewController.description()),
54 | let jsonData = NSKeyedUnarchiver.unarchiveObject(withFile: diskCachePath) as? Data,
55 | let unarchiveVC = try? JSONDecoder().decode(MyProfileViewController.self, from: jsonData)
56 | {
57 | profileVC = unarchiveVC
58 | }
59 | else
60 | {
61 | profileVC = MyProfileViewController()
62 | }
63 |
64 | nodesVC.searchVC.getAllNodesIfNeed()
65 |
66 | let homeNav = UINavigationController(rootViewController: homeVC)
67 | let nodesNav = UINavigationController(rootViewController: nodesVC)
68 | let searchNav = UINavigationController(rootViewController: searchVC)
69 | let notificationNav = UINavigationController(rootViewController: notificationVC)
70 | let profileNav = UINavigationController(rootViewController:profileVC)
71 |
72 | homeNav.tabBarItem = UITabBarItem(title: nil, image: R.Image.Home, selectedImage: R.Image.Home)
73 | nodesNav.tabBarItem = UITabBarItem(title: nil, image: R.Image.Nodes, selectedImage: R.Image.Nodes)
74 | searchNav.tabBarItem = UITabBarItem(title: nil, image: R.Image.TabarSearch, selectedImage: R.Image.TabarSearch)
75 | notificationTabItem = UITabBarItem(title: nil, image: R.Image.Notification, selectedImage: R.Image.Notification)
76 | notificationNav.tabBarItem = notificationTabItem
77 | profileNav.tabBarItem = UITabBarItem(title: nil, image: R.Image.Profile, selectedImage: R.Image.Profile)
78 |
79 | viewControllers = [homeNav, nodesNav, searchNav, notificationNav, profileNav]
80 |
81 | refreshColorScheme()
82 |
83 | NotificationCenter.default.addObserver(self, selector: #selector(refreshColorScheme), name: NSNotification.Name.Setting.NightModeDidChange, object: nil)
84 | }
85 |
86 | @objc
87 | private func refreshColorScheme()
88 | {
89 | tabBar.setupTabBar()
90 | }
91 |
92 | func setNotificationNum(_ number: Int)
93 | {
94 | notificationTabItem.badgeValue = number > 0 ? String(number) : nil
95 | }
96 |
97 | func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool
98 | {
99 | if viewControllers![selectedIndex] == viewController
100 | {
101 | if selectedIndex == 0
102 | {
103 | homeVC.doubleTapTabarItem()
104 | }
105 | else if selectedIndex == 1
106 | {
107 | nodesVC.doubleTapTabarItem()
108 | }
109 | else if selectedIndex == 3
110 | {
111 | notificationVC.doubleTapTabarItem()
112 | }
113 | return false
114 | }
115 | return true
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------