Merge pull request #7381 from MrPetovan/task/7309-frio-compose
[frio] New Compose page
This commit is contained in:
		
				commit
				
					
						e8459cce34
					
				
			
		
					 32 changed files with 1730 additions and 184 deletions
				
			
		| 
						 | 
				
			
			@ -32,12 +32,14 @@
 | 
			
		|||
		"ezyang/htmlpurifier": "~4.7.0",
 | 
			
		||||
		"friendica/json-ld": "^1.0",
 | 
			
		||||
		"league/html-to-markdown": "~4.8.0",
 | 
			
		||||
		"level-2/dice": ">1.0",
 | 
			
		||||
		"lightopenid/lightopenid": "dev-master",
 | 
			
		||||
		"michelf/php-markdown": "^1.7",
 | 
			
		||||
		"mobiledetect/mobiledetectlib": "2.8.*",
 | 
			
		||||
		"monolog/monolog": "^1.24",
 | 
			
		||||
		"nikic/fast-route": "^1.3",
 | 
			
		||||
		"paragonie/hidden-string": "^1.0",
 | 
			
		||||
		"pear/console_table": "^1.3",
 | 
			
		||||
		"pear/text_languagedetect": "1.*",
 | 
			
		||||
		"pragmarx/google2fa": "^5.0",
 | 
			
		||||
		"pragmarx/recovery": "^0.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -58,8 +60,7 @@
 | 
			
		|||
		"npm-asset/fullcalendar": "^3.0.1",
 | 
			
		||||
		"npm-asset/cropperjs": "1.2.2",
 | 
			
		||||
		"npm-asset/imagesloaded": "4.1.4",
 | 
			
		||||
		"pear/console_table": "^1.3",
 | 
			
		||||
		"level-2/dice": ">1.0"
 | 
			
		||||
		"npm-asset/typeahead.js": "^0.11.1"
 | 
			
		||||
	},
 | 
			
		||||
	"repositories": [
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										166
									
								
								composer.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										166
									
								
								composer.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -4,7 +4,7 @@
 | 
			
		|||
        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
 | 
			
		||||
        "This file is @generated automatically"
 | 
			
		||||
    ],
 | 
			
		||||
    "content-hash": "9f101b2b4a651f425e155d0029223774",
 | 
			
		||||
    "content-hash": "8c88c86ebce0bbf672356d65fadc0008",
 | 
			
		||||
    "packages": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "asika/simple-console",
 | 
			
		||||
| 
						 | 
				
			
			@ -1076,7 +1076,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.2.2.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "30dc7a7ce872155b23a33bd10ad4c76c0d613f55"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1170,7 +1169,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "8f18b0ce5c76a5d18017f71c0a795c65b9138f2a"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
| 
						 | 
				
			
			@ -1213,7 +1211,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/fullcalendar/-/fullcalendar-3.10.0.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "cc5e87d518fd6550e142816a31dd191664847919"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
