@hashtagcms/themes 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/package.json +45 -0
  4. package/src/core/js/bootstrap.js +36 -0
  5. package/src/core/js/components/subscribe.js +54 -0
  6. package/src/core/js/helpers/common.js +20 -0
  7. package/src/core/js/helpers/form.js +192 -0
  8. package/src/core/js/utils/analytics.js +85 -0
  9. package/src/themes/basic/fonts/FontAwesome.otf +0 -0
  10. package/src/themes/basic/fonts/fontawesome-webfont.eot +0 -0
  11. package/src/themes/basic/fonts/fontawesome-webfont.svg +2671 -0
  12. package/src/themes/basic/fonts/fontawesome-webfont.ttf +0 -0
  13. package/src/themes/basic/fonts/fontawesome-webfont.woff +0 -0
  14. package/src/themes/basic/fonts/fontawesome-webfont.woff2 +0 -0
  15. package/src/themes/basic/img/clouds.jpg +0 -0
  16. package/src/themes/basic/img/cms-box-1.png +0 -0
  17. package/src/themes/basic/img/cms-box-2.png +0 -0
  18. package/src/themes/basic/img/dfd.png +0 -0
  19. package/src/themes/basic/img/favicon.png +0 -0
  20. package/src/themes/basic/img/hasshtagcms-product-1.jpg +0 -0
  21. package/src/themes/basic/img/hasshtagcms-product-2.jpg +0 -0
  22. package/src/themes/basic/img/hasshtagcms-product-bg.jpg +0 -0
  23. package/src/themes/basic/img/hasshtagcms-product-with-text.jpg +0 -0
  24. package/src/themes/basic/img/hill.jpg +0 -0
  25. package/src/themes/basic/img/logo-transparent.png +0 -0
  26. package/src/themes/basic/img/logo-white-bg.jpg +0 -0
  27. package/src/themes/basic/img/qr-code.jpg +0 -0
  28. package/src/themes/basic/img/sunset.jpg +0 -0
  29. package/src/themes/basic/img/sunset2.jpg +0 -0
  30. package/src/themes/basic/img/user-default.jpg +0 -0
  31. package/src/themes/basic/js/app.js +23 -0
  32. package/src/themes/basic/sass/_basic.scss +410 -0
  33. package/src/themes/basic/sass/_variables.scss +12 -0
  34. package/src/themes/basic/sass/animate.css +1579 -0
  35. package/src/themes/basic/sass/app.scss +10 -0
  36. package/src/themes/basic/sass/font-awesome/HELP-US-OUT.txt +7 -0
  37. package/src/themes/basic/sass/font-awesome/css/font-awesome.css +2337 -0
  38. package/src/themes/basic/sass/font-awesome/css/font-awesome.min.css +4 -0
  39. package/src/themes/basic/sass/font-awesome/fonts/FontAwesome.otf +0 -0
  40. package/src/themes/basic/sass/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  41. package/src/themes/basic/sass/font-awesome/fonts/fontawesome-webfont.svg +2671 -0
  42. package/src/themes/basic/sass/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  43. package/src/themes/basic/sass/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  44. package/src/themes/basic/sass/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  45. package/src/themes/basic/sass/font-awesome/scss/_animated.scss +34 -0
  46. package/src/themes/basic/sass/font-awesome/scss/_bordered-pulled.scss +25 -0
  47. package/src/themes/basic/sass/font-awesome/scss/_core.scss +12 -0
  48. package/src/themes/basic/sass/font-awesome/scss/_fixed-width.scss +6 -0
  49. package/src/themes/basic/sass/font-awesome/scss/_icons.scss +789 -0
  50. package/src/themes/basic/sass/font-awesome/scss/_larger.scss +13 -0
  51. package/src/themes/basic/sass/font-awesome/scss/_list.scss +19 -0
  52. package/src/themes/basic/sass/font-awesome/scss/_mixins.scss +60 -0
  53. package/src/themes/basic/sass/font-awesome/scss/_path.scss +15 -0
  54. package/src/themes/basic/sass/font-awesome/scss/_rotated-flipped.scss +20 -0
  55. package/src/themes/basic/sass/font-awesome/scss/_screen-reader.scss +5 -0
  56. package/src/themes/basic/sass/font-awesome/scss/_stacked.scss +20 -0
  57. package/src/themes/basic/sass/font-awesome/scss/_variables.scss +799 -0
  58. package/src/themes/basic/sass/font-awesome/scss/font-awesome.scss +18 -0
  59. package/src/themes/elegant/img/feature-ai.png +0 -0
  60. package/src/themes/elegant/img/feature-cloud.png +0 -0
  61. package/src/themes/elegant/img/hero-bg.png +0 -0
  62. package/src/themes/elegant/js/app.js +126 -0
  63. package/src/themes/elegant/sass/_elegant.scss +508 -0
  64. package/src/themes/elegant/sass/_variables.scss +42 -0
  65. package/src/themes/elegant/sass/app.scss +14 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Marghoob Suleman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @hashtagcms/themes
2
+
3
+ > Beautiful, customizable themes for HashtagCMS with shared core logic
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@hashtagcms/themes.svg)](https://www.npmjs.com/package/@hashtagcms/themes)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+
8
+ This package contains frontend themes and core JavaScript logic for the HashtagCMS ecosystem. Switch between themes while sharing the same underlying functionality.
9
+
10
+ ## ✨ Features
11
+
12
+ - 🎨 **Multiple Themes** - Choose from Basic or Elegant themes
13
+ - 🔧 **Customizable** - Override variables and extend styles
14
+ - 📦 **Modular** - Use source files or pre-compiled assets
15
+ - 🚀 **Framework Agnostic** - Works with any JavaScript framework
16
+ - 📱 **Responsive** - Mobile-first design
17
+ - ♿ **Accessible** - WCAG compliant
18
+
19
+ ## 📦 Quick Start
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ npm install @hashtagcms/themes
25
+ ```
26
+
27
+ ### Basic Usage
28
+
29
+ **Import in your SCSS:**
30
+ ```scss
31
+ @import "~@hashtagcms/themes/src/themes/basic/sass/app";
32
+ ```
33
+
34
+ **Import in your JavaScript:**
35
+ ```javascript
36
+ import '@hashtagcms/themes/src/themes/basic/js/app';
37
+ ```
38
+
39
+ ### Using Pre-compiled Assets
40
+
41
+ ```html
42
+ <link rel="stylesheet" href="node_modules/@hashtagcms/themes/dist/themes/basic/app.css">
43
+ <script src="node_modules/@hashtagcms/themes/dist/themes/basic/app.js"></script>
44
+ ```
45
+
46
+ ## 🎨 Available Themes
47
+
48
+ ### Basic Theme
49
+ Clean, traditional design perfect for content-focused websites.
50
+ - ✅ FontAwesome icons
51
+ - ✅ Bootstrap 5 styling
52
+ - ✅ Neutral color palette
53
+ - ✅ Subscribe form component
54
+
55
+ ### Elegant Theme
56
+ Modern, sophisticated design with smooth animations.
57
+ - ✅ Glass morphism effects
58
+ - ✅ Parallax scrolling
59
+ - ✅ Gradient text and buttons
60
+ - ✅ Card animations
61
+ - ✅ Dark color scheme
62
+
63
+ ## 📚 Documentation
64
+
65
+ - **[Getting Started](./docs/GETTING_STARTED.md)** - Installation and basic usage
66
+ - **[Creating Themes](./docs/CREATING_THEMES.md)** - Step-by-step guide to create custom themes
67
+ - **[Theme Structure](./docs/THEME_STRUCTURE.md)** - Package organization and architecture
68
+ - **[API Reference](./docs/API_REFERENCE.md)** - Complete API documentation
69
+ - **[FAQ](./docs/FAQ.md)** - Frequently asked questions
70
+
71
+ ## 🚀 Creating a Custom Theme
72
+
73
+ ```bash
74
+ # 1. Create theme directory
75
+ mkdir -p src/themes/my-theme/{js,sass,img}
76
+
77
+ # 2. Create entry files
78
+ touch src/themes/my-theme/js/app.js
79
+ touch src/themes/my-theme/sass/app.scss
80
+
81
+ # 3. Build
82
+ npm run build
83
+ ```
84
+
85
+ See the [Creating Themes Guide](./docs/CREATING_THEMES.md) for detailed instructions.
86
+
87
+ ## 🛠️ Development
88
+
89
+ ### Build Commands
90
+
91
+ ```bash
92
+ # Development build
93
+ npm run dev
94
+
95
+ # Production build
96
+ npm run build
97
+
98
+ # Watch mode
99
+ npm run watch
100
+ ```
101
+
102
+ ### Project Structure
103
+
104
+ ```
105
+ @hashtagcms/themes/
106
+ ├── src/
107
+ │ ├── core/ # Shared JavaScript logic
108
+ │ └── themes/ # Individual themes
109
+ │ ├── basic/
110
+ │ └── elegant/
111
+ ├── dist/ # Compiled assets
112
+ ├── docs/ # Documentation
113
+ └── package.json
114
+ ```
115
+
116
+ ## 🤝 Contributing
117
+
118
+ We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
119
+
120
+ ### How to Contribute
121
+
122
+ 1. Fork the repository
123
+ 2. Create your theme or feature
124
+ 3. Test thoroughly
125
+ 4. Submit a pull request
126
+
127
+ ## 📄 License
128
+
129
+ [MIT](LICENSE) © Marghoob Suleman
130
+
131
+ ## 🆘 Support
132
+
133
+ - 📖 [Documentation](./docs/GETTING_STARTED.md)
134
+ - 💬 [GitHub Discussions](https://github.com/marghoobsuleman/hashtagcms-themes/discussions)
135
+ - 🐛 [Report Issues](https://github.com/marghoobsuleman/hashtagcms-themes/issues)
136
+
137
+ ## 🌟 Showcase
138
+
139
+ Using @hashtagcms/themes in your project? We'd love to feature it! Open an issue to share your work.
140
+
141
+ ---
142
+
143
+ Made with ❤️ by the HashtagCMS team
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@hashtagcms/themes",
3
+ "version": "1.0.0",
4
+ "author": {
5
+ "name": "Marghoob Suleman",
6
+ "email": "marghoobsuleman@gmail.com",
7
+ "url": "https://github.com/marghoobsuleman"
8
+ },
9
+ "description": "Frontend Themes and Core Logic for HashtagCMS",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/marghoobsuleman/cms-frontend-kit.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/marghoobsuleman/cms-frontend-kit/issues"
16
+ },
17
+ "homepage": "https://github.com/marghoobsuleman/cms-frontend-kit#readme",
18
+ "main": "src/core/js/index.js",
19
+ "files": [
20
+ "src"
21
+ ],
22
+ "scripts": {
23
+ "test": "echo \"Error: no test specified\" && exit 1",
24
+ "dev": "webpack --mode development --progress --color",
25
+ "prod": "webpack --mode production --progress --color",
26
+ "build": "npm run prod",
27
+ "watch": "webpack --mode development --watch --progress --color"
28
+ },
29
+ "devDependencies": {
30
+ "@babel/core": "^7.23.0",
31
+ "@babel/preset-env": "^7.23.0",
32
+ "babel-loader": "^9.1.0",
33
+ "css-loader": "^6.8.0",
34
+ "mini-css-extract-plugin": "^2.7.0",
35
+ "copy-webpack-plugin": "^11.0.0",
36
+ "sass": "^1.69.0",
37
+ "sass-loader": "^13.3.0",
38
+ "webpack": "^5.89.0",
39
+ "webpack-cli": "^5.1.0"
40
+ },
41
+ "dependencies": {
42
+ "axios": "^1.8.0",
43
+ "bootstrap": "^5.3.3"
44
+ }
45
+ }
@@ -0,0 +1,36 @@
1
+ import axios from "axios";
2
+ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
3
+ let token = document.head.querySelector('meta[name="csrf-token"]');
4
+ if (token) {
5
+ axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
6
+ axios.defaults.headers.common['Authorization'] = 'Bearer '+token.content;
7
+ axios.defaults.withCredentials = true;
8
+ } else {
9
+ console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
10
+ }
11
+ window.axios = axios;
12
+
13
+
14
+ import Analytics from "./utils/analytics";
15
+ import Subscribe from "./components/subscribe";
16
+
17
+ window.HashtagCms = {};
18
+ window.HashtagCms.Subscribe = new Subscribe();
19
+ window.HashtagCms.Analytics = new Analytics();
20
+
21
+ /**
22
+ * Echo exposes an expressive Api for subscribing to channels and listening
23
+ * for events that are broadcast by Laravel. Echo and event broadcasting
24
+ * allows your team to easily build robust real-time web applications.
25
+ */
26
+
27
+ // import Echo from 'laravel-echo'
28
+
29
+ // window.Pusher = require('pusher-js');
30
+
31
+ // window.Echo = new Echo({
32
+ // broadcaster: 'pusher',
33
+ // key: process.env.MIX_PUSHER_APP_KEY,
34
+ // cluster: process.env.MIX_PUSHER_APP_CLUSTER,
35
+ // encrypted: true
36
+ // });
@@ -0,0 +1,54 @@
1
+ export default class Subscribe {
2
+ constructor() {
3
+ this.elements = {
4
+ close: "span[data-class='subscribe-close']",
5
+ message: "span[data-class='subscribe-message']", form: "form[data-form='subscribe-form']",
6
+ messageHolder: "div[data-message-holder='subscribe-message-holder']"
7
+ };
8
+ this.init();
9
+ }
10
+
11
+ init() {
12
+
13
+ if (document.querySelector(this.elements.close)) {
14
+ document.querySelector(this.elements.close).addEventListener('click', (ele) => {
15
+ document.querySelector(this.elements.messageHolder).style.display = "none";
16
+ });
17
+ }
18
+ }
19
+
20
+ subscribeNow() {
21
+ let showMessage = function (message) {
22
+ document.querySelector($this.elements.message).innerText = message;
23
+ }
24
+ let $this = this;
25
+ let afterSave = function (data) {
26
+ console.log("data ", data);
27
+ document.querySelector($this.elements.messageHolder).style.display = "";
28
+ if (data.success == true) {
29
+ showMessage(data.message);
30
+ document.querySelector($this.elements.message).innerText = data.message;
31
+ document.querySelector($this.elements.form + " input[type='email']").value = '';
32
+ document.querySelector($this.elements.message).classList.remove("text-danger");
33
+ document.querySelector($this.elements.message).classList.add("text-success");
34
+ } else {
35
+ showMessage(data.message.email[0] || "There is some error.");
36
+ document.querySelector($this.elements.message).classList.remove("text-success");
37
+ document.querySelector($this.elements.message).classList.add("text-danger");
38
+ }
39
+ }
40
+
41
+ let email = document.querySelector(this.elements.form + " input[type='email']");
42
+ let url = "/common/subscribe";
43
+ let data = { email: email.value };
44
+ axios.post(url, data)
45
+ .then(response => {
46
+ afterSave(response.data);
47
+ }).catch((error) => {
48
+ afterSave(error.response.data);
49
+ });
50
+
51
+ return false;
52
+ }
53
+
54
+ }
@@ -0,0 +1,20 @@
1
+ export class AppConfig {
2
+ constructor(data) {
3
+ this.configData = data;
4
+ }
5
+
6
+ setConfigData(data) {
7
+ this.configData = data;
8
+ }
9
+
10
+ getValue(key, defaultVal) {
11
+ return this.configData[key] || defaultVal;
12
+ }
13
+
14
+ getMedia(path) {
15
+ let media = this.getValue("media");
16
+ return media.http_path+"/"+path;
17
+ }
18
+ };
19
+
20
+ //create a function to call an api using ajax
@@ -0,0 +1,192 @@
1
+ export class Errors {
2
+ /**
3
+ * Create a new Errors instance.
4
+ */
5
+ constructor() {
6
+ this.errors = {};
7
+ }
8
+
9
+
10
+ /**
11
+ * Determine if an errors exists for the given field.
12
+ *
13
+ * @param {string} field
14
+ */
15
+ has(field) {
16
+ return this.errors.hasOwnProperty(field);
17
+ }
18
+
19
+
20
+ /**
21
+ * Determine if we have any errors.
22
+ */
23
+ any() {
24
+ return Object.keys(this.errors).length > 0;
25
+ }
26
+
27
+
28
+ /**
29
+ * Retrieve the error message for a field.
30
+ *
31
+ * @param {string} field
32
+ */
33
+ get(field) {
34
+ if (this.errors[field]) {
35
+ return this.errors[field][0];
36
+ }
37
+ }
38
+
39
+
40
+ /**
41
+ * Record the new errors.
42
+ *
43
+ * @param {object} errors
44
+ */
45
+ record(errors) {
46
+ this.errors = errors;
47
+ }
48
+
49
+
50
+ /**
51
+ * Clear one or all error fields.
52
+ *
53
+ * @param {string|null} field
54
+ */
55
+ clear(field) {
56
+ if (field) {
57
+ delete this.errors[field];
58
+
59
+ return;
60
+ }
61
+
62
+ this.errors = {};
63
+ }
64
+ }
65
+
66
+
67
+ export default class Form {
68
+ /**
69
+ * Create a new Form instance.
70
+ *
71
+ * @param {object} data
72
+ */
73
+ constructor(data) {
74
+ this.originalData = data;
75
+
76
+ for (let field in data) {
77
+ this[field] = data[field];
78
+ }
79
+
80
+ this.errors = new Errors();
81
+ }
82
+
83
+
84
+ /**
85
+ * Fetch all relevant data for the form.
86
+ */
87
+ data() {
88
+ let data = {};
89
+
90
+ for (let property in this.originalData) {
91
+ data[property] = this[property];
92
+ }
93
+
94
+ return data;
95
+ }
96
+
97
+
98
+ /**
99
+ * Reset the form fields.
100
+ */
101
+ reset() {
102
+ for (let field in this.originalData) {
103
+ this[field] = '';
104
+ }
105
+
106
+ this.errors.clear();
107
+ }
108
+
109
+
110
+ /**
111
+ * Send a POST request to the given URL.
112
+ * .
113
+ * @param {string} url
114
+ */
115
+ post(url, resetAfterSubmit=true) {
116
+ return this.submit('post', url, resetAfterSubmit);
117
+ }
118
+
119
+
120
+ /**
121
+ * Send a PUT request to the given URL.
122
+ * .
123
+ * @param {string} url
124
+ */
125
+ put(url, resetAfterSubmit=true) {
126
+ return this.submit('put', url, resetAfterSubmit);
127
+ }
128
+
129
+
130
+ /**
131
+ * Send a PATCH request to the given URL.
132
+ * .
133
+ * @param {string} url
134
+ */
135
+ patch(url, resetAfterSubmit=true) {
136
+ return this.submit('patch', url, resetAfterSubmit);
137
+ }
138
+
139
+
140
+ /**
141
+ * Send a DELETE request to the given URL.
142
+ * .
143
+ * @param {string} url
144
+ */
145
+ delete(url, resetAfterSubmit=true) {
146
+ return this.submit('delete', url, resetAfterSubmit);
147
+ }
148
+
149
+
150
+ /**
151
+ * Submit the form.
152
+ *
153
+ * @param {string} requestType
154
+ * @param {string} url
155
+ */
156
+ submit(requestType, url, resetAfterSubmit=true) {
157
+ return new Promise((resolve, reject) => {
158
+ axios[requestType](url, this.data())
159
+ .then(response => {
160
+ resolve(response.data);
161
+ this.onSuccess(response.data, resetAfterSubmit);
162
+ }).catch(error => {
163
+ this.onFail(error.response.data);
164
+ reject(error.response.data);
165
+ });
166
+ });
167
+ }
168
+
169
+
170
+ /**
171
+ * Handle a successful form submission.
172
+ *
173
+ * @param {object} data
174
+ */
175
+ onSuccess(data, resetAfterSubmit=true) {
176
+ // alert(data.message); // temporary
177
+ if(resetAfterSubmit==true) {
178
+ this.reset();
179
+ }
180
+
181
+ }
182
+
183
+
184
+ /**
185
+ * Handle a failed form submission.
186
+ *
187
+ * @param {object} errors
188
+ */
189
+ onFail(errors) {
190
+ this.errors.record(errors);
191
+ }
192
+ }
@@ -0,0 +1,85 @@
1
+ export default class Analytics {
2
+
3
+ constructor() {
4
+
5
+ }
6
+
7
+ init(data) {
8
+ this.readCounter(data);
9
+ }
10
+ readCounter(data) {
11
+
12
+ this.submit("post", "/analytics/publish", data);
13
+ }
14
+
15
+ /**
16
+ * Submit request.
17
+ *
18
+ * @param {string} requestType
19
+ * @param {string} url
20
+ * @param data
21
+ */
22
+ submit(requestType, url, data) {
23
+ /*data = new Blob([JSON.stringify(data)], {type : 'application/json'});
24
+ data.csrfToken = window.Laravel.csrfToken;
25
+ console.log("data",data);
26
+ navigator.sendBeacon(url, data);*/
27
+ return new Promise((resolve, reject) => {
28
+ axios[requestType](url, data)
29
+ .then(response => {
30
+ resolve(response.data);
31
+ }).catch(error => {
32
+ reject(error.response.data);
33
+ });
34
+ });
35
+ }
36
+
37
+ trackEventView (category, action ,value, cb) {
38
+ try {
39
+ //very very old ga
40
+ _gaq.push(['_trackEvent', category, action, value]);
41
+ } catch(e) {
42
+ }
43
+ if ( typeof ga != "undefined") {
44
+ try {
45
+ ga('send', {
46
+ hitType : 'event',
47
+ eventCategory : category,
48
+ eventAction : action,
49
+ eventLabel : value
50
+ });
51
+ } catch(e) {
52
+ }
53
+ }
54
+ if (cb) {
55
+ cb.apply(this, arguments);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Track Page view
61
+ * @param value
62
+ * @param cb
63
+ */
64
+ trackPageView (value, cb) {
65
+ try {
66
+ //Very very old ga
67
+ _gaq.push(['_trackPageview', value]);
68
+ } catch(e) {
69
+ }
70
+
71
+ if ( typeof ga != "undefined") {
72
+ try {
73
+ ga('send',
74
+ {hitType: 'pageview',
75
+ page: value
76
+ });
77
+ } catch(e) {}
78
+ }
79
+
80
+ if (cb) {
81
+ cb.apply(this, arguments);
82
+ }
83
+ }
84
+
85
+ }