| 
						 | 
				
			
			@ -1260,28 +1257,11 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "1376efcd162bb768c34c3727ac89cc04051f3cc7"
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
                "npm-asset/ev-emitter": ">=1.0.0,<2.0.0"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "npm-asset/chalk": ">=1.1.1,<2.0.0",
 | 
			
		||||
                "npm-asset/cheerio": ">=0.19.0,<0.20.0",
 | 
			
		||||
                "npm-asset/gulp": ">=3.9.0,<4.0.0",
 | 
			
		||||
                "npm-asset/gulp-jshint": ">=1.11.2,<2.0.0",
 | 
			
		||||
                "npm-asset/gulp-json-lint": ">=0.1.0,<0.2.0",
 | 
			
		||||
                "npm-asset/gulp-rename": ">=1.2.2,<2.0.0",
 | 
			
		||||
                "npm-asset/gulp-replace": ">=0.5.4,<0.6.0",
 | 
			
		||||
                "npm-asset/gulp-requirejs-optimize": "dev-github:metafizzy/gulp-requirejs-optimize",
 | 
			
		||||
                "npm-asset/gulp-uglify": ">=1.4.2,<2.0.0",
 | 
			
		||||
                "npm-asset/gulp-util": ">=3.0.7,<4.0.0",
 | 
			
		||||
                "npm-asset/highlight.js": ">=8.9.1,<9.0.0",
 | 
			
		||||
                "npm-asset/marked": ">=0.3.5,<0.4.0",
 | 
			
		||||
                "npm-asset/minimist": ">=1.2.0,<2.0.0",
 | 
			
		||||
                "npm-asset/transfob": ">=1.0.0,<2.0.0"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "npm-asset-bugs": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1324,17 +1304,8 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/jgrowl/-/jgrowl-1.4.6.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "2736e332aaee73ccf0a14a5f0066391a0a13f4a3"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "npm-asset/grunt": "~0.4.2",
 | 
			
		||||
                "npm-asset/grunt-contrib-cssmin": "~0.9.0",
 | 
			
		||||
                "npm-asset/grunt-contrib-jshint": "~0.6.3",
 | 
			
		||||
                "npm-asset/grunt-contrib-less": "~0.11.0",
 | 
			
		||||
                "npm-asset/grunt-contrib-uglify": "~0.4.0",
 | 
			
		||||
                "npm-asset/grunt-contrib-watch": "~0.6.1"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "npm-asset-bugs": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1365,35 +1336,8 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "2c89d6889b5eac522a7eea32c14521559c6cbf02"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "npm-asset/commitplease": "2.0.0",
 | 
			
		||||
                "npm-asset/core-js": "0.9.17",
 | 
			
		||||
                "npm-asset/grunt": "0.4.5",
 | 
			
		||||
                "npm-asset/grunt-babel": "5.0.1",
 | 
			
		||||
                "npm-asset/grunt-cli": "0.1.13",
 | 
			
		||||
                "npm-asset/grunt-compare-size": "0.4.0",
 | 
			
		||||
                "npm-asset/grunt-contrib-jshint": "0.11.2",
 | 
			
		||||
                "npm-asset/grunt-contrib-uglify": "0.9.2",
 | 
			
		||||
                "npm-asset/grunt-contrib-watch": "0.6.1",
 | 
			
		||||
                "npm-asset/grunt-git-authors": "2.0.1",
 | 
			
		||||
                "npm-asset/grunt-jscs": "2.1.0",
 | 
			
		||||
                "npm-asset/grunt-jsonlint": "1.0.4",
 | 
			
		||||
                "npm-asset/grunt-npmcopy": "0.1.0",
 | 
			
		||||
                "npm-asset/gzip-js": "0.3.2",
 | 
			
		||||
                "npm-asset/jsdom": "5.6.1",
 | 
			
		||||
                "npm-asset/load-grunt-tasks": "1.0.0",
 | 
			
		||||
                "npm-asset/qunit-assert-step": "1.0.3",
 | 
			
		||||
                "npm-asset/qunitjs": "1.17.1",
 | 
			
		||||
                "npm-asset/requirejs": "2.1.17",
 | 
			
		||||
                "npm-asset/sinon": "1.10.3",
 | 
			
		||||
                "npm-asset/sizzle": "2.2.1",
 | 
			
		||||
                "npm-asset/strip-json-comments": "1.0.3",
 | 
			
		||||
                "npm-asset/testswarm": "1.1.0",
 | 
			
		||||
                "npm-asset/win-spawn": "2.0.0"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "npm-asset-bugs": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1436,7 +1380,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/jquery-colorbox/-/jquery-colorbox-1.6.4.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "799452523a6c494839224ef702e807deb9c06cc5"
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1483,7 +1426,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/jquery-datetimepicker/-/jquery-datetimepicker-2.5.20.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "687d6204b90b03dc93f725f8df036e1d061f37ac"
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1541,15 +1483,8 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "06f0335f16e353a695e7206bf50503cb523a6ee5"
 | 
			
		||||
            },
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "npm-asset/grunt": "~0.4.1",
 | 
			
		||||
                "npm-asset/grunt-contrib-connect": "~0.5.0",
 | 
			
		||||
                "npm-asset/grunt-contrib-jshint": "~0.7.1",
 | 
			
		||||
                "npm-asset/grunt-contrib-uglify": "~0.2.7"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "npm-asset-bugs": {
 | 
			
		||||
| 
						 | 
				
			
			@ -1596,7 +1531,6 @@
 | 
			
		|||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
 | 
			
		||||
                "reference": null,
 | 
			
		||||
                "shasum": "3c257f9839fc0e93ff53149632239eb90783ff66"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
| 
						 | 
				
			
			@ -1707,6 +1641,58 @@
 | 
			
		|||
            "homepage": "https://github.com/kartik-v/php-date-formatter",
 | 
			
		||||
            "time": "2018-07-13T06:56:46+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "npm-asset/typeahead.js",
 | 
			
		||||
            "version": "0.11.1",
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "tar",
 | 
			
		||||
                "url": "https://registry.npmjs.org/typeahead.js/-/typeahead.js-0.11.1.tgz",
 | 
			
		||||
                "shasum": "4e64e671b22310a8606f4aec805924ba84b015b8"
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
                "npm-asset/jquery": ">=1.7"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "npm-asset-library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "npm-asset-bugs": {
 | 
			
		||||
                    "url": "https://github.com/twitter/typeahead.js/issues"
 | 
			
		||||
                },
 | 
			
		||||
                "npm-asset-main": "dist/typeahead.bundle.js",
 | 
			
		||||
                "npm-asset-directories": [],
 | 
			
		||||
                "npm-asset-repository": {
 | 
			
		||||
                    "type": "git",
 | 
			
		||||
                    "url": "https://github.com/twitter/typeahead.js.git"
 | 
			
		||||
                },
 | 
			
		||||
                "npm-asset-scripts": {
 | 
			
		||||
                    "test": "./node_modules/karma/bin/karma start --single-run --browsers PhantomJS"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Twitter, Inc.",
 | 
			
		||||
                    "url": "https://twitter.com/twitteross"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Jake Harding",
 | 
			
		||||
                    "url": "https://twitter.com/JakeHarding"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Tim Trueman",
 | 
			
		||||
                    "url": "https://twitter.com/timtrueman"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Veljko Skarich",
 | 
			
		||||
                    "url": "https://twitter.com/vskarich"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "fast and fully-featured autocomplete library",
 | 
			
		||||
            "homepage": "http://twitter.github.com/typeahead.js",
 | 
			
		||||
            "keywords": [
 | 
			
		||||
                "autocomplete",
 | 
			
		||||
                "typeahead"
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2015-04-27T04:03:42+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "paragonie/certainty",
 | 
			
		||||
            "version": "v1.0.4",
 | 
			
		||||
| 
						 | 
				
			
			@ -2883,7 +2869,7 @@
 | 
			
		|||
            "time": "2017-03-25T17:14:26+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "mikey179/vfsStream",
 | 
			
		||||
            "name": "mikey179/vfsstream",
 | 
			
		||||
            "version": "v1.6.5",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
| 
						 | 
				
			
			@ -2920,8 +2906,8 @@
 | 
			
		|||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Frank Kleine",
 | 
			
		||||
                    "homepage": "http://frankkleine.de/",
 | 
			
		||||
                    "role": "Developer"
 | 
			
		||||
                    "role": "Developer",
 | 
			
		||||
                    "homepage": "http://frankkleine.de/"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Virtual file system to mock the real file system in unit tests.",
 | 
			
		||||
| 
						 | 
				
			
			@ -3353,8 +3339,8 @@
 | 
			
		|||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Sebastian Bergmann",
 | 
			
		||||
                    "email": "sb@sebastian-bergmann.de",
 | 
			
		||||
                    "role": "lead"
 | 
			
		||||
                    "role": "lead",
 | 
			
		||||
                    "email": "sb@sebastian-bergmann.de"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
 | 
			
		||||
| 
						 | 
				
			
			@ -3621,8 +3607,8 @@
 | 
			
		|||
            "authors": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "Sebastian Bergmann",
 | 
			
		||||
                    "email": "sebastian@phpunit.de",
 | 
			
		||||
                    "role": "lead"
 | 
			
		||||
                    "role": "lead",
 | 
			
		||||
                    "email": "sebastian@phpunit.de"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "The PHP Unit Testing framework.",
 | 
			
		||||
| 
						 | 
				
			
			@ -3795,7 +3781,7 @@
 | 
			
		|||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Provides the functionality to compare PHP values for equality",
 | 
			
		||||
            "homepage": "https://github.com/sebastianbergmann/comparator",
 | 
			
		||||
            "homepage": "http://www.github.com/sebastianbergmann/comparator",
 | 
			
		||||
            "keywords": [
 | 
			
		||||
                "comparator",
 | 
			
		||||
                "compare",
 | 
			
		||||
| 
						 | 
				
			
			@ -3897,7 +3883,7 @@
 | 
			
		|||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Provides functionality to handle HHVM/PHP environments",
 | 
			
		||||
            "homepage": "https://github.com/sebastianbergmann/environment",
 | 
			
		||||
            "homepage": "http://www.github.com/sebastianbergmann/environment",
 | 
			
		||||
            "keywords": [
 | 
			
		||||
                "Xdebug",
 | 
			
		||||
                "environment",
 | 
			
		||||
| 
						 | 
				
			
			@ -3965,7 +3951,7 @@
 | 
			
		|||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Provides the functionality to export PHP variables for visualization",
 | 
			
		||||
            "homepage": "https://github.com/sebastianbergmann/exporter",
 | 
			
		||||
            "homepage": "http://www.github.com/sebastianbergmann/exporter",
 | 
			
		||||
            "keywords": [
 | 
			
		||||
                "export",
 | 
			
		||||
                "exporter"
 | 
			
		||||
| 
						 | 
				
			
			@ -4017,7 +4003,7 @@
 | 
			
		|||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Snapshotting of global state",
 | 
			
		||||
            "homepage": "https://github.com/sebastianbergmann/global-state",
 | 
			
		||||
            "homepage": "http://www.github.com/sebastianbergmann/global-state",
 | 
			
		||||
            "keywords": [
 | 
			
		||||
                "global state"
 | 
			
		||||
            ],
 | 
			
		||||
| 
						 | 
				
			
			@ -4119,7 +4105,7 @@
 | 
			
		|||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "description": "Provides functionality to recursively process PHP variables",
 | 
			
		||||
            "homepage": "https://github.com/sebastianbergmann/recursion-context",
 | 
			
		||||
            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
 | 
			
		||||
            "time": "2016-11-19T07:33:16+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -4209,16 +4195,16 @@
 | 
			
		|||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/polyfill-ctype",
 | 
			
		||||
            "version": "v1.11.0",
 | 
			
		||||
            "version": "v1.9.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/polyfill-ctype.git",
 | 
			
		||||
                "reference": "82ebae02209c21113908c229e9883c419720738a"
 | 
			
		||||
                "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a",
 | 
			
		||||
                "reference": "82ebae02209c21113908c229e9883c419720738a",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
 | 
			
		||||
                "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
| 
						 | 
				
			
			@ -4230,7 +4216,7 @@
 | 
			
		|||
            "type": "library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
                "branch-alias": {
 | 
			
		||||
                    "dev-master": "1.11-dev"
 | 
			
		||||
                    "dev-master": "1.9-dev"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            "autoload": {
 | 
			
		||||
| 
						 | 
				
			
			@ -4263,20 +4249,20 @@
 | 
			
		|||
                "polyfill",
 | 
			
		||||
                "portable"
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2019-02-06T07:57:58+00:00"
 | 
			
		||||
            "time": "2018-08-06T14:22:27+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "symfony/yaml",
 | 
			
		||||
            "version": "v3.4.30",
 | 
			
		||||
            "version": "v3.4.16",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/symfony/yaml.git",
 | 
			
		||||
                "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b"
 | 
			
		||||
                "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/yaml/zipball/051d045c684148060ebfc9affb7e3f5e0899d40b",
 | 
			
		||||
                "reference": "051d045c684148060ebfc9affb7e3f5e0899d40b",
 | 
			
		||||
                "url": "https://api.github.com/repos/symfony/yaml/zipball/61973ecda60e9f3561e929e19c07d4878b960fc1",
 | 
			
		||||
                "reference": "61973ecda60e9f3561e929e19c07d4878b960fc1",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
| 
						 | 
				
			
			@ -4322,7 +4308,7 @@
 | 
			
		|||
            ],
 | 
			
		||||
            "description": "Symfony Yaml Component",
 | 
			
		||||
            "homepage": "https://symfony.com",
 | 
			
		||||
            "time": "2019-07-24T13:01:31+00:00"
 | 
			
		||||
            "time": "2018-09-24T08:15:45+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "webmozart/assert",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,11 +4,11 @@
 | 
			
		|||
 */
 | 
			
		||||
 | 
			
		||||
use Friendica\App;
 | 
			
		||||
use Friendica\Content\Smilies;
 | 
			
		||||
use Friendica\Content\Text\BBCode;
 | 
			
		||||
use Friendica\Core\Protocol;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Model\FileTag;
 | 
			
		||||
use Friendica\Model\Group;
 | 
			
		||||
use Friendica\Util\Strings;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -20,18 +20,9 @@ use Friendica\Util\Strings;
 | 
			
		|||
function expand_acl($s) {
 | 
			
		||||
	// turn string array of angle-bracketed elements into numeric array
 | 
			
		||||
	// e.g. "<1><2><3>" => array(1,2,3);
 | 
			
		||||
	$ret = [];
 | 
			
		||||
	preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $s, $matches, PREG_PATTERN_ORDER);
 | 
			
		||||
 | 
			
		||||
	if (strlen($s)) {
 | 
			
		||||
		$t = str_replace('<', '', $s);
 | 
			
		||||
		$a = explode('>', $t);
 | 
			
		||||
		foreach ($a as $aa) {
 | 
			
		||||
			if (intval($aa)) {
 | 
			
		||||
				$ret[] = intval($aa);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return $ret;
 | 
			
		||||
	return $matches[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +33,8 @@ function expand_acl($s) {
 | 
			
		|||
function sanitise_acl(&$item) {
 | 
			
		||||
	if (intval($item)) {
 | 
			
		||||
		$item = '<' . intval(Strings::escapeTags(trim($item))) . '>';
 | 
			
		||||
	} elseif (in_array($item, [Group::FOLLOWERS, Group::MUTUALS])) {
 | 
			
		||||
		$item = '<' . $item . '>';
 | 
			
		||||
	} else {
 | 
			
		||||
		unset($item);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ use Friendica\App;
 | 
			
		|||
use Friendica\Core\Hook;
 | 
			
		||||
use Friendica\Core\L10n;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\Model\Group;
 | 
			
		||||
use Friendica\Model\Item;
 | 
			
		||||
 | 
			
		||||
function lockview_content(App $a)
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,19 @@ function lockview_content(App $a)
 | 
			
		|||
	$l = [];
 | 
			
		||||
 | 
			
		||||
	if (count($allowed_groups)) {
 | 
			
		||||
		$key = array_search(Group::FOLLOWERS, $allowed_groups);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$l[] = '<b>' . L10n::t('Followers') . '</b>';
 | 
			
		||||
			unset($allowed_groups[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$key = array_search(Group::MUTUALS, $allowed_groups);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$l[] = '<b>' . L10n::t('Mutuals') . '</b>';
 | 
			
		||||
			unset($allowed_groups[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
 | 
			
		||||
			DBA::escape(implode(', ', $allowed_groups))
 | 
			
		||||
		);
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +103,18 @@ function lockview_content(App $a)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (count($deny_groups)) {
 | 
			
		||||
		$key = array_search(Group::FOLLOWERS, $deny_groups);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$l[] = '<b><strike>' . L10n::t('Followers') . '</strike></b>';
 | 
			
		||||
			unset($deny_groups[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$key = array_search(Group::MUTUALS, $deny_groups);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$l[] = '<b><strike>' . L10n::t('Mutuals') . '</strike></b>';
 | 
			
		||||
			unset($deny_groups[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
 | 
			
		||||
			DBA::escape(implode(', ', $deny_groups))
 | 
			
		||||
		);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -643,7 +643,7 @@ function networkThreadedView(App $a, $update, $parent)
 | 
			
		|||
			// NOTREACHED
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$contacts = Group::expand([$gid]);
 | 
			
		||||
		$contacts = Group::expand(local_user(), [$gid]);
 | 
			
		||||
 | 
			
		||||
		if ((is_array($contacts)) && count($contacts)) {
 | 
			
		||||
			$contact_str_self = '';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,6 +94,7 @@ class Router
 | 
			
		|||
		$this->routeCollector->addRoute(['GET'],         '/attach/{item:\d+}',   Module\Attach::class);
 | 
			
		||||
		$this->routeCollector->addRoute(['GET'],         '/babel',               Module\Debug\Babel::class);
 | 
			
		||||
		$this->routeCollector->addRoute(['GET'],         '/bookmarklet',         Module\Bookmarklet::class);
 | 
			
		||||
		$this->routeCollector->addRoute(['GET', 'POST'], '/compose[/{type}]',    Module\Item\Compose::class);
 | 
			
		||||
		$this->routeCollector->addGroup('/contact', function (RouteCollector $collector) {
 | 
			
		||||
			$collector->addRoute(['GET'], '[/]',                                 Module\Contact::class);
 | 
			
		||||
			$collector->addRoute(['GET', 'POST'], '/{id:\d+}[/]',                Module\Contact::class);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,8 @@ class Protocol
 | 
			
		|||
 | 
			
		||||
	const FEDERATED = [self::DFRN, self::DIASPORA, self::OSTATUS, self::ACTIVITYPUB];
 | 
			
		||||
 | 
			
		||||
	const SUPPORT_PRIVATE = [self::DFRN, self::DIASPORA, self::MAIL, self::ACTIVITYPUB, self::PUMPIO];
 | 
			
		||||
 | 
			
		||||
	// Supported through a connector
 | 
			
		||||
	const DIASPORA2 = 'dspc';    // Diaspora connector
 | 
			
		||||
	const LINKEDIN  = 'lnkd';    // LinkedIn
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,12 +2,14 @@
 | 
			
		|||
/**
 | 
			
		||||
 * @file src/Model/Group.php
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Model;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseModule;
 | 
			
		||||
use Friendica\BaseObject;
 | 
			
		||||
use Friendica\Core\L10n;
 | 
			
		||||
use Friendica\Core\Logger;
 | 
			
		||||
use Friendica\Core\Protocol;
 | 
			
		||||
use Friendica\Core\Renderer;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,9 +18,21 @@ use Friendica\Database\DBA;
 | 
			
		|||
 */
 | 
			
		||||
class Group extends BaseObject
 | 
			
		||||
{
 | 
			
		||||
	const FOLLOWERS = '~';
 | 
			
		||||
	const MUTUALS = '&';
 | 
			
		||||
 | 
			
		||||
	public static function getByUserId($uid, $includesDeleted = false)
 | 
			
		||||
	{
 | 
			
		||||
		$conditions = ['uid' => $uid];
 | 
			
		||||
 | 
			
		||||
		if (!$includesDeleted) {
 | 
			
		||||
			$conditions['deleted'] = false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return DBA::selectToArray('group', [], $conditions);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 *
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int $group_id
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
| 
						 | 
				
			
			@ -76,8 +90,8 @@ class Group extends BaseObject
 | 
			
		|||
	/**
 | 
			
		||||
	 * Update group information.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param  int    $id   Group ID
 | 
			
		||||
	 * @param  string $name Group name
 | 
			
		||||
	 * @param int    $id   Group ID
 | 
			
		||||
	 * @param string $name Group name
 | 
			
		||||
	 *
 | 
			
		||||
	 * @return bool Was the update successful?
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
| 
						 | 
				
			
			@ -96,14 +110,13 @@ class Group extends BaseObject
 | 
			
		|||
	 */
 | 
			
		||||
	public static function getIdsByContactId($cid)
 | 
			
		||||
	{
 | 
			
		||||
		$condition = ['contact-id' => $cid];
 | 
			
		||||
		$stmt = DBA::select('group_member', ['gid'], $condition);
 | 
			
		||||
 | 
			
		||||
		$return = [];
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group_member', ['gid'], ['contact-id' => $cid]);
 | 
			
		||||
		while ($group = DBA::fetch($stmt)) {
 | 
			
		||||
			$return[] = $group['gid'];
 | 
			
		||||
		}
 | 
			
		||||
		DBA::close($stmt);
 | 
			
		||||
 | 
			
		||||
		return $return;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -170,8 +183,9 @@ class Group extends BaseObject
 | 
			
		|||
	 * @return boolean
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public static function remove($gid) {
 | 
			
		||||
		if (! $gid) {
 | 
			
		||||
	public static function remove($gid)
 | 
			
		||||
	{
 | 
			
		||||
		if (!$gid) {
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -215,14 +229,15 @@ class Group extends BaseObject
 | 
			
		|||
	/**
 | 
			
		||||
	 * @brief      Mark a group as deleted based on its name
 | 
			
		||||
	 *
 | 
			
		||||
	 * @deprecated Use Group::remove instead
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int    $uid
 | 
			
		||||
	 * @param string $name
 | 
			
		||||
	 * @return bool
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 * @deprecated Use Group::remove instead
 | 
			
		||||
	 *
 | 
			
		||||
	 */
 | 
			
		||||
	public static function removeByName($uid, $name) {
 | 
			
		||||
	public static function removeByName($uid, $name)
 | 
			
		||||
	{
 | 
			
		||||
		$return = false;
 | 
			
		||||
		if (!empty($uid) && !empty($name)) {
 | 
			
		||||
			$gid = self::getIdByName($uid, $name);
 | 
			
		||||
| 
						 | 
				
			
			@ -280,13 +295,13 @@ class Group extends BaseObject
 | 
			
		|||
	/**
 | 
			
		||||
	 * @brief      Removes a contact from a group based on its name
 | 
			
		||||
	 *
 | 
			
		||||
	 * @deprecated Use Group::removeMember instead
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int    $uid
 | 
			
		||||
	 * @param string $name
 | 
			
		||||
	 * @param int    $cid
 | 
			
		||||
	 * @return boolean
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 * @deprecated Use Group::removeMember instead
 | 
			
		||||
	 *
 | 
			
		||||
	 */
 | 
			
		||||
	public static function removeMemberByName($uid, $name, $cid)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -300,23 +315,55 @@ class Group extends BaseObject
 | 
			
		|||
	/**
 | 
			
		||||
	 * @brief Returns the combined list of contact ids from a group id list
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param int     $uid
 | 
			
		||||
	 * @param array   $group_ids
 | 
			
		||||
	 * @param boolean $check_dead
 | 
			
		||||
	 * @return array
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public static function expand($group_ids, $check_dead = false)
 | 
			
		||||
	public static function expand($uid, array $group_ids, $check_dead = false)
 | 
			
		||||
	{
 | 
			
		||||
		if (!is_array($group_ids) || !count($group_ids)) {
 | 
			
		||||
			return [];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group_member', ['contact-id'], ['gid' => $group_ids]);
 | 
			
		||||
 | 
			
		||||
		$return = [];
 | 
			
		||||
		while($group_member = DBA::fetch($stmt)) {
 | 
			
		||||
 | 
			
		||||
		$key = array_search(self::FOLLOWERS, $group_ids);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$followers = Contact::selectToArray(['id'], [
 | 
			
		||||
				'uid' => $uid,
 | 
			
		||||
				'rel' => [Contact::FOLLOWER, Contact::FRIEND],
 | 
			
		||||
				'protocol' => Protocol::SUPPORT_PRIVATE,
 | 
			
		||||
			]);
 | 
			
		||||
 | 
			
		||||
			foreach ($followers as $follower) {
 | 
			
		||||
				$return[] = $follower['id'];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unset($group_ids[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$key = array_search(self::MUTUALS, $group_ids);
 | 
			
		||||
		if ($key !== false) {
 | 
			
		||||
			$mutuals = Contact::selectToArray(['id'], [
 | 
			
		||||
				'uid' => $uid,
 | 
			
		||||
				'rel' => [Contact::FRIEND],
 | 
			
		||||
				'protocol' => Protocol::SUPPORT_PRIVATE,
 | 
			
		||||
			]);
 | 
			
		||||
 | 
			
		||||
			foreach ($mutuals as $mutual) {
 | 
			
		||||
				$return[] = $mutual['id'];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			unset($group_ids[$key]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group_member', ['contact-id'], ['gid' => $group_ids]);
 | 
			
		||||
		while ($group_member = DBA::fetch($stmt)) {
 | 
			
		||||
			$return[] = $group_member['contact-id'];
 | 
			
		||||
		}
 | 
			
		||||
		DBA::close($stmt);
 | 
			
		||||
 | 
			
		||||
		if ($check_dead) {
 | 
			
		||||
			Contact::pruneUnavailable($return);
 | 
			
		||||
| 
						 | 
				
			
			@ -332,12 +379,10 @@ class Group extends BaseObject
 | 
			
		|||
	 * @param int    $gid   An optional pre-selected group
 | 
			
		||||
	 * @param string $label An optional label of the list
 | 
			
		||||
	 * @return string
 | 
			
		||||
	 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public static function displayGroupSelection($uid, $gid = 0, $label = '')
 | 
			
		||||
	{
 | 
			
		||||
		$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => $uid], ['order' => ['name']]);
 | 
			
		||||
 | 
			
		||||
		$display_groups = [
 | 
			
		||||
			[
 | 
			
		||||
				'name' => '',
 | 
			
		||||
| 
						 | 
				
			
			@ -345,6 +390,8 @@ class Group extends BaseObject
 | 
			
		|||
				'selected' => ''
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => $uid], ['order' => ['name']]);
 | 
			
		||||
		while ($group = DBA::fetch($stmt)) {
 | 
			
		||||
			$display_groups[] = [
 | 
			
		||||
				'name' => $group['name'],
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +399,9 @@ class Group extends BaseObject
 | 
			
		|||
				'selected' => $gid == $group['id'] ? 'true' : ''
 | 
			
		||||
			];
 | 
			
		||||
		}
 | 
			
		||||
		Logger::log('groups: ' . print_r($display_groups, true));
 | 
			
		||||
		DBA::close($stmt);
 | 
			
		||||
 | 
			
		||||
		Logger::info('Got groups', $display_groups);
 | 
			
		||||
 | 
			
		||||
		if ($label == '') {
 | 
			
		||||
			$label = L10n::t('Default privacy group for new contacts');
 | 
			
		||||
| 
						 | 
				
			
			@ -377,7 +426,7 @@ class Group extends BaseObject
 | 
			
		|||
	 * @param string $group_id
 | 
			
		||||
	 * @param int    $cid
 | 
			
		||||
	 * @return string
 | 
			
		||||
	 * @throws \Friendica\Network\HTTPException\InternalServerErrorException
 | 
			
		||||
	 * @throws \Exception
 | 
			
		||||
	 */
 | 
			
		||||
	public static function sidebarWidget($every = 'contact', $each = 'group', $editmode = 'standard', $group_id = '', $cid = 0)
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -394,13 +443,12 @@ class Group extends BaseObject
 | 
			
		|||
			]
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => local_user()], ['order' => ['name']]);
 | 
			
		||||
 | 
			
		||||
		$member_of = [];
 | 
			
		||||
		if ($cid) {
 | 
			
		||||
			$member_of = self::getIdsByContactId($cid);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$stmt = DBA::select('group', [], ['deleted' => 0, 'uid' => local_user()], ['order' => ['name']]);
 | 
			
		||||
		while ($group = DBA::fetch($stmt)) {
 | 
			
		||||
			$selected = (($group_id == $group['id']) ? ' group-selected' : '');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -423,6 +471,7 @@ class Group extends BaseObject
 | 
			
		|||
				'ismember' => in_array($group['id'], $member_of),
 | 
			
		||||
			];
 | 
			
		||||
		}
 | 
			
		||||
		DBA::close($stmt);
 | 
			
		||||
 | 
			
		||||
		// Don't show the groups on the network page when there is only one
 | 
			
		||||
		if ((count($display_groups) <= 2) && ($each == 'network')) {
 | 
			
		||||
| 
						 | 
				
			
			@ -445,7 +494,6 @@ class Group extends BaseObject
 | 
			
		|||
			'$form_security_token' => BaseModule::getFormSecurityToken('group_edit'),
 | 
			
		||||
		]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		return $o;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2802,7 +2802,7 @@ class Item extends BaseObject
 | 
			
		|||
									$replace = true;
 | 
			
		||||
								}
 | 
			
		||||
							} elseif ($item) {
 | 
			
		||||
								if (self::samePermissions($item, $photo)) {
 | 
			
		||||
								if (self::samePermissions($uid, $item, $photo)) {
 | 
			
		||||
									$replace = true;
 | 
			
		||||
								}
 | 
			
		||||
							}
 | 
			
		||||
| 
						 | 
				
			
			@ -2852,7 +2852,7 @@ class Item extends BaseObject
 | 
			
		|||
			!empty($obj['deny_cid']) || !empty($obj['deny_gid']);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static function samePermissions($obj1, $obj2)
 | 
			
		||||
	private static function samePermissions($uid, $obj1, $obj2)
 | 
			
		||||
	{
 | 
			
		||||
		// first part is easy. Check that these are exactly the same.
 | 
			
		||||
		if (($obj1['allow_cid'] == $obj2['allow_cid'])
 | 
			
		||||
| 
						 | 
				
			
			@ -2873,12 +2873,12 @@ class Item extends BaseObject
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// returns an array of contact-ids that are allowed to see this object
 | 
			
		||||
	public static function enumeratePermissions($obj)
 | 
			
		||||
	public static function enumeratePermissions(array $obj)
 | 
			
		||||
	{
 | 
			
		||||
		$allow_people = expand_acl($obj['allow_cid']);
 | 
			
		||||
		$allow_groups = Group::expand(expand_acl($obj['allow_gid']));
 | 
			
		||||
		$allow_groups = Group::expand($obj['uid'], expand_acl($obj['allow_gid']));
 | 
			
		||||
		$deny_people  = expand_acl($obj['deny_cid']);
 | 
			
		||||
		$deny_groups  = Group::expand(expand_acl($obj['deny_gid']));
 | 
			
		||||
		$deny_groups  = Group::expand($obj['uid'], expand_acl($obj['deny_gid']));
 | 
			
		||||
		$recipients   = array_unique(array_merge($allow_people, $allow_groups));
 | 
			
		||||
		$deny         = array_unique(array_merge($deny_people, $deny_groups));
 | 
			
		||||
		$recipients   = array_diff($recipients, $deny);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										217
									
								
								src/Module/Item/Compose.php
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/Module/Item/Compose.php
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,217 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Friendica\Module\Item;
 | 
			
		||||
 | 
			
		||||
use Friendica\BaseModule;
 | 
			
		||||
use Friendica\Content\Feature;
 | 
			
		||||
use Friendica\Core\Config;
 | 
			
		||||
use Friendica\Core\Hook;
 | 
			
		||||
use Friendica\Core\L10n;
 | 
			
		||||
use Friendica\Core\Renderer;
 | 
			
		||||
use Friendica\Database\DBA;
 | 
			
		||||
use Friendica\Model\Contact;
 | 
			
		||||
use Friendica\Model\FileTag;
 | 
			
		||||
use Friendica\Model\Group;
 | 
			
		||||
use Friendica\Model\Item;
 | 
			
		||||
use Friendica\Model\User;
 | 
			
		||||
use Friendica\Module\Login;
 | 
			
		||||
use Friendica\Network\HTTPException\NotImplementedException;
 | 
			
		||||
use Friendica\Util\Crypto;
 | 
			
		||||
 | 
			
		||||
class Compose extends BaseModule
 | 
			
		||||
{
 | 
			
		||||
	public static function post()
 | 
			
		||||
	{
 | 
			
		||||
		if (!empty($_REQUEST['body'])) {
 | 
			
		||||
			$_REQUEST['return'] = 'network';
 | 
			
		||||
			require_once 'mod/item.php';
 | 
			
		||||
			item_post(self::getApp());
 | 
			
		||||
		} else {
 | 
			
		||||
			notice(L10n::t('Please enter a post body.'));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static function content()
 | 
			
		||||
	{
 | 
			
		||||
		if (!local_user()) {
 | 
			
		||||
			return Login::form('compose', false);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$a = self::getApp();
 | 
			
		||||
 | 
			
		||||
		if ($a->getCurrentTheme() !== 'frio') {
 | 
			
		||||
			throw new NotImplementedException(L10n::t('This feature is only available with the frio theme.'));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/// @TODO Retrieve parameter from router
 | 
			
		||||
		$posttype = $a->argv[1] ?? Item::PT_ARTICLE;
 | 
			
		||||
		if (!in_array($posttype, [Item::PT_ARTICLE, Item::PT_PERSONAL_NOTE])) {
 | 
			
		||||
			switch ($posttype) {
 | 
			
		||||
				case 'note':
 | 
			
		||||
					$posttype = Item::PT_PERSONAL_NOTE;
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					$posttype = Item::PT_ARTICLE;
 | 
			
		||||
					break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$user = User::getById(local_user(), ['allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'hidewall', 'default-location']);
 | 
			
		||||
 | 
			
		||||
		switch ($posttype) {
 | 
			
		||||
			case Item::PT_PERSONAL_NOTE:
 | 
			
		||||
				$compose_title = L10n::t('Compose new personal note');
 | 
			
		||||
				$type = 'note';
 | 
			
		||||
				$doesFederate = false;
 | 
			
		||||
				$contact_allow = $a->contact['id'];
 | 
			
		||||
				$group_allow = '';
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				$compose_title = L10n::t('Compose new post');
 | 
			
		||||
				$type = 'post';
 | 
			
		||||
				$doesFederate = true;
 | 
			
		||||
				$contact_allow = implode(',', expand_acl($user['allow_cid']));
 | 
			
		||||
				$group_allow = implode(',', expand_acl($user['allow_gid'])) ?: Group::FOLLOWERS;
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$title         = $_REQUEST['title']         ?? '';
 | 
			
		||||
		$category      = $_REQUEST['category']      ?? '';
 | 
			
		||||
		$body          = $_REQUEST['body']          ?? '';
 | 
			
		||||
		$location      = $_REQUEST['location']      ?? $user['default-location'];
 | 
			
		||||
		$wall          = $_REQUEST['wall']          ?? $type == 'post';
 | 
			
		||||
		$contact_allow = $_REQUEST['contact_allow'] ?? $contact_allow;
 | 
			
		||||
		$group_allow   = $_REQUEST['group_allow']   ?? $group_allow;
 | 
			
		||||
		$contact_deny  = $_REQUEST['contact_deny']  ?? implode(',', expand_acl($user['deny_cid']));
 | 
			
		||||
		$group_deny    = $_REQUEST['group_deny']    ?? implode(',', expand_acl($user['deny_gid']));
 | 
			
		||||
		$visibility    = ($contact_allow . $user['allow_gid'] . $user['deny_cid'] . $user['deny_gid']) ? 'custom' : 'public';
 | 
			
		||||
 | 
			
		||||
		$acl_contacts = Contact::selectToArray(['id', 'name', 'addr', 'micro'], ['uid' => local_user(), 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]);
 | 
			
		||||
		array_walk($acl_contacts, function (&$value) {
 | 
			
		||||
			$value['type'] = 'contact';
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$acl_groups = [
 | 
			
		||||
			[
 | 
			
		||||
				'id' => Group::FOLLOWERS,
 | 
			
		||||
				'name' => L10n::t('Followers'),
 | 
			
		||||
				'addr' => '',
 | 
			
		||||
				'micro' => 'images/twopeople.png',
 | 
			
		||||
				'type' => 'group',
 | 
			
		||||
			],
 | 
			
		||||
			[
 | 
			
		||||
				'id' => Group::MUTUALS,
 | 
			
		||||
				'name' => L10n::t('Mutuals'),
 | 
			
		||||
				'addr' => '',
 | 
			
		||||
				'micro' => 'images/twopeople.png',
 | 
			
		||||
				'type' => 'group',
 | 
			
		||||
			]
 | 
			
		||||
		];
 | 
			
		||||
		foreach (Group::getByUserId(local_user()) as $group) {
 | 
			
		||||
			$acl_groups[] = [
 | 
			
		||||
				'id' => $group['id'],
 | 
			
		||||
				'name' => $group['name'],
 | 
			
		||||
				'addr' => '',
 | 
			
		||||
				'micro' => 'images/twopeople.png',
 | 
			
		||||
				'type' => 'group',
 | 
			
		||||
			];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$acl = array_merge($acl_groups, $acl_contacts);
 | 
			
		||||
 | 
			
		||||
		$jotnets_fields = [];
 | 
			
		||||
		$mail_enabled = false;
 | 
			
		||||
		$pubmail_enabled = false;
 | 
			
		||||
		if (function_exists('imap_open') && !Config::get('system', 'imap_disabled')) {
 | 
			
		||||
			$mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
 | 
			
		||||
			if (DBA::isResult($mailacct)) {
 | 
			
		||||
				$mail_enabled = true;
 | 
			
		||||
				$pubmail_enabled = !empty($mailacct['pubmail']);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (empty($user['hidewall'])) {
 | 
			
		||||
			if ($mail_enabled) {
 | 
			
		||||
				$jotnets_fields[] = [
 | 
			
		||||
					'type' => 'checkbox',
 | 
			
		||||
					'field' => [
 | 
			
		||||
						'pubmail_enable',
 | 
			
		||||
						L10n::t('Post to Email'),
 | 
			
		||||
						$pubmail_enabled
 | 
			
		||||
					]
 | 
			
		||||
				];
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Hook::callAll('jot_networks', $jotnets_fields);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$jotplugins = '';
 | 
			
		||||
		Hook::callAll('jot_tool', $jotplugins);
 | 
			
		||||
 | 
			
		||||
		// Output
 | 
			
		||||
 | 
			
		||||
		$a->registerFooterScript('view/js/ajaxupload.js');
 | 
			
		||||
		$a->registerFooterScript('view/js/linkPreview.js');
 | 
			
		||||
		$a->registerFooterScript('view/asset/typeahead.js/dist/typeahead.bundle.js');
 | 
			
		||||
		$a->registerFooterScript('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput.js');
 | 
			
		||||
		$a->registerStylesheet('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput.css');
 | 
			
		||||
		$a->registerStylesheet('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput-typeahead.css');
 | 
			
		||||
 | 
			
		||||
		$tpl = Renderer::getMarkupTemplate('item/compose-footer.tpl');
 | 
			
		||||
		$a->page['footer'] .= Renderer::replaceMacros($tpl, [
 | 
			
		||||
			'$acl_contacts' => $acl_contacts,
 | 
			
		||||
			'$acl_groups' => $acl_groups,
 | 
			
		||||
			'$acl' => $acl,
 | 
			
		||||
		]);
 | 
			
		||||
 | 
			
		||||
		$tpl = Renderer::getMarkupTemplate('item/compose.tpl');
 | 
			
		||||
		return Renderer::replaceMacros($tpl, [
 | 
			
		||||
			'$compose_title'=> $compose_title,
 | 
			
		||||
			'$id'           => 0,
 | 
			
		||||
			'$posttype'     => $posttype,
 | 
			
		||||
			'$type'         => $type,
 | 
			
		||||
			'$wall'         => $wall,
 | 
			
		||||
			'$default'      => L10n::t(''),
 | 
			
		||||
			'$mylink'       => $a->removeBaseURL($a->contact['url']),
 | 
			
		||||
			'$mytitle'      => L10n::t('This is you'),
 | 
			
		||||
			'$myphoto'      => $a->removeBaseURL($a->contact['thumb']),
 | 
			
		||||
			'$submit'       => L10n::t('Submit'),
 | 
			
		||||
			'$edbold'       => L10n::t('Bold'),
 | 
			
		||||
			'$editalic'     => L10n::t('Italic'),
 | 
			
		||||
			'$eduline'      => L10n::t('Underline'),
 | 
			
		||||
			'$edquote'      => L10n::t('Quote'),
 | 
			
		||||
			'$edcode'       => L10n::t('Code'),
 | 
			
		||||
			'$edimg'        => L10n::t('Image'),
 | 
			
		||||
			'$edurl'        => L10n::t('Link'),
 | 
			
		||||
			'$edattach'     => L10n::t('Link or Media'),
 | 
			
		||||
			'$prompttext'   => L10n::t('Please enter a image/video/audio/webpage URL:'),
 | 
			
		||||
			'$preview'      => L10n::t('Preview'),
 | 
			
		||||
			'$location_set' => L10n::t('Set your location'),
 | 
			
		||||
			'$location_clear' => L10n::t('Clear the location'),
 | 
			
		||||
			'$location_unavailable' => L10n::t('Location services are unavailable on your device'),
 | 
			
		||||
			'$location_disabled' => L10n::t('Location services are disabled. Please check the website\'s permissions on your device'),
 | 
			
		||||
			'$wait'         => L10n::t('Please wait'),
 | 
			
		||||
			'$placeholdertitle' => L10n::t('Set title'),
 | 
			
		||||
			'$placeholdercategory' => (Feature::isEnabled(local_user(),'categories') ? L10n::t('Categories (comma-separated list)') : ''),
 | 
			
		||||
			'$public_title'  => L10n::t('Public'),
 | 
			
		||||
			'$public_desc'  => L10n::t('This post will be sent to all your followers and can be seen in the community pages and by anyone with its link.'),
 | 
			
		||||
			'$custom_title' => L10n::t('Limited/Private'),
 | 
			
		||||
			'$custom_desc'  => L10n::t('This post will be sent only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.'),
 | 
			
		||||
			'$emailcc'      => L10n::t('CC: email addresses'),
 | 
			
		||||
			'$title'        => $title,
 | 
			
		||||
			'$category'     => $category,
 | 
			
		||||
			'$body'         => $body,
 | 
			
		||||
			'$location'     => $location,
 | 
			
		||||
			'$visibility'   => $visibility,
 | 
			
		||||
			'$contact_allow'=> $contact_allow,
 | 
			
		||||
			'$group_allow'  => $group_allow,
 | 
			
		||||
			'$contact_deny' => $contact_deny,
 | 
			
		||||
			'$group_deny'   => $group_deny,
 | 
			
		||||
			'$jotplugins'   => $jotplugins,
 | 
			
		||||
			'$doesFederate' => $doesFederate,
 | 
			
		||||
			'$jotnets_fields'=> $jotnets_fields,
 | 
			
		||||
			'$sourceapp'    => L10n::t($a->sourcename),
 | 
			
		||||
			'$rand_num'     => Crypto::randomDigits(12)
 | 
			
		||||
		]);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -271,9 +271,9 @@ class Notifier
 | 
			
		|||
				}
 | 
			
		||||
 | 
			
		||||
				$allow_people = expand_acl($parent['allow_cid']);
 | 
			
		||||
				$allow_groups = Group::expand(expand_acl($parent['allow_gid']),true);
 | 
			
		||||
				$allow_groups = Group::expand($uid, expand_acl($parent['allow_gid']),true);
 | 
			
		||||
				$deny_people  = expand_acl($parent['deny_cid']);
 | 
			
		||||
				$deny_groups  = Group::expand(expand_acl($parent['deny_gid']));
 | 
			
		||||
				$deny_groups  = Group::expand($uid, expand_acl($parent['deny_gid']));
 | 
			
		||||
 | 
			
		||||
				// if our parent is a public forum (forum_mode == 1), uplink to the origional author causing
 | 
			
		||||
				// a delivery fork. private groups (forum_mode == 2) do not uplink
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@
 | 
			
		|||
 | 
			
		||||
namespace Friendica\Test;
 | 
			
		||||
 | 
			
		||||
use Friendica\Model\Group;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -55,8 +56,8 @@ class TextTest extends TestCase
 | 
			
		|||
	 */
 | 
			
		||||
	public function testExpandAclNormal()
 | 
			
		||||
	{
 | 
			
		||||
		$text='<1><2><3>';
 | 
			
		||||
		$this->assertEquals(array(1, 2, 3), expand_acl($text));
 | 
			
		||||
		$text='<1><2><3><' . Group::FOLLOWERS . '><' . Group::MUTUALS . '>';
 | 
			
		||||
		$this->assertEquals(array('1', '2', '3', Group::FOLLOWERS, Group::MUTUALS), expand_acl($text));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +65,8 @@ class TextTest extends TestCase
 | 
			
		|||
	 */
 | 
			
		||||
	public function testExpandAclBigNumber()
 | 
			
		||||
	{
 | 
			
		||||
		$text='<1><'.PHP_INT_MAX.'><15>';
 | 
			
		||||
		$this->assertEquals(array(1, PHP_INT_MAX, 15), expand_acl($text));
 | 
			
		||||
		$text='<1><' . PHP_INT_MAX . '><15>';
 | 
			
		||||
		$this->assertEquals(array('1', (string)PHP_INT_MAX, '15'), expand_acl($text));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +77,7 @@ class TextTest extends TestCase
 | 
			
		|||
	public function testExpandAclString()
 | 
			
		||||
	{
 | 
			
		||||
		$text="<1><279012><tt>";
 | 
			
		||||
		$this->assertEquals(array(1, 279012), expand_acl($text));
 | 
			
		||||
		$this->assertEquals(array('1', '279012'), expand_acl($text));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +88,7 @@ class TextTest extends TestCase
 | 
			
		|||
	public function testExpandAclSpace()
 | 
			
		||||
	{
 | 
			
		||||
		$text="<1><279 012><32>";
 | 
			
		||||
		$this->assertEquals(array(1, "279", "32"), expand_acl($text));
 | 
			
		||||
		$this->assertEquals(array('1', '32'), expand_acl($text));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +175,7 @@ class TextTest extends TestCase
 | 
			
		|||
	public function testExpandAclEmptyMatch()
 | 
			
		||||
	{
 | 
			
		||||
		$text="<1><><3>";
 | 
			
		||||
		$this->assertEquals(array(1,3), expand_acl($text));
 | 
			
		||||
		$this->assertEquals(array('1', '3'), expand_acl($text));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -632,7 +632,6 @@ function post_comment(id) {
 | 
			
		|||
	unpause();
 | 
			
		||||
	commentBusy = true;
 | 
			
		||||
	$('body').css('cursor', 'wait');
 | 
			
		||||
	$("#comment-preview-inp-" + id).val("0");
 | 
			
		||||
	$.post(
 | 
			
		||||
		"item",
 | 
			
		||||
		$("#comment-edit-form-" + id).serialize(),
 | 
			
		||||
| 
						 | 
				
			
			@ -661,11 +660,10 @@ function post_comment(id) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
function preview_comment(id) {
 | 
			
		||||
	$("#comment-preview-inp-" + id).val("1");
 | 
			
		||||
	$("#comment-edit-preview-" + id).show();
 | 
			
		||||
	$.post(
 | 
			
		||||
		"item",
 | 
			
		||||
		$("#comment-edit-form-" + id).serialize(),
 | 
			
		||||
		$("#comment-edit-form-" + id).serialize() + '&preview=1',
 | 
			
		||||
		function(data) {
 | 
			
		||||
			if (data.preview) {
 | 
			
		||||
				$("#comment-edit-preview-" + id).html(data.preview);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				{{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
				<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}" >
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										251
									
								
								view/templates/item/compose-footer.tpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								view/templates/item/compose-footer.tpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,251 @@
 | 
			
		|||
<script type="text/javascript">
 | 
			
		||||
	function updateLocationButtonDisplay(location_button, location_input)
 | 
			
		||||
	{
 | 
			
		||||
		location_button.classList.remove('btn-primary');
 | 
			
		||||
		if (location_input.value) {
 | 
			
		||||
			location_button.disabled = false;
 | 
			
		||||
			location_button.classList.add('btn-primary');
 | 
			
		||||
			location_button.title = location_button.dataset.titleClear;
 | 
			
		||||
		} else if (!"geolocation" in navigator) {
 | 
			
		||||
			location_button.disabled = true;
 | 
			
		||||
			location_button.title = location_button.dataset.titleUnavailable;
 | 
			
		||||
		} else if (location_button.disabled) {
 | 
			
		||||
			location_button.title = location_button.dataset.titleDisabled;
 | 
			
		||||
		} else {
 | 
			
		||||
			location_button.title = location_button.dataset.titleSet;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$(function() {
 | 
			
		||||
		// Jot attachment live preview.
 | 
			
		||||
		let $textarea = $('#comment-edit-text-0');
 | 
			
		||||
		$textarea.linkPreview();
 | 
			
		||||
		$textarea.keyup(function(){
 | 
			
		||||
			var textlen = $(this).val().length;
 | 
			
		||||
			$('#character-counter').text(textlen);
 | 
			
		||||
		});
 | 
			
		||||
		$textarea.editor_autocomplete(baseurl+"/acl");
 | 
			
		||||
		$textarea.bbco_autocomplete('bbcode');
 | 
			
		||||
 | 
			
		||||
		let $acl_allow_input = $('#acl_allow');
 | 
			
		||||
		let $group_allow_input = $('[name=group_allow]');
 | 
			
		||||
		let $contact_allow_input = $('[name=contact_allow]');
 | 
			
		||||
		let $acl_deny_input = $('#acl_deny');
 | 
			
		||||
		let $group_deny_input = $('[name=group_deny]');
 | 
			
		||||
		let $contact_deny_input = $('[name=contact_deny]');
 | 
			
		||||
 | 
			
		||||
		// Visibility accordion
 | 
			
		||||
 | 
			
		||||
		// Prevents open panel to collapse
 | 
			
		||||
		// @see https://stackoverflow.com/a/43593116
 | 
			
		||||
		$('[data-toggle="collapse"]').click(function(e) {
 | 
			
		||||
			target = $(this).attr('href');
 | 
			
		||||
			if ($(target).hasClass('in')) {
 | 
			
		||||
				e.preventDefault(); // to stop the page jump to the anchor target.
 | 
			
		||||
				e.stopPropagation()
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		// Accessibility: enable space and enter to open a panel when focused
 | 
			
		||||
		$('body').on('keyup', '[data-toggle="collapse"]:focus', function (e) {
 | 
			
		||||
			if (e.key === ' ' || e.key === 'Enter') {
 | 
			
		||||
				$(this).click();
 | 
			
		||||
				e.preventDefault();
 | 
			
		||||
				e.stopPropagation();
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$('#visibility-public-panel').on('show.bs.collapse', function() {
 | 
			
		||||
			$('#visibility-public').prop('checked', true);
 | 
			
		||||
			$group_allow_input.prop('disabled', true);
 | 
			
		||||
			$contact_allow_input.prop('disabled', true);
 | 
			
		||||
			$group_deny_input.prop('disabled', true);
 | 
			
		||||
			$contact_deny_input.prop('disabled', true);
 | 
			
		||||
 | 
			
		||||
			$('.profile-jot-net input[type=checkbox]').each(function() {
 | 
			
		||||
				// Restores checkbox state if it had been saved
 | 
			
		||||
				if ($(this).attr('data-checked') !== undefined) {
 | 
			
		||||
					$(this).prop('checked', $(this).attr('data-checked') === 'true');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			$('.profile-jot-net input').attr('disabled', false);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$('#visibility-custom-panel').on('show.bs.collapse', function() {
 | 
			
		||||
			$('#visibility-custom').prop('checked', true);
 | 
			
		||||
			$group_allow_input.prop('disabled', false);
 | 
			
		||||
			$contact_allow_input.prop('disabled', false);
 | 
			
		||||
			$group_deny_input.prop('disabled', false);
 | 
			
		||||
			$contact_deny_input.prop('disabled', false);
 | 
			
		||||
 | 
			
		||||
			$('.profile-jot-net input[type=checkbox]').each(function() {
 | 
			
		||||
				// Saves current checkbox state
 | 
			
		||||
				$(this)
 | 
			
		||||
					.attr('data-checked', $(this).prop('checked'))
 | 
			
		||||
					.prop('checked', false);
 | 
			
		||||
			});
 | 
			
		||||
			$('.profile-jot-net input').attr('disabled', 'disabled');
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		if (document.querySelector('input[name="visibility"]:checked').value === 'custom') {
 | 
			
		||||
			$('#visibility-custom-panel').collapse({parent: '#visibility-accordion'});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Custom visibility tags inputs
 | 
			
		||||
 | 
			
		||||
		let acl_groups = new Bloodhound({
 | 
			
		||||
			local: {{$acl_groups|@json_encode nofilter}},
 | 
			
		||||
			identify: function(obj) { return obj.id; },
 | 
			
		||||
			datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name']),
 | 
			
		||||
			queryTokenizer: Bloodhound.tokenizers.whitespace,
 | 
			
		||||
		});
 | 
			
		||||
		let acl_contacts = new Bloodhound({
 | 
			
		||||
			local: {{$acl_contacts|@json_encode nofilter}},
 | 
			
		||||
			identify: function(obj) { return obj.id; },
 | 
			
		||||
			datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
 | 
			
		||||
			queryTokenizer: Bloodhound.tokenizers.whitespace,
 | 
			
		||||
		});
 | 
			
		||||
		let acl = new Bloodhound({
 | 
			
		||||
			local: {{$acl|@json_encode nofilter}},
 | 
			
		||||
			identify: function(obj) { return obj.id; },
 | 
			
		||||
			datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
 | 
			
		||||
			queryTokenizer: Bloodhound.tokenizers.whitespace,
 | 
			
		||||
		});
 | 
			
		||||
		acl.initialize();
 | 
			
		||||
 | 
			
		||||
		let suggestionTemplate = function (item) {
 | 
			
		||||
			return '<div><img src="' + item.micro + '" alt="" style="float: left; width: auto; height: 2.8em; margin-right: 0.5em;"> <strong>' + item.name + '</strong><br /><em>' + item.addr + '</em></div>';
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		$acl_allow_input.tagsinput({
 | 
			
		||||
			confirmKeys: [13, 44],
 | 
			
		||||
			cancelConfirmKeysOnEmpty: true,
 | 
			
		||||
			freeInput: false,
 | 
			
		||||
			tagClass: function(item) {
 | 
			
		||||
				switch (item.type) {
 | 
			
		||||
					case 'group'   : return 'label label-primary';
 | 
			
		||||
					case 'contact'  :
 | 
			
		||||
					default:
 | 
			
		||||
						return 'label label-info';
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			itemValue: 'id',
 | 
			
		||||
			itemText: 'name',
 | 
			
		||||
			itemThumb: 'micro',
 | 
			
		||||
			itemTitle: function(item) {
 | 
			
		||||
				return item.addr;
 | 
			
		||||
			},
 | 
			
		||||
			typeaheadjs: {
 | 
			
		||||
				name: 'contacts',
 | 
			
		||||
				displayKey: 'name',
 | 
			
		||||
				templates: {
 | 
			
		||||
					suggestion: suggestionTemplate
 | 
			
		||||
				},
 | 
			
		||||
				source: acl.ttAdapter()
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$acl_deny_input
 | 
			
		||||
		.tagsinput({
 | 
			
		||||
			confirmKeys: [13, 44],
 | 
			
		||||
			freeInput: false,
 | 
			
		||||
			tagClass: function(item) {
 | 
			
		||||
				switch (item.type) {
 | 
			
		||||
					case 'group'   : return 'label label-primary';
 | 
			
		||||
					case 'contact'  :
 | 
			
		||||
					default:
 | 
			
		||||
						return 'label label-info';
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			itemValue: 'id',
 | 
			
		||||
			itemText: 'name',
 | 
			
		||||
			itemThumb: 'micro',
 | 
			
		||||
			itemTitle: function(item) {
 | 
			
		||||
				return item.addr;
 | 
			
		||||
			},
 | 
			
		||||
			typeaheadjs: {
 | 
			
		||||
				name: 'contacts',
 | 
			
		||||
				displayKey: 'name',
 | 
			
		||||
				templates: {
 | 
			
		||||
					suggestion: suggestionTemplate
 | 
			
		||||
				},
 | 
			
		||||
				source: acl.ttAdapter()
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Import existing ACL into the tags input fields.
 | 
			
		||||
 | 
			
		||||
		$group_allow_input.val().split(',').forEach(function (val) {
 | 
			
		||||
			$acl_allow_input.tagsinput('add', acl_groups.get(val)[0]);
 | 
			
		||||
		});
 | 
			
		||||
		$contact_allow_input.val().split(',').forEach(function (val) {
 | 
			
		||||
			$acl_allow_input.tagsinput('add', acl_contacts.get(val)[0]);
 | 
			
		||||
		});
 | 
			
		||||
		$group_deny_input.val().split(',').forEach(function (val) {
 | 
			
		||||
			$acl_deny_input.tagsinput('add', acl_groups.get(val)[0]);
 | 
			
		||||
		});
 | 
			
		||||
		$contact_deny_input.val().split(',').forEach(function (val) {
 | 
			
		||||
			$acl_deny_input.tagsinput('add', acl_contacts.get(val)[0]);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		// Anti-duplicate callback + acl fields value generation
 | 
			
		||||
 | 
			
		||||
		$acl_allow_input.on('itemAdded', function (event) {
 | 
			
		||||
			// Removes duplicate in the opposite acl box
 | 
			
		||||
			$acl_deny_input.tagsinput('remove', event.item);
 | 
			
		||||
 | 
			
		||||
			// Update the real acl field
 | 
			
		||||
			$group_allow_input.val('');
 | 
			
		||||
			$contact_allow_input.val('');
 | 
			
		||||
			[].forEach.call($acl_allow_input.tagsinput('items'), function (item) {
 | 
			
		||||
				if (item.type === 'group') {
 | 
			
		||||
					$group_allow_input.val($group_allow_input.val() + '<' + item.id + '>');
 | 
			
		||||
				} else {
 | 
			
		||||
					$contact_allow_input.val($contact_allow_input.val() + '<' + item.id + '>');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		$acl_deny_input.on('itemAdded', function (event) {
 | 
			
		||||
			// Removes duplicate in the opposite acl box
 | 
			
		||||
			$acl_allow_input.tagsinput('remove', event.item);
 | 
			
		||||
 | 
			
		||||
			// Update the real acl field
 | 
			
		||||
			$group_deny_input.val('');
 | 
			
		||||
			$contact_deny_input.val('');
 | 
			
		||||
			[].forEach.call($acl_deny_input.tagsinput('items'), function (item) {
 | 
			
		||||
				if (item.type === 'group') {
 | 
			
		||||
					$group_deny_input.val($group_allow_input.val() + '<' + item.id + '>');
 | 
			
		||||
				} else {
 | 
			
		||||
					$contact_deny_input.val($contact_allow_input.val() + '<' + item.id + '>');
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		let location_button = document.getElementById('profile-location');
 | 
			
		||||
		let location_input = document.getElementById('jot-location');
 | 
			
		||||
 | 
			
		||||
		updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
 | 
			
		||||
		location_input.addEventListener('change', function () {
 | 
			
		||||
			updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
		});
 | 
			
		||||
		location_input.addEventListener('keyup', function () {
 | 
			
		||||
			updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		location_button.addEventListener('click', function() {
 | 
			
		||||
			if (location_input.value) {
 | 
			
		||||
				location_input.value = '';
 | 
			
		||||
				updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
			} else if ("geolocation" in navigator) {
 | 
			
		||||
				navigator.geolocation.getCurrentPosition(function(position) {
 | 
			
		||||
					location_input.value = position.coords.latitude + ', ' + position.coords.longitude;
 | 
			
		||||
					updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
				}, function (error) {
 | 
			
		||||
					location_button.disabled = true;
 | 
			
		||||
					updateLocationButtonDisplay(location_button, location_input);
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	})
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										156
									
								
								view/templates/item/compose.tpl
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								view/templates/item/compose.tpl
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
<div class="generic-page-wrapper">
 | 
			
		||||
	<h2>{{$compose_title}}</h2>
 | 
			
		||||
	<div id="profile-jot-wrapper">
 | 
			
		||||
		<form class="comment-edit-form" data-item-id="{{$id}}" id="comment-edit-form-{{$id}}" action="compose/{{$type}}" method="post">
 | 
			
		||||
		    {{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
			<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
			<input type="hidden" name="post_type" value="{{$posttype}}" />
 | 
			
		||||
			<input type="hidden" name="wall" value="{{$wall}}" />
 | 
			
		||||
 | 
			
		||||
			<div id="jot-title-wrap">
 | 
			
		||||
				<input type="text" name="title" id="jot-title" class="jothidden jotforms form-control" placeholder="{{$placeholdertitle}}" title="{{$placeholdertitle}}" value="{{$title}}" tabindex="1"/>
 | 
			
		||||
			</div>
 | 
			
		||||
		    {{if $placeholdercategory}}
 | 
			
		||||
				<div id="jot-category-wrap">
 | 
			
		||||
					<input name="category" id="jot-category" class="jothidden jotforms form-control" type="text" placeholder="{{$placeholdercategory}}" title="{{$placeholdercategory}}" value="{{$category}}" tabindex="2"/>
 | 
			
		||||
				</div>
 | 
			
		||||
		    {{/if}}
 | 
			
		||||
 | 
			
		||||
			<p class="comment-edit-bb-{{$id}} comment-icon-list">
 | 
			
		||||
				<span>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon bb-img" aria-label="{{$edimg}}" title="{{$edimg}}" data-role="insert-formatting" data-bbcode="img" data-id="{{$id}}" tabindex="7">
 | 
			
		||||
						<i class="fa fa-picture-o"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon bb-attach" aria-label="{{$edattach}}" title="{{$edattach}}" ondragenter="return commentLinkDrop(event, {{$id}});" ondragover="return commentLinkDrop(event, {{$id}});" ondrop="commentLinkDropper(event);" onclick="commentGetLink({{$id}}, '{{$prompttext}}');" tabindex="8">
 | 
			
		||||
						<i class="fa fa-paperclip"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
				</span>
 | 
			
		||||
				<span>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon bb-url" aria-label="{{$edurl}}" title="{{$edurl}}" onclick="insertFormatting('url',{{$id}});" tabindex="9">
 | 
			
		||||
						<i class="fa fa-link"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon underline" aria-label="{{$eduline}}" title="{{$eduline}}" onclick="insertFormatting('u',{{$id}});" tabindex="10">
 | 
			
		||||
						<i class="fa fa-underline"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon italic" aria-label="{{$editalic}}" title="{{$editalic}}" onclick="insertFormatting('i',{{$id}});" tabindex="11">
 | 
			
		||||
						<i class="fa fa-italic"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon bold" aria-label="{{$edbold}}" title="{{$edbold}}" onclick="insertFormatting('b',{{$id}});" tabindex="12">
 | 
			
		||||
						<i class="fa fa-bold"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon quote" aria-label="{{$edquote}}" title="{{$edquote}}" onclick="insertFormatting('quote',{{$id}});" tabindex="13">
 | 
			
		||||
						<i class="fa fa-quote-left"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
				</span>
 | 
			
		||||
			</p>
 | 
			
		||||
			<p>
 | 
			
		||||
				<textarea id="comment-edit-text-{{$id}}" class="comment-edit-text form-control text-autosize" name="body" placeholder="{{$default}}" rows="7" tabindex="3">{{$body}}</textarea>
 | 
			
		||||
			</p>
 | 
			
		||||
 | 
			
		||||
			<p class="comment-edit-submit-wrapper">
 | 
			
		||||
{{if $type == 'post'}}
 | 
			
		||||
				<span role="presentation" class="form-inline">
 | 
			
		||||
					<input type="text" name="location" class="form-control" id="jot-location" value="{{$location}}" placeholder="{{$location_set}}"/>
 | 
			
		||||
					<button type="button" class="btn btn-sm icon" id="profile-location"
 | 
			
		||||
					        data-title-set="{{$location_set}}"
 | 
			
		||||
					        data-title-disabled="{{$location_disabled}}"
 | 
			
		||||
					        data-title-unavailable="{{$location_unavailable}}"
 | 
			
		||||
					        data-title-clear="{{$location_clear}}"
 | 
			
		||||
					        title="{{$location_set}}"
 | 
			
		||||
					        tabindex="6">
 | 
			
		||||
						<i class="fa fa-map-marker" aria-hidden="true"></i>
 | 
			
		||||
					</button>
 | 
			
		||||
				</span>
 | 
			
		||||
{{/if}}
 | 
			
		||||
				<span role="presentation" id="profile-rotator-wrapper">
 | 
			
		||||
					<img role="presentation" id="profile-rotator" src="images/rotator.gif" alt="{{$wait}}" title="{{$wait}}" style="display: none;" />
 | 
			
		||||
				</span>
 | 
			
		||||
				<span role="presentation" id="character-counter" class="grey text-info"></span>
 | 
			
		||||
		        {{if $preview}}
 | 
			
		||||
					<button type="button" class="btn btn-defaul btn-sm" onclick="preview_comment({{$id}});" id="comment-edit-preview-link-{{$id}}" tabindex="5"><i class="fa fa-eye"></i> {{$preview}}</button>
 | 
			
		||||
		        {{/if}}
 | 
			
		||||
				<button type="submit" class="btn btn-primary btn-sm" id="comment-edit-submit-{{$id}}" name="submit" tabindex="4"><i class="fa fa-envelope"></i> {{$submit}}</button>
 | 
			
		||||
			</p>
 | 
			
		||||
 | 
			
		||||
			<div id="comment-edit-preview-{{$id}}" class="comment-edit-preview" style="display:none;"></div>
 | 
			
		||||
 | 
			
		||||
			<input type="hidden" name="group_allow" value="{{$group_allow}}" {{if $visibility == 'public'}}disabled{{/if}}/>
 | 
			
		||||
			<input type="hidden" name="contact_allow" value="{{$contact_allow}}" {{if $visibility == 'public'}}disabled{{/if}}/>
 | 
			
		||||
			<input type="hidden" name="group_deny" value="{{$group_deny}}" {{if $visibility == 'public'}}disabled{{/if}}/>
 | 
			
		||||
			<input type="hidden" name="contact_deny" value="{{$contact_deny}}" {{if $visibility == 'public'}}disabled{{/if}}/>
 | 
			
		||||
{{if $type == 'post'}}
 | 
			
		||||
			<h3>Visibility</h3>
 | 
			
		||||
			<div class="panel-group" id="visibility-accordion" role="tablist" aria-multiselectable="true">
 | 
			
		||||
				<div class="panel panel-success">
 | 
			
		||||
					<div class="panel-heading" id="visibility-public-heading" role="button" data-toggle="collapse" data-parent="#visibility-accordion" href="#visibility-public-panel" aria-expanded="true" aria-controls="visibility-public-panel" tabindex="14">
 | 
			
		||||
						<label>
 | 
			
		||||
							<input type="radio" name="visibility" id="visibility-public" value="public" {{if $visibility == 'public'}}checked{{/if}} style="display:none">
 | 
			
		||||
							<i class="fa fa-globe"></i> {{$public_title}}
 | 
			
		||||
						</label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div id="visibility-public-panel" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="visibility-public-heading">
 | 
			
		||||
						<div class="panel-body">
 | 
			
		||||
							<p>{{$public_desc}}</p>
 | 
			
		||||
                    {{if $doesFederate && $jotnets_fields}}
 | 
			
		||||
                        {{if $jotnets_fields|count < 3}}
 | 
			
		||||
							<div class="profile-jot-net">
 | 
			
		||||
                        {{else}}
 | 
			
		||||
							<details class="profile-jot-net">
 | 
			
		||||
							<summary>{{$jotnets_summary}}</summary>
 | 
			
		||||
                        {{/if}}
 | 
			
		||||
 | 
			
		||||
                        {{foreach $jotnets_fields as $jotnets_field}}
 | 
			
		||||
                            {{if $jotnets_field.type == 'checkbox'}}
 | 
			
		||||
                                {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
 | 
			
		||||
                            {{elseif $jotnets_field.type == 'select'}}
 | 
			
		||||
                                {{include file="field_select.tpl" field=$jotnets_field.field}}
 | 
			
		||||
                            {{/if}}
 | 
			
		||||
                        {{/foreach}}
 | 
			
		||||
 | 
			
		||||
                        {{if $jotnets_fields|count >= 3}}
 | 
			
		||||
							</details>
 | 
			
		||||
                        {{else}}
 | 
			
		||||
							</div>
 | 
			
		||||
                        {{/if}}
 | 
			
		||||
                    {{/if}}
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
				<div class="panel panel-info">
 | 
			
		||||
					<div class="panel-heading collapsed" id="visibility-custom-heading" role="button" data-toggle="collapse" data-parent="#visibility-accordion" href="#visibility-custom-panel" aria-expanded="true" aria-controls="visibility-custom-panel" tabindex="15">
 | 
			
		||||
						<label>
 | 
			
		||||
							<input type="radio" name="visibility" id="visibility-custom" value="custom" {{if $visibility == 'custom'}}checked{{/if}} style="display:none">
 | 
			
		||||
							<i class="fa fa-lock"></i> {{$custom_title}}
 | 
			
		||||
						</label>
 | 
			
		||||
					</div>
 | 
			
		||||
					<div id="visibility-custom-panel" class="panel-collapse collapse" role="tabpanel" aria-labelledby="visibility-custom-heading">
 | 
			
		||||
						<div class="panel-body">
 | 
			
		||||
							<p>{{$custom_desc}}</p>
 | 
			
		||||
 | 
			
		||||
							<div class="form-group">
 | 
			
		||||
								<label for="acl_allow">Deliver to:</label>
 | 
			
		||||
								<input type="text" class="form-control input-lg" id="acl_allow">
 | 
			
		||||
							</div>
 | 
			
		||||
 | 
			
		||||
							<div class="form-group">
 | 
			
		||||
								<label for="acl_deny">Except to:</label>
 | 
			
		||||
								<input type="text" class="form-control input-lg" id="acl_deny">
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
        {{if $doesFederate}}
 | 
			
		||||
			<div class="form-group">
 | 
			
		||||
				<label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
 | 
			
		||||
				<input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
 | 
			
		||||
			</div>
 | 
			
		||||
			<div id="profile-jot-email-end"></div>
 | 
			
		||||
		{{/if}}
 | 
			
		||||
			<div class="jotplugins">
 | 
			
		||||
				{{$jotplugins nofilter}}
 | 
			
		||||
			</div>
 | 
			
		||||
{{/if}}
 | 
			
		||||
		</form>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				<input type="hidden" name="return" value="{{$return_path}}" />
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}">
 | 
			
		||||
					<a class="comment-edit-photo-link" href="{{$mylink}}" title="{{$mytitle}}"><img class="my-comment-photo" src="{{$myphoto}}" alt="{{$mytitle}}" title="{{$mytitle}}" /></a>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				{{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
				<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				<input type="hidden" name="return" value="{{$return_path}}" />
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}">
 | 
			
		||||
					<a class="comment-edit-photo-link" href="{{$mylink}}" title="{{$mytitle}}"><img class="my-comment-photo" src="{{$myphoto}}" alt="{{$mytitle}}" title="{{$mytitle}}" /></a>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,15 +16,16 @@ function theme_post(App $a)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (isset($_POST['frio-settings-submit'])) {
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'scheme',           defaults($_POST, 'frio_scheme', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'nav_bg',           defaults($_POST, 'frio_nav_bg', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'nav_icon_color',   defaults($_POST, 'frio_nav_icon_color', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'link_color',       defaults($_POST, 'frio_link_color', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'background_color', defaults($_POST, 'frio_background_color', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'contentbg_transp', defaults($_POST, 'frio_contentbg_transp', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'background_image', defaults($_POST, 'frio_background_image', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'bg_image_option',  defaults($_POST, 'frio_bg_image_option', ''));
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'scheme',           $_POST['frio_scheme']           ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'nav_bg',           $_POST['frio_nav_bg']           ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'nav_icon_color',   $_POST['frio_nav_icon_color']   ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'link_color',       $_POST['frio_link_color']       ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'background_color', $_POST['frio_background_color'] ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'contentbg_transp', $_POST['frio_contentbg_transp'] ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'background_image', $_POST['frio_background_image'] ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'bg_image_option',  $_POST['frio_bg_image_option']  ?? '');
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'css_modified',     time());
 | 
			
		||||
		PConfig::set(local_user(), 'frio', 'enable_compose',   $_POST['frio_enable_compose']   ?? 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,17 +36,18 @@ function theme_admin_post(App $a)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (isset($_POST['frio-settings-submit'])) {
 | 
			
		||||
		Config::set('frio', 'scheme',           defaults($_POST, 'frio_scheme', ''));
 | 
			
		||||
		Config::set('frio', 'nav_bg',           defaults($_POST, 'frio_nav_bg', ''));
 | 
			
		||||
		Config::set('frio', 'nav_icon_color',   defaults($_POST, 'frio_nav_icon_color', ''));
 | 
			
		||||
		Config::set('frio', 'link_color',       defaults($_POST, 'frio_link_color', ''));
 | 
			
		||||
		Config::set('frio', 'background_color', defaults($_POST, 'frio_background_color', ''));
 | 
			
		||||
		Config::set('frio', 'contentbg_transp', defaults($_POST, 'frio_contentbg_transp', ''));
 | 
			
		||||
		Config::set('frio', 'background_image', defaults($_POST, 'frio_background_image', ''));
 | 
			
		||||
		Config::set('frio', 'bg_image_option',  defaults($_POST, 'frio_bg_image_option', ''));
 | 
			
		||||
		Config::set('frio', 'login_bg_image',   defaults($_POST, 'frio_login_bg_image', ''));
 | 
			
		||||
		Config::set('frio', 'login_bg_color',   defaults($_POST, 'frio_login_bg_color', ''));
 | 
			
		||||
		Config::set('frio', 'scheme',           $_POST['frio_scheme']           ?? '');
 | 
			
		||||
		Config::set('frio', 'nav_bg',           $_POST['frio_nav_bg']           ?? '');
 | 
			
		||||
		Config::set('frio', 'nav_icon_color',   $_POST['frio_nav_icon_color']   ?? '');
 | 
			
		||||
		Config::set('frio', 'link_color',       $_POST['frio_link_color']       ?? '');
 | 
			
		||||
		Config::set('frio', 'background_color', $_POST['frio_background_color'] ?? '');
 | 
			
		||||
		Config::set('frio', 'contentbg_transp', $_POST['frio_contentbg_transp'] ?? '');
 | 
			
		||||
		Config::set('frio', 'background_image', $_POST['frio_background_image'] ?? '');
 | 
			
		||||
		Config::set('frio', 'bg_image_option',  $_POST['frio_bg_image_option']  ?? '');
 | 
			
		||||
		Config::set('frio', 'login_bg_image',   $_POST['frio_login_bg_image']   ?? '');
 | 
			
		||||
		Config::set('frio', 'login_bg_color',   $_POST['frio_login_bg_color']   ?? '');
 | 
			
		||||
		Config::set('frio', 'css_modified',     time());
 | 
			
		||||
		Config::set('frio', 'enable_compose',   $_POST['frio_enable_compose']   ?? 0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +69,7 @@ function theme_content(App $a)
 | 
			
		|||
	$arr['contentbg_transp'] = PConfig::get(local_user(), 'frio', 'contentbg_transp', Config::get('frio', 'contentbg_transp'));
 | 
			
		||||
	$arr['background_image'] = PConfig::get(local_user(), 'frio', 'background_image', Config::get('frio', 'background_image'));
 | 
			
		||||
	$arr['bg_image_option']  = PConfig::get(local_user(), 'frio', 'bg_image_option' , Config::get('frio', 'bg_image_option'));
 | 
			
		||||
	$arr['enable_compose']   = PConfig::get(local_user(), 'frio', 'enable_compose'  , Config::get('frio', 'enable_compose'));
 | 
			
		||||
 | 
			
		||||
	return frio_form($arr);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +81,7 @@ function theme_admin(App $a)
 | 
			
		|||
	}
 | 
			
		||||
	$arr = [];
 | 
			
		||||
 | 
			
		||||
	$arr['scheme']           = Config::get('frio', 'scheme', Config::get('frio', 'scheme'));
 | 
			
		||||
	$arr['scheme']           = Config::get('frio', 'scheme', Config::get('frio', 'schema'));
 | 
			
		||||
	$arr['share_string']     = '';
 | 
			
		||||
	$arr['nav_bg']           = Config::get('frio', 'nav_bg');
 | 
			
		||||
	$arr['nav_icon_color']   = Config::get('frio', 'nav_icon_color');
 | 
			
		||||
| 
						 | 
				
			
			@ -89,6 +92,7 @@ function theme_admin(App $a)
 | 
			
		|||
	$arr['bg_image_option']  = Config::get('frio', 'bg_image_option');
 | 
			
		||||
	$arr['login_bg_image']   = Config::get('frio', 'login_bg_image');
 | 
			
		||||
	$arr['login_bg_color']   = Config::get('frio', 'login_bg_color');
 | 
			
		||||
	$arr['enable_compose']   = Config::get('frio', 'enable_compose');
 | 
			
		||||
 | 
			
		||||
	return frio_form($arr);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +136,7 @@ function frio_form($arr)
 | 
			
		|||
		'$background_image' => array_key_exists('background_image', $disable) ? '' : ['frio_background_image', L10n::t('Set the background image'), $arr['background_image'], $background_image_help, false],
 | 
			
		||||
		'$bg_image_options_title' => L10n::t('Background image style'),
 | 
			
		||||
		'$bg_image_options' => Image::get_options($arr),
 | 
			
		||||
		'$enable_compose'   => ['frio_enable_compose', L10n::t('Enable Compose page'), $arr['enable_compose'], L10n::t('This replaces the jot modal window for writing new posts with a link to <a href="compose">the new Compose page</a>.')],
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
	if (array_key_exists('login_bg_image', $arr) && !array_key_exists('login_bg_image', $disable)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -192,7 +192,6 @@ blockquote {
 | 
			
		|||
    background-image: none;
 | 
			
		||||
    text-shadow: none;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    outline: 0!important;
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    font-weight: 600;
 | 
			
		||||
| 
						 | 
				
			
			@ -1314,7 +1313,8 @@ section ul.tabs {
 | 
			
		|||
section #jotOpen {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
#jotOpen {
 | 
			
		||||
#jotOpen,
 | 
			
		||||
#composeOpen {
 | 
			
		||||
    margin-top: 3px;
 | 
			
		||||
    float: right;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1372,7 +1372,8 @@ section #jotOpen {
 | 
			
		|||
#jot-text-wrap .preview textarea {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
#preview_profile-jot-text {
 | 
			
		||||
#preview_profile-jot-text,
 | 
			
		||||
.comment-edit-form .preview {
 | 
			
		||||
    position: relative;
 | 
			
		||||
    padding: 0px 10px;
 | 
			
		||||
    margin-top: -2px;
 | 
			
		||||
| 
						 | 
				
			
			@ -1383,7 +1384,8 @@ section #jotOpen {
 | 
			
		|||
    background: #fff;
 | 
			
		||||
    color: #555;
 | 
			
		||||
}
 | 
			
		||||
textarea#profile-jot-text:focus + #preview_profile-jot-text {
 | 
			
		||||
textarea#profile-jot-text:focus + #preview_profile-jot-text,
 | 
			
		||||
textarea.comment-edit-text:focus + .comment-edit-form .preview {
 | 
			
		||||
    border: 2px solid #6fdbe8;
 | 
			
		||||
    border-top: none;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3425,7 +3427,6 @@ section .profile-match-wrapper {
 | 
			
		|||
    background-image: none;
 | 
			
		||||
    text-shadow: none;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    outline: 0!important;
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    font-weight: 600;
 | 
			
		||||
| 
						 | 
				
			
			@ -3444,7 +3445,6 @@ section .profile-match-wrapper {
 | 
			
		|||
    background-image: none;
 | 
			
		||||
    text-shadow: none;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    outline: 0!important;
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    font-weight: 600;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										20
									
								
								view/theme/frio/frameworks/friendica-tagsinput/LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								view/theme/frio/frameworks/friendica-tagsinput/LICENSE
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2013 Tim Schlechter
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
			
		||||
this software and associated documentation files (the "Software"), to deal in
 | 
			
		||||
the Software without restriction, including without limitation the rights to
 | 
			
		||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
			
		||||
the Software, and to permit persons to whom the Software is furnished to do so,
 | 
			
		||||
subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
			
		||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
			
		||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
			
		||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
			
		||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
/*
 | 
			
		||||
 * friendica-tagsinput v0.8.0
 | 
			
		||||
 * 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
.twitter-typeahead .tt-query,
 | 
			
		||||
.twitter-typeahead .tt-hint {
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.twitter-typeahead .tt-hint
 | 
			
		||||
{
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tt-menu {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 100%;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    z-index: 1000;
 | 
			
		||||
    display: none;
 | 
			
		||||
    float: left;
 | 
			
		||||
    min-width: 160px;
 | 
			
		||||
    padding: 5px 0;
 | 
			
		||||
    margin: 2px 0 0;
 | 
			
		||||
    list-style: none;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    background-color: #ffffff;
 | 
			
		||||
    border: 1px solid #cccccc;
 | 
			
		||||
    border: 1px solid rgba(0, 0, 0, 0.15);
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
 | 
			
		||||
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
 | 
			
		||||
    background-clip: padding-box;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tt-suggestion {
 | 
			
		||||
    display: block;
 | 
			
		||||
    padding: 3px 20px;
 | 
			
		||||
    clear: both;
 | 
			
		||||
    font-weight: normal;
 | 
			
		||||
    line-height: 1.428571429;
 | 
			
		||||
    color: #333333;
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tt-suggestion:hover,
 | 
			
		||||
.tt-suggestion:focus {
 | 
			
		||||
    color: #ffffff;
 | 
			
		||||
    text-decoration: none;
 | 
			
		||||
    outline: 0;
 | 
			
		||||
    background-color: #428bca;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
/*
 | 
			
		||||
 * friendica-tagsinput v0.8.0
 | 
			
		||||
 * 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
.friendica-tagsinput {
 | 
			
		||||
  background-color: #fff;
 | 
			
		||||
  border: 1px solid #ccc;
 | 
			
		||||
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
 | 
			
		||||
  display: inline-block;
 | 
			
		||||
  padding: 4px 6px;
 | 
			
		||||
  color: #555;
 | 
			
		||||
  vertical-align: middle;
 | 
			
		||||
  border-radius: 4px;
 | 
			
		||||
  max-width: 100%;
 | 
			
		||||
  line-height: 22px;
 | 
			
		||||
  cursor: text;
 | 
			
		||||
  height: auto;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput.input-lg {
 | 
			
		||||
  line-height: 27px;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput input {
 | 
			
		||||
  border: none;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
  outline: none;
 | 
			
		||||
  background-color: transparent;
 | 
			
		||||
  padding: 0 6px;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
  width: auto;
 | 
			
		||||
  max-width: inherit;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput.form-control input::-moz-placeholder {
 | 
			
		||||
  color: #777;
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput.form-control input:-ms-input-placeholder {
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput.form-control input::-webkit-input-placeholder {
 | 
			
		||||
  color: #777;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput input:focus {
 | 
			
		||||
  border: none;
 | 
			
		||||
  box-shadow: none;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag {
 | 
			
		||||
  margin: 0 2px 2px 0;
 | 
			
		||||
  color: white;
 | 
			
		||||
  font-weight: normal;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag img {
 | 
			
		||||
  width: auto;
 | 
			
		||||
  height: 1.5em;
 | 
			
		||||
  vertical-align: text-top;
 | 
			
		||||
  margin-right: 8px;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag [data-role="remove"] {
 | 
			
		||||
  margin-left: 8px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag [data-role="remove"]:after {
 | 
			
		||||
  content: "x";
 | 
			
		||||
  padding: 0px 2px;
 | 
			
		||||
  font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag [data-role="remove"]:hover {
 | 
			
		||||
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
 | 
			
		||||
}
 | 
			
		||||
.friendica-tagsinput .tag [data-role="remove"]:hover:active {
 | 
			
		||||
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,695 @@
 | 
			
		|||
/*
 | 
			
		||||
 * friendica-tagsinput v0.8.0
 | 
			
		||||
 * Based on bootstrap-tagsinput v0.8.0
 | 
			
		||||
 *
 | 
			
		||||
 * Adds:
 | 
			
		||||
 * - optional thumbnail
 | 
			
		||||
 * - copying source input element class to the pseudo-input element
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
(function ($) {
 | 
			
		||||
  "use strict";
 | 
			
		||||
 | 
			
		||||
  var defaultOptions = {
 | 
			
		||||
    tagClass: function(item) {
 | 
			
		||||
      return 'label label-info';
 | 
			
		||||
    },
 | 
			
		||||
    focusClass: 'focus',
 | 
			
		||||
    itemValue: function(item) {
 | 
			
		||||
      return item ? item.toString() : item;
 | 
			
		||||
    },
 | 
			
		||||
    itemText: function(item) {
 | 
			
		||||
      return this.itemValue(item);
 | 
			
		||||
    },
 | 
			
		||||
    itemTitle: function(item) {
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    itemThumb: function(item) {
 | 
			
		||||
      return null;
 | 
			
		||||
    },
 | 
			
		||||
    freeInput: true,
 | 
			
		||||
    addOnBlur: true,
 | 
			
		||||
    maxTags: undefined,
 | 
			
		||||
    maxChars: undefined,
 | 
			
		||||
    confirmKeys: [13, 44],
 | 
			
		||||
    delimiter: ',',
 | 
			
		||||
    delimiterRegex: null,
 | 
			
		||||
    cancelConfirmKeysOnEmpty: false,
 | 
			
		||||
    onTagExists: function(item, $tag) {
 | 
			
		||||
      $tag.hide().fadeIn();
 | 
			
		||||
    },
 | 
			
		||||
    trimValue: false,
 | 
			
		||||
    allowDuplicates: false,
 | 
			
		||||
    triggerChange: true
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Constructor function
 | 
			
		||||
   */
 | 
			
		||||
  function TagsInput(element, options) {
 | 
			
		||||
    this.isInit = true;
 | 
			
		||||
    this.itemsArray = [];
 | 
			
		||||
 | 
			
		||||
    this.$element = $(element);
 | 
			
		||||
    this.$element.hide();
 | 
			
		||||
 | 
			
		||||
    this.isSelect = (element.tagName === 'SELECT');
 | 
			
		||||
    this.multiple = (this.isSelect && element.hasAttribute('multiple'));
 | 
			
		||||
    this.objectItems = options && options.itemValue;
 | 
			
		||||
    this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
 | 
			
		||||
    this.inputSize = Math.max(1, this.placeholderText.length);
 | 
			
		||||
 | 
			
		||||
    this.$container = $('<div class="friendica-tagsinput"></div>');
 | 
			
		||||
    this.$container.addClass(this.$element.attr('class'));
 | 
			
		||||
    this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
 | 
			
		||||
 | 
			
		||||
    this.$element.before(this.$container);
 | 
			
		||||
 | 
			
		||||
    this.build(options);
 | 
			
		||||
    this.isInit = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  TagsInput.prototype = {
 | 
			
		||||
    constructor: TagsInput,
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds the given item as a new tag. Pass true to dontPushVal to prevent
 | 
			
		||||
     * updating the elements val()
 | 
			
		||||
     */
 | 
			
		||||
    add: function(item, dontPushVal, options) {
 | 
			
		||||
      let self = this;
 | 
			
		||||
 | 
			
		||||
      if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      // Ignore falsey values, except false
 | 
			
		||||
      if (item !== false && !item)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      // Trim value
 | 
			
		||||
      if (typeof item === "string" && self.options.trimValue) {
 | 
			
		||||
        item = $.trim(item);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Throw an error when trying to add an object while the itemValue option was not set
 | 
			
		||||
      if (typeof item === "object" && !self.objectItems)
 | 
			
		||||
        throw("Can't add objects when itemValue option is not set");
 | 
			
		||||
 | 
			
		||||
      // Ignore strings only containg whitespace
 | 
			
		||||
      if (item.toString().match(/^\s*$/))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      // If SELECT but not multiple, remove current tag
 | 
			
		||||
      if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
 | 
			
		||||
        self.remove(self.itemsArray[0]);
 | 
			
		||||
 | 
			
		||||
      if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
 | 
			
		||||
        var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
 | 
			
		||||
        var items = item.split(delimiter);
 | 
			
		||||
        if (items.length > 1) {
 | 
			
		||||
          for (var i = 0; i < items.length; i++) {
 | 
			
		||||
            this.add(items[i], true);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (!dontPushVal)
 | 
			
		||||
            self.pushVal(self.options.triggerChange);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      var itemValue = self.options.itemValue(item),
 | 
			
		||||
          itemText = self.options.itemText(item),
 | 
			
		||||
          tagClass = self.options.tagClass(item),
 | 
			
		||||
          itemTitle = self.options.itemTitle(item),
 | 
			
		||||
          itemThumb = self.options.itemThumb(item);
 | 
			
		||||
 | 
			
		||||
      // Ignore items allready added
 | 
			
		||||
      var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
 | 
			
		||||
      if (existing && !self.options.allowDuplicates) {
 | 
			
		||||
        // Invoke onTagExists
 | 
			
		||||
        if (self.options.onTagExists) {
 | 
			
		||||
          var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
 | 
			
		||||
          self.options.onTagExists(item, $existingTag);
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // if length greater than limit
 | 
			
		||||
      if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      // raise beforeItemAdd arg
 | 
			
		||||
      var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
 | 
			
		||||
      self.$element.trigger(beforeItemAddEvent);
 | 
			
		||||
      if (beforeItemAddEvent.cancel)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      // register item in internal array and map
 | 
			
		||||
      self.itemsArray.push(item);
 | 
			
		||||
 | 
			
		||||
      // add a tag element
 | 
			
		||||
      var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' +
 | 
			
		||||
          (itemThumb !== null ? '<img src="' + itemThumb + '" alt="">' : '') +
 | 
			
		||||
          htmlEncode(itemText) + '<span data-role="remove"></span>' +
 | 
			
		||||
          '</span>');
 | 
			
		||||
      $tag.data('item', item);
 | 
			
		||||
      self.findInputWrapper().before($tag);
 | 
			
		||||
      $tag.after(' ');
 | 
			
		||||
 | 
			
		||||
      // Check to see if the tag exists in its raw or uri-encoded form
 | 
			
		||||
      var optionExists = (
 | 
			
		||||
          $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
 | 
			
		||||
          $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // add <option /> if item represents a value not present in one of the <select />'s options
 | 
			
		||||
      if (self.isSelect && !optionExists) {
 | 
			
		||||
        var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
 | 
			
		||||
        $option.data('item', item);
 | 
			
		||||
        $option.attr('value', itemValue);
 | 
			
		||||
        self.$element.append($option);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!dontPushVal)
 | 
			
		||||
        self.pushVal(self.options.triggerChange);
 | 
			
		||||
 | 
			
		||||
      // Add class when reached maxTags
 | 
			
		||||
      if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
 | 
			
		||||
        self.$container.addClass('friendica-tagsinput-max');
 | 
			
		||||
 | 
			
		||||
      // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
 | 
			
		||||
      if ($('.typeahead, .twitter-typeahead', self.$container).length) {
 | 
			
		||||
        self.$input.typeahead('val', '');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (this.isInit) {
 | 
			
		||||
        self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
 | 
			
		||||
      } else {
 | 
			
		||||
        self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes the given item. Pass true to dontPushVal to prevent updating the
 | 
			
		||||
     * elements val()
 | 
			
		||||
     */
 | 
			
		||||
    remove: function(item, dontPushVal, options) {
 | 
			
		||||
      var self = this;
 | 
			
		||||
 | 
			
		||||
      if (self.objectItems) {
 | 
			
		||||
        if (typeof item === "object")
 | 
			
		||||
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  self.options.itemValue(item); } );
 | 
			
		||||
        else
 | 
			
		||||
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  item; } );
 | 
			
		||||
 | 
			
		||||
        item = item[item.length-1];
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (item) {
 | 
			
		||||
        var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
 | 
			
		||||
        self.$element.trigger(beforeItemRemoveEvent);
 | 
			
		||||
        if (beforeItemRemoveEvent.cancel)
 | 
			
		||||
          return;
 | 
			
		||||
 | 
			
		||||
        $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
 | 
			
		||||
        $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
 | 
			
		||||
        if($.inArray(item, self.itemsArray) !== -1)
 | 
			
		||||
          self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (!dontPushVal)
 | 
			
		||||
        self.pushVal(self.options.triggerChange);
 | 
			
		||||
 | 
			
		||||
      // Remove class when reached maxTags
 | 
			
		||||
      if (self.options.maxTags > self.itemsArray.length)
 | 
			
		||||
        self.$container.removeClass('friendica-tagsinput-max');
 | 
			
		||||
 | 
			
		||||
      self.$element.trigger($.Event('itemRemoved',  { item: item, options: options }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes all items
 | 
			
		||||
     */
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
      var self = this;
 | 
			
		||||
 | 
			
		||||
      $('.tag', self.$container).remove();
 | 
			
		||||
      $('option', self.$element).remove();
 | 
			
		||||
 | 
			
		||||
      while(self.itemsArray.length > 0)
 | 
			
		||||
        self.itemsArray.pop();
 | 
			
		||||
 | 
			
		||||
      self.pushVal(self.options.triggerChange);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refreshes the tags so they match the text/value of their corresponding
 | 
			
		||||
     * item.
 | 
			
		||||
     */
 | 
			
		||||
    refresh: function() {
 | 
			
		||||
      var self = this;
 | 
			
		||||
      $('.tag', self.$container).each(function() {
 | 
			
		||||
        var $tag = $(this),
 | 
			
		||||
            item = $tag.data('item'),
 | 
			
		||||
            itemValue = self.options.itemValue(item),
 | 
			
		||||
            itemText = self.options.itemText(item),
 | 
			
		||||
            tagClass = self.options.tagClass(item);
 | 
			
		||||
 | 
			
		||||
          // Update tag's class and inner text
 | 
			
		||||
          $tag.attr('class', null);
 | 
			
		||||
          $tag.addClass('tag ' + htmlEncode(tagClass));
 | 
			
		||||
          $tag.contents().filter(function() {
 | 
			
		||||
            return this.nodeType == 3;
 | 
			
		||||
          })[0].nodeValue = htmlEncode(itemText);
 | 
			
		||||
 | 
			
		||||
          if (self.isSelect) {
 | 
			
		||||
            var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
 | 
			
		||||
            option.attr('value', itemValue);
 | 
			
		||||
          }
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the items added as tags
 | 
			
		||||
     */
 | 
			
		||||
    items: function() {
 | 
			
		||||
      return this.itemsArray;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Assembly value by retrieving the value of each item, and set it on the
 | 
			
		||||
     * element.
 | 
			
		||||
     */
 | 
			
		||||
    pushVal: function() {
 | 
			
		||||
      var self = this,
 | 
			
		||||
          val = $.map(self.items(), function(item) {
 | 
			
		||||
            return self.options.itemValue(item).toString();
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
      self.$element.val(val, true);
 | 
			
		||||
 | 
			
		||||
      if (self.options.triggerChange)
 | 
			
		||||
        self.$element.trigger('change');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes the tags input behaviour on the element
 | 
			
		||||
     */
 | 
			
		||||
    build: function(options) {
 | 
			
		||||
      var self = this;
 | 
			
		||||
 | 
			
		||||
      self.options = $.extend({}, defaultOptions, options);
 | 
			
		||||
      // When itemValue is set, freeInput should always be false
 | 
			
		||||
      if (self.objectItems)
 | 
			
		||||
        self.options.freeInput = false;
 | 
			
		||||
 | 
			
		||||
      makeOptionItemFunction(self.options, 'itemValue');
 | 
			
		||||
      makeOptionItemFunction(self.options, 'itemText');
 | 
			
		||||
      makeOptionItemFunction(self.options, 'itemThumb');
 | 
			
		||||
      makeOptionFunction(self.options, 'tagClass');
 | 
			
		||||
 | 
			
		||||
      // Typeahead Bootstrap version 2.3.2
 | 
			
		||||
      if (self.options.typeahead) {
 | 
			
		||||
        var typeahead = self.options.typeahead || {};
 | 
			
		||||
 | 
			
		||||
        makeOptionFunction(typeahead, 'source');
 | 
			
		||||
 | 
			
		||||
        self.$input.typeahead($.extend({}, typeahead, {
 | 
			
		||||
          source: function (query, process) {
 | 
			
		||||
            function processItems(items) {
 | 
			
		||||
              var texts = [];
 | 
			
		||||
 | 
			
		||||
              for (var i = 0; i < items.length; i++) {
 | 
			
		||||
                var text = self.options.itemText(items[i]);
 | 
			
		||||
                map[text] = items[i];
 | 
			
		||||
                texts.push(text);
 | 
			
		||||
              }
 | 
			
		||||
              process(texts);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.map = {};
 | 
			
		||||
            var map = this.map,
 | 
			
		||||
                data = typeahead.source(query);
 | 
			
		||||
 | 
			
		||||
            if ($.isFunction(data.success)) {
 | 
			
		||||
              // support for Angular callbacks
 | 
			
		||||
              data.success(processItems);
 | 
			
		||||
            } else if ($.isFunction(data.then)) {
 | 
			
		||||
              // support for Angular promises
 | 
			
		||||
              data.then(processItems);
 | 
			
		||||
            } else {
 | 
			
		||||
              // support for functions and jquery promises
 | 
			
		||||
              $.when(data)
 | 
			
		||||
                  .then(processItems);
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          updater: function (text) {
 | 
			
		||||
            self.add(this.map[text]);
 | 
			
		||||
            return this.map[text];
 | 
			
		||||
          },
 | 
			
		||||
          matcher: function (text) {
 | 
			
		||||
            return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
 | 
			
		||||
          },
 | 
			
		||||
          sorter: function (texts) {
 | 
			
		||||
            return texts.sort();
 | 
			
		||||
          },
 | 
			
		||||
          highlighter: function (text) {
 | 
			
		||||
            var regex = new RegExp( '(' + this.query + ')', 'gi' );
 | 
			
		||||
            return text.replace( regex, "<strong>$1</strong>" );
 | 
			
		||||
          }
 | 
			
		||||
        }));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // typeahead.js
 | 
			
		||||
      if (self.options.typeaheadjs) {
 | 
			
		||||
        var typeaheadConfig = null;
 | 
			
		||||
        var typeaheadDatasets = {};
 | 
			
		||||
 | 
			
		||||
        // Determine if main configurations were passed or simply a dataset
 | 
			
		||||
        var typeaheadjs = self.options.typeaheadjs;
 | 
			
		||||
        if ($.isArray(typeaheadjs)) {
 | 
			
		||||
          typeaheadConfig = typeaheadjs[0];
 | 
			
		||||
          typeaheadDatasets = typeaheadjs[1];
 | 
			
		||||
        } else {
 | 
			
		||||
          typeaheadDatasets = typeaheadjs;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
 | 
			
		||||
          if (typeaheadDatasets.valueKey)
 | 
			
		||||
            self.add(datum[typeaheadDatasets.valueKey]);
 | 
			
		||||
          else
 | 
			
		||||
            self.add(datum);
 | 
			
		||||
          self.$input.typeahead('val', '');
 | 
			
		||||
        }, self));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      self.$container.on('click', $.proxy(function(event) {
 | 
			
		||||
        if (! self.$element.attr('disabled')) {
 | 
			
		||||
          self.$input.removeAttr('disabled');
 | 
			
		||||
        }
 | 
			
		||||
        self.$input.focus();
 | 
			
		||||
      }, self));
 | 
			
		||||
 | 
			
		||||
      if (self.options.addOnBlur && self.options.freeInput) {
 | 
			
		||||
        self.$input.on('focusout', $.proxy(function(event) {
 | 
			
		||||
          // HACK: only process on focusout when no typeahead opened, to
 | 
			
		||||
          //       avoid adding the typeahead text as tag
 | 
			
		||||
          if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
 | 
			
		||||
            self.add(self.$input.val());
 | 
			
		||||
            self.$input.val('');
 | 
			
		||||
          }
 | 
			
		||||
        }, self));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Toggle the 'focus' css class on the container when it has focus
 | 
			
		||||
      self.$container.on({
 | 
			
		||||
        focusin: function() {
 | 
			
		||||
          self.$container.addClass(self.options.focusClass);
 | 
			
		||||
        },
 | 
			
		||||
        focusout: function() {
 | 
			
		||||
          self.$container.removeClass(self.options.focusClass);
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      self.$container.on('keydown', 'input', $.proxy(function(event) {
 | 
			
		||||
        var $input = $(event.target),
 | 
			
		||||
            $inputWrapper = self.findInputWrapper();
 | 
			
		||||
 | 
			
		||||
        if (self.$element.attr('disabled')) {
 | 
			
		||||
          self.$input.attr('disabled', 'disabled');
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (event.which) {
 | 
			
		||||
            // BACKSPACE
 | 
			
		||||
          case 8:
 | 
			
		||||
            if (doGetCaretPosition($input[0]) === 0) {
 | 
			
		||||
              var prev = $inputWrapper.prev();
 | 
			
		||||
              if (prev.length) {
 | 
			
		||||
                self.remove(prev.data('item'));
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
            // DELETE
 | 
			
		||||
          case 46:
 | 
			
		||||
            if (doGetCaretPosition($input[0]) === 0) {
 | 
			
		||||
              var next = $inputWrapper.next();
 | 
			
		||||
              if (next.length) {
 | 
			
		||||
                self.remove(next.data('item'));
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
            // LEFT ARROW
 | 
			
		||||
          case 37:
 | 
			
		||||
            // Try to move the input before the previous tag
 | 
			
		||||
            var $prevTag = $inputWrapper.prev();
 | 
			
		||||
            if ($input.val().length === 0 && $prevTag[0]) {
 | 
			
		||||
              $prevTag.before($inputWrapper);
 | 
			
		||||
              $input.focus();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
            // RIGHT ARROW
 | 
			
		||||
          case 39:
 | 
			
		||||
            // Try to move the input after the next tag
 | 
			
		||||
            var $nextTag = $inputWrapper.next();
 | 
			
		||||
            if ($input.val().length === 0 && $nextTag[0]) {
 | 
			
		||||
              $nextTag.after($inputWrapper);
 | 
			
		||||
              $input.focus();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          default:
 | 
			
		||||
            // ignore
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Reset internal input's size
 | 
			
		||||
        var textLength = $input.val().length,
 | 
			
		||||
            wordSpace = Math.ceil(textLength / 5),
 | 
			
		||||
            size = textLength + wordSpace + 1;
 | 
			
		||||
        $input.attr('size', Math.max(this.inputSize, $input.val().length));
 | 
			
		||||
      }, self));
 | 
			
		||||
 | 
			
		||||
      self.$container.on('keypress', 'input', $.proxy(function(event) {
 | 
			
		||||
        var $input = $(event.target);
 | 
			
		||||
 | 
			
		||||
        if (self.$element.attr('disabled')) {
 | 
			
		||||
          self.$input.attr('disabled', 'disabled');
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var text = $input.val(),
 | 
			
		||||
            maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
 | 
			
		||||
        if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
 | 
			
		||||
          // Only attempt to add a tag if there is data in the field
 | 
			
		||||
          if (text.length !== 0) {
 | 
			
		||||
            self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
 | 
			
		||||
            $input.val('');
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // If the field is empty, let the event triggered fire as usual
 | 
			
		||||
          if (self.options.cancelConfirmKeysOnEmpty === false) {
 | 
			
		||||
            event.preventDefault();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Reset internal input's size
 | 
			
		||||
        var textLength = $input.val().length,
 | 
			
		||||
            wordSpace = Math.ceil(textLength / 5),
 | 
			
		||||
            size = textLength + wordSpace + 1;
 | 
			
		||||
        $input.attr('size', Math.max(this.inputSize, $input.val().length));
 | 
			
		||||
      }, self));
 | 
			
		||||
 | 
			
		||||
      // Remove icon clicked
 | 
			
		||||
      self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
 | 
			
		||||
        if (self.$element.attr('disabled')) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        self.remove($(event.target).closest('.tag').data('item'));
 | 
			
		||||
      }, self));
 | 
			
		||||
 | 
			
		||||
      // Only add existing value as tags when using strings as tags
 | 
			
		||||
      if (self.options.itemValue === defaultOptions.itemValue) {
 | 
			
		||||
        if (self.$element[0].tagName === 'INPUT') {
 | 
			
		||||
          self.add(self.$element.val());
 | 
			
		||||
        } else {
 | 
			
		||||
          $('option', self.$element).each(function() {
 | 
			
		||||
            self.add($(this).attr('value'), true);
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes all tagsinput behaviour and unregsiter all event handlers
 | 
			
		||||
     */
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
      var self = this;
 | 
			
		||||
 | 
			
		||||
      // Unbind events
 | 
			
		||||
      self.$container.off('keypress', 'input');
 | 
			
		||||
      self.$container.off('click', '[role=remove]');
 | 
			
		||||
 | 
			
		||||
      self.$container.remove();
 | 
			
		||||
      self.$element.removeData('tagsinput');
 | 
			
		||||
      self.$element.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets focus on the tagsinput
 | 
			
		||||
     */
 | 
			
		||||
    focus: function() {
 | 
			
		||||
      this.$input.focus();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the internal input element
 | 
			
		||||
     */
 | 
			
		||||
    input: function() {
 | 
			
		||||
      return this.$input;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the element which is wrapped around the internal input. This
 | 
			
		||||
     * is normally the $container, but typeahead.js moves the $input element.
 | 
			
		||||
     */
 | 
			
		||||
    findInputWrapper: function() {
 | 
			
		||||
      var elt = this.$input[0],
 | 
			
		||||
          container = this.$container[0];
 | 
			
		||||
      while(elt && elt.parentNode !== container)
 | 
			
		||||
        elt = elt.parentNode;
 | 
			
		||||
 | 
			
		||||
      return $(elt);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Register JQuery plugin
 | 
			
		||||
   */
 | 
			
		||||
  $.fn.tagsinput = function(arg1, arg2, arg3) {
 | 
			
		||||
    var results = [];
 | 
			
		||||
 | 
			
		||||
    this.each(function() {
 | 
			
		||||
      var tagsinput = $(this).data('tagsinput');
 | 
			
		||||
      // Initialize a new tags input
 | 
			
		||||
      if (!tagsinput) {
 | 
			
		||||
          tagsinput = new TagsInput(this, arg1);
 | 
			
		||||
          $(this).data('tagsinput', tagsinput);
 | 
			
		||||
          results.push(tagsinput);
 | 
			
		||||
 | 
			
		||||
          if (this.tagName === 'SELECT') {
 | 
			
		||||
              $('option', $(this)).attr('selected', 'selected');
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Init tags from $(this).val()
 | 
			
		||||
          $(this).val($(this).val());
 | 
			
		||||
      } else if (!arg1 && !arg2) {
 | 
			
		||||
          // tagsinput already exists
 | 
			
		||||
          // no function, trying to init
 | 
			
		||||
          results.push(tagsinput);
 | 
			
		||||
      } else if(tagsinput[arg1] !== undefined) {
 | 
			
		||||
          // Invoke function on existing tags input
 | 
			
		||||
            if(tagsinput[arg1].length === 3 && arg3 !== undefined){
 | 
			
		||||
               var retVal = tagsinput[arg1](arg2, null, arg3);
 | 
			
		||||
            }else{
 | 
			
		||||
               var retVal = tagsinput[arg1](arg2);
 | 
			
		||||
            }
 | 
			
		||||
          if (retVal !== undefined)
 | 
			
		||||
              results.push(retVal);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if ( typeof arg1 == 'string') {
 | 
			
		||||
      // Return the results from the invoked function calls
 | 
			
		||||
      return results.length > 1 ? results : results[0];
 | 
			
		||||
    } else {
 | 
			
		||||
      return results;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $.fn.tagsinput.Constructor = TagsInput;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Most options support both a string or number as well as a function as
 | 
			
		||||
   * option value. This function makes sure that the option with the given
 | 
			
		||||
   * key in the given options is wrapped in a function
 | 
			
		||||
   */
 | 
			
		||||
  function makeOptionItemFunction(options, key) {
 | 
			
		||||
    if (typeof options[key] !== 'function') {
 | 
			
		||||
      var propertyName = options[key];
 | 
			
		||||
      options[key] = function(item) { return item[propertyName]; };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  function makeOptionFunction(options, key) {
 | 
			
		||||
    if (typeof options[key] !== 'function') {
 | 
			
		||||
      var value = options[key];
 | 
			
		||||
      options[key] = function() { return value; };
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * HtmlEncodes the given value
 | 
			
		||||
   */
 | 
			
		||||
  var htmlEncodeContainer = $('<div />');
 | 
			
		||||
  function htmlEncode(value) {
 | 
			
		||||
    if (value) {
 | 
			
		||||
      return htmlEncodeContainer.text(value).html();
 | 
			
		||||
    } else {
 | 
			
		||||
      return '';
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns the position of the caret in the given input field
 | 
			
		||||
   * http://flightschool.acylt.com/devnotes/caret-position-woes/
 | 
			
		||||
   */
 | 
			
		||||
  function doGetCaretPosition(oField) {
 | 
			
		||||
    var iCaretPos = 0;
 | 
			
		||||
    if (document.selection) {
 | 
			
		||||
      oField.focus ();
 | 
			
		||||
      var oSel = document.selection.createRange();
 | 
			
		||||
      oSel.moveStart ('character', -oField.value.length);
 | 
			
		||||
      iCaretPos = oSel.text.length;
 | 
			
		||||
    } else if (oField.selectionStart || oField.selectionStart == '0') {
 | 
			
		||||
      iCaretPos = oField.selectionStart;
 | 
			
		||||
    }
 | 
			
		||||
    return (iCaretPos);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
    * Returns boolean indicates whether user has pressed an expected key combination.
 | 
			
		||||
    * @param object keyPressEvent: JavaScript event object, refer
 | 
			
		||||
    *     http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
 | 
			
		||||
    * @param object lookupList: expected key combinations, as in:
 | 
			
		||||
    *     [13, {which: 188, shiftKey: true}]
 | 
			
		||||
    */
 | 
			
		||||
  function keyCombinationInList(keyPressEvent, lookupList) {
 | 
			
		||||
      var found = false;
 | 
			
		||||
      $.each(lookupList, function (index, keyCombination) {
 | 
			
		||||
          if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
 | 
			
		||||
              found = true;
 | 
			
		||||
              return false;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (keyPressEvent.which === keyCombination.which) {
 | 
			
		||||
              var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
 | 
			
		||||
                  shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
 | 
			
		||||
                  ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
 | 
			
		||||
              if (alt && shift && ctrl) {
 | 
			
		||||
                  found = true;
 | 
			
		||||
                  return false;
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return found;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialize tagsinput behaviour on inputs and selects which have
 | 
			
		||||
   * data-role=tagsinput
 | 
			
		||||
   */
 | 
			
		||||
  $(function() {
 | 
			
		||||
    $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
 | 
			
		||||
  });
 | 
			
		||||
})(window.jQuery);
 | 
			
		||||
| 
						 | 
				
			
			@ -64,9 +64,24 @@ $(document).ready(function(){
 | 
			
		|||
	});
 | 
			
		||||
 | 
			
		||||
	// add Jot botton to the scecond navbar
 | 
			
		||||
	if( $("section #jotOpen").length ) {
 | 
			
		||||
		$("section #jotOpen").appendTo("#topbar-second > .container > #navbar-button");
 | 
			
		||||
		if( $("#jot-popup").is(":hidden")) $("#topbar-second > .container > #navbar-button #jotOpen").hide();
 | 
			
		||||
	let $jotButton = $("#jotOpen");
 | 
			
		||||
	let $composeButton = $("#composeOpen");
 | 
			
		||||
	if (compose) {
 | 
			
		||||
		$jotButton.hide();
 | 
			
		||||
		if ($composeButton.length) {
 | 
			
		||||
			$composeButton.appendTo("#topbar-second > .container > #navbar-button");
 | 
			
		||||
			if($("#jot-popup").is(":hidden")) {
 | 
			
		||||
				$composeButton.hide();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		$composeButton.hide();
 | 
			
		||||
		if ($jotButton.length) {
 | 
			
		||||
			$jotButton.appendTo("#topbar-second > .container > #navbar-button");
 | 
			
		||||
			if($("#jot-popup").is(":hidden")) {
 | 
			
		||||
				$jotButton.hide();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// show bulk deletion button at network page if checkbox is checked
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
		<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
		{{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
		<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
		<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
		<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
 | 
			
		||||
		<p class="comment-edit-bb-{{$id}} comment-icon-list">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
{{* The button to open the jot - in This theme we move the button with js to the second nav bar *}}
 | 
			
		||||
<button class="btn btn-sm btn-main pull-right" id="jotOpen" aria-label="{{$new_post}}" title="{{$new_post}}" onclick="jotShow();"><i class="fa fa-pencil-square-o fa-2x"></i></button>
 | 
			
		||||
 | 
			
		||||
<a class="btn btn-sm btn-main pull-right" id="composeOpen" href="compose/{{$posttype}}{{if $content}}?body={{$content}}{{/if}}" aria-label="{{$new_post}}" title="{{$new_post}}"><i class="fa fa-pencil-square-o fa-2x"></i></a>
 | 
			
		||||
 | 
			
		||||
<div id="jot-content">
 | 
			
		||||
	<div id="jot-sections">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -183,6 +183,10 @@
 | 
			
		|||
	});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="form-group">
 | 
			
		||||
    {{include file="field_checkbox.tpl" field=$enable_compose}}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="settings-submit-wrapper form-group pull-right">
 | 
			
		||||
	<button type="submit" value="{{$submit}}" class="settings-submit btn btn-primary" name="frio-settings-submit">{{$submit}}</button>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,14 @@ function frio_init(App $a)
 | 
			
		|||
			</script>
 | 
			
		||||
EOT;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$enable_compose = \Friendica\Core\PConfig::get(local_user(), 'frio', 'enable_compose');
 | 
			
		||||
	$compose = $enable_compose === '1' || $enable_compose === null && Config::get('frio', 'enable_compose') ? 1 : 0;
 | 
			
		||||
	$a->page['htmlhead'] .= <<< HTML
 | 
			
		||||
		<script type="text/javascript">
 | 
			
		||||
			var compose = $compose;
 | 
			
		||||
		</script>
 | 
			
		||||
HTML;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function frio_install()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				{{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
				<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
				<input type="hidden" name="parent" value="{{$parent}}" />
 | 
			
		||||
				{{*<!--<input type="hidden" name="return" value="{{$return_path}}" />-->*}}
 | 
			
		||||
				<input type="hidden" name="jsreload" value="{{$jsreload}}" />
 | 
			
		||||
				<input type="hidden" name="preview" id="comment-preview-inp-{{$id}}" value="0" />
 | 
			
		||||
				<input type="hidden" name="post_id_random" value="{{$rand_num}}" />
 | 
			
		||||
 | 
			
		||||
				<div class="comment-edit-photo" id="comment-edit-photo-{{$id}}">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue