1f75a963eSBram Moolenaar" Vim completion script
2fc1421ebSBram Moolenaar" Language:	HTML and XHTML
3f75a963eSBram Moolenaar" Maintainer:	Mikolaj Machowski ( mikmach AT wp DOT pl )
45be4ceecSBram Moolenaar" Last Change:	2019 Sep 27
5946e27abSBram Moolenaar
6946e27abSBram Moolenaar" Distinguish between HTML versions.
7946e27abSBram Moolenaar" To use with other HTML versions add another "elseif" condition to match
8946e27abSBram Moolenaar" proper DOCTYPE.
9946e27abSBram Moolenaarfunction! htmlcomplete#DetectOmniFlavor()
10946e27abSBram Moolenaar  if &filetype == 'xhtml'
11946e27abSBram Moolenaar    let b:html_omni_flavor = 'xhtml10s'
12946e27abSBram Moolenaar  else
13946e27abSBram Moolenaar    let b:html_omni_flavor = 'html401t'
14946e27abSBram Moolenaar  endif
15946e27abSBram Moolenaar  let i = 1
16946e27abSBram Moolenaar  let line = ""
17946e27abSBram Moolenaar  while i < 10 && i < line("$")
18946e27abSBram Moolenaar    let line = getline(i)
19946e27abSBram Moolenaar    if line =~ '<!DOCTYPE.*\<DTD '
20946e27abSBram Moolenaar      break
21946e27abSBram Moolenaar    endif
22946e27abSBram Moolenaar    let i += 1
23946e27abSBram Moolenaar  endwhile
24946e27abSBram Moolenaar  if line =~ '<!DOCTYPE.*\<DTD '  " doctype line found above
25946e27abSBram Moolenaar    if line =~ ' HTML 3\.2'
26946e27abSBram Moolenaar      let b:html_omni_flavor = 'html32'
27946e27abSBram Moolenaar    elseif line =~ ' XHTML 1\.1'
28946e27abSBram Moolenaar      let b:html_omni_flavor = 'xhtml11'
29946e27abSBram Moolenaar    else    " two-step detection with strict/frameset/transitional
30946e27abSBram Moolenaar      if line =~ ' XHTML 1\.0'
31946e27abSBram Moolenaar	let b:html_omni_flavor = 'xhtml10'
32946e27abSBram Moolenaar      elseif line =~ ' HTML 4\.01'
33946e27abSBram Moolenaar	let b:html_omni_flavor = 'html401'
34946e27abSBram Moolenaar      elseif line =~ ' HTML 4.0\>'
35946e27abSBram Moolenaar	let b:html_omni_flavor = 'html40'
36946e27abSBram Moolenaar      endif
37946e27abSBram Moolenaar      if line =~ '\<Transitional\>'
38946e27abSBram Moolenaar	let b:html_omni_flavor .= 't'
39946e27abSBram Moolenaar      elseif line =~ '\<Frameset\>'
40946e27abSBram Moolenaar	let b:html_omni_flavor .= 'f'
41946e27abSBram Moolenaar      else
42946e27abSBram Moolenaar	let b:html_omni_flavor .= 's'
43946e27abSBram Moolenaar      endif
44946e27abSBram Moolenaar    endif
45946e27abSBram Moolenaar  endif
46946e27abSBram Moolenaarendfunction
47f75a963eSBram Moolenaar
48f75a963eSBram Moolenaarfunction! htmlcomplete#CompleteTags(findstart, base)
49f75a963eSBram Moolenaar  if a:findstart
50f75a963eSBram Moolenaar    " locate the start of the word
51f75a963eSBram Moolenaar    let line = getline('.')
52f75a963eSBram Moolenaar    let start = col('.') - 1
5328c258fdSBram Moolenaar	let curline = line('.')
54d5cdbeb8SBram Moolenaar	let compl_begin = col('.') - 2
554a85b415SBram Moolenaar    while start >= 0 && line[start - 1] =~ '\(\k\|[!:.-]\)'
56f75a963eSBram Moolenaar		let start -= 1
57f75a963eSBram Moolenaar    endwhile
588b6144bdSBram Moolenaar	" Handling of entities {{{
59d5cdbeb8SBram Moolenaar	if start >= 0 && line[start - 1] =~ '&'
60d5cdbeb8SBram Moolenaar		let b:entitiescompl = 1
61d5cdbeb8SBram Moolenaar		let b:compl_context = ''
62d5cdbeb8SBram Moolenaar		return start
63d5cdbeb8SBram Moolenaar	endif
648b6144bdSBram Moolenaar	" }}}
658b6144bdSBram Moolenaar	" Handling of <style> tag {{{
66bfd8fc05SBram Moolenaar	let stylestart = searchpair('<style\>', '', '<\/style\>', "bnW")
67bfd8fc05SBram Moolenaar	let styleend   = searchpair('<style\>', '', '<\/style\>', "nW")
68bfd8fc05SBram Moolenaar	if stylestart != 0 && styleend != 0
6928c258fdSBram Moolenaar		if stylestart <= curline && styleend >= curline
70d5cdbeb8SBram Moolenaar			let start = col('.') - 1
71bfd8fc05SBram Moolenaar			let b:csscompl = 1
72d5cdbeb8SBram Moolenaar			while start >= 0 && line[start - 1] =~ '\(\k\|-\)'
73d5cdbeb8SBram Moolenaar				let start -= 1
74d5cdbeb8SBram Moolenaar			endwhile
75bfd8fc05SBram Moolenaar		endif
76bfd8fc05SBram Moolenaar	endif
778b6144bdSBram Moolenaar	" }}}
788b6144bdSBram Moolenaar	" Handling of <script> tag {{{
79b8a7b560SBram Moolenaar	let scriptstart = searchpair('<script\>', '', '<\/script\>', "bnW")
80b8a7b560SBram Moolenaar	let scriptend   = searchpair('<script\>', '', '<\/script\>', "nW")
81b8a7b560SBram Moolenaar	if scriptstart != 0 && scriptend != 0
82b8a7b560SBram Moolenaar		if scriptstart <= curline && scriptend >= curline
83b8a7b560SBram Moolenaar			let start = col('.') - 1
84b8a7b560SBram Moolenaar			let b:jscompl = 1
85b8a7b560SBram Moolenaar			let b:jsrange = [scriptstart, scriptend]
861d2ba7faSBram Moolenaar			while start >= 0 && line[start - 1] =~ '\k'
87b8a7b560SBram Moolenaar				let start -= 1
88b8a7b560SBram Moolenaar			endwhile
898b6144bdSBram Moolenaar			" We are inside of <script> tag. But we should also get contents
908b6144bdSBram Moolenaar			" of all linked external files and (secondary, less probably) other <script> tags
918b6144bdSBram Moolenaar			" This logic could possible be done in separate function - may be
928b6144bdSBram Moolenaar			" reused in events scripting (also with option could be reused for
938b6144bdSBram Moolenaar			" CSS
948b6144bdSBram Moolenaar			let b:js_extfiles = []
958b6144bdSBram Moolenaar			let l = line('.')
968b6144bdSBram Moolenaar			let c = col('.')
978b6144bdSBram Moolenaar			call cursor(1,1)
988b6144bdSBram Moolenaar			while search('<\@<=script\>', 'W') && line('.') <= l
998b6144bdSBram Moolenaar				if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
1008b6144bdSBram Moolenaar					let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
1018b6144bdSBram Moolenaar					if filereadable(sname)
1028b6144bdSBram Moolenaar						let b:js_extfiles += readfile(sname)
103b8a7b560SBram Moolenaar					endif
104b8a7b560SBram Moolenaar				endif
1058b6144bdSBram Moolenaar			endwhile
1068b6144bdSBram Moolenaar			call cursor(1,1)
1078b6144bdSBram Moolenaar			let js_scripttags = []
1088b6144bdSBram Moolenaar			while search('<script\>', 'W') && line('.') < l
1098b6144bdSBram Moolenaar				if matchstr(getline('.'), '<script[^>]*src') == ''
1108b6144bdSBram Moolenaar					let js_scripttag = getline(line('.'), search('</script>', 'W'))
1118b6144bdSBram Moolenaar					let js_scripttags += js_scripttag
1128b6144bdSBram Moolenaar				endif
1138b6144bdSBram Moolenaar			endwhile
1148b6144bdSBram Moolenaar			let b:js_extfiles += js_scripttags
1158b6144bdSBram Moolenaar			call cursor(l,c)
1168b6144bdSBram Moolenaar			unlet! l c
1178b6144bdSBram Moolenaar		endif
1188b6144bdSBram Moolenaar	endif
1198b6144bdSBram Moolenaar	" }}}
120b8a7b560SBram Moolenaar	if !exists("b:csscompl") && !exists("b:jscompl")
121d5cdbeb8SBram Moolenaar		let b:compl_context = getline('.')[0:(compl_begin)]
12228c258fdSBram Moolenaar		if b:compl_context !~ '<[^>]*$'
1238b6144bdSBram Moolenaar			" Look like we may have broken tag. Check previous lines.
12428c258fdSBram Moolenaar			let i = 1
12528c258fdSBram Moolenaar			while 1
12628c258fdSBram Moolenaar				let context_line = getline(curline-i)
12728c258fdSBram Moolenaar				if context_line =~ '<[^>]*$'
12828c258fdSBram Moolenaar					" Yep, this is this line
129d5ab34bdSBram Moolenaar					let context_lines = getline(curline-i, curline-1) + [b:compl_context]
13028c258fdSBram Moolenaar					let b:compl_context = join(context_lines, ' ')
13128c258fdSBram Moolenaar					break
132e0fa560eSBram Moolenaar				elseif context_line =~ '>[^<]*$' || i == curline
133e0fa560eSBram Moolenaar					" We are in normal tag line, no need for completion at all
134e0fa560eSBram Moolenaar					" OR reached first line without tag at all
13528c258fdSBram Moolenaar					let b:compl_context = ''
13628c258fdSBram Moolenaar					break
13728c258fdSBram Moolenaar				endif
13828c258fdSBram Moolenaar				let i += 1
13928c258fdSBram Moolenaar			endwhile
14028c258fdSBram Moolenaar			" Make sure we don't have counter
14128c258fdSBram Moolenaar			unlet! i
14228c258fdSBram Moolenaar		endif
14309df3127SBram Moolenaar		let b:compl_context = matchstr(b:compl_context, '.*\zs<.*')
144e0fa560eSBram Moolenaar
1458b6144bdSBram Moolenaar		" Return proper start for on-events. Without that beginning of
1468b6144bdSBram Moolenaar		" completion will be badly reported
1478b6144bdSBram Moolenaar		if b:compl_context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
1488b6144bdSBram Moolenaar			let start = col('.') - 1
1491d2ba7faSBram Moolenaar			while start >= 0 && line[start - 1] =~ '\k'
1508b6144bdSBram Moolenaar				let start -= 1
1518b6144bdSBram Moolenaar			endwhile
1528b6144bdSBram Moolenaar		endif
153e0fa560eSBram Moolenaar		" If b:compl_context begins with <? we are inside of PHP code. It
154e0fa560eSBram Moolenaar		" wasn't closed so PHP completion passed it to HTML
155e0fa560eSBram Moolenaar		if &filetype =~? 'php' && b:compl_context =~ '^<?'
156e0fa560eSBram Moolenaar			let b:phpcompl = 1
157e0fa560eSBram Moolenaar			let start = col('.') - 1
158e0fa560eSBram Moolenaar			while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]'
159e0fa560eSBram Moolenaar				let start -= 1
160e0fa560eSBram Moolenaar			endwhile
161e0fa560eSBram Moolenaar		endif
162d5cdbeb8SBram Moolenaar	else
163d5cdbeb8SBram Moolenaar		let b:compl_context = getline('.')[0:compl_begin]
164bfd8fc05SBram Moolenaar	endif
165f75a963eSBram Moolenaar    return start
166f75a963eSBram Moolenaar  else
167d5cdbeb8SBram Moolenaar	" Initialize base return lists
168d5cdbeb8SBram Moolenaar    let res = []
169d5cdbeb8SBram Moolenaar    let res2 = []
170d5cdbeb8SBram Moolenaar	" a:base is very short - we need context
171d5cdbeb8SBram Moolenaar	let context = b:compl_context
172bfd8fc05SBram Moolenaar	" Check if we should do CSS completion inside of <style> tag
173e0fa560eSBram Moolenaar	" or JS completion inside of <script> tag or PHP completion in case of <?
174e0fa560eSBram Moolenaar	" tag AND &ft==php
175bfd8fc05SBram Moolenaar	if exists("b:csscompl")
176bfd8fc05SBram Moolenaar		unlet! b:csscompl
17709df3127SBram Moolenaar		let context = b:compl_context
1788b6144bdSBram Moolenaar		unlet! b:compl_context
179d5cdbeb8SBram Moolenaar		return csscomplete#CompleteCSS(0, context)
180b8a7b560SBram Moolenaar	elseif exists("b:jscompl")
181b8a7b560SBram Moolenaar		unlet! b:jscompl
1828b6144bdSBram Moolenaar		return javascriptcomplete#CompleteJS(0, a:base)
183e0fa560eSBram Moolenaar	elseif exists("b:phpcompl")
184e0fa560eSBram Moolenaar		unlet! b:phpcompl
185e0fa560eSBram Moolenaar		let context = b:compl_context
186e0fa560eSBram Moolenaar		return phpcomplete#CompletePHP(0, a:base)
18709df3127SBram Moolenaar	else
18809df3127SBram Moolenaar		if len(b:compl_context) == 0 && !exists("b:entitiescompl")
18909df3127SBram Moolenaar			return []
190bfd8fc05SBram Moolenaar		endif
19109df3127SBram Moolenaar		let context = matchstr(b:compl_context, '.\zs.*')
19209df3127SBram Moolenaar	endif
19309df3127SBram Moolenaar	unlet! b:compl_context
1948b6144bdSBram Moolenaar	" Entities completion {{{
195d5cdbeb8SBram Moolenaar	if exists("b:entitiescompl")
196d5cdbeb8SBram Moolenaar		unlet! b:entitiescompl
197d5cdbeb8SBram Moolenaar
198f193fffdSBram Moolenaar		if !exists("b:html_doctype")
199f193fffdSBram Moolenaar			call htmlcomplete#CheckDoctype()
200f193fffdSBram Moolenaar		endif
201fc1421ebSBram Moolenaar		if !exists("b:html_omni")
202c1e37901SBram Moolenaar			"runtime! autoload/xml/xhtml10s.vim
203c1e37901SBram Moolenaar			call htmlcomplete#LoadData()
204a5792f58SBram Moolenaar		endif
205d5cdbeb8SBram Moolenaar
206fc1421ebSBram Moolenaar	    let entities =  b:html_omni['vimxmlentities']
207a5792f58SBram Moolenaar
20809df3127SBram Moolenaar		if len(a:base) == 1
209a5792f58SBram Moolenaar			for m in entities
2108349fd7cSBram Moolenaar				if m =~ '^'.a:base
211d5cdbeb8SBram Moolenaar					call add(res, m.';')
212d5cdbeb8SBram Moolenaar				endif
213d5cdbeb8SBram Moolenaar			endfor
2148349fd7cSBram Moolenaar			return res
21509df3127SBram Moolenaar		else
21609df3127SBram Moolenaar			for m in entities
21709df3127SBram Moolenaar				if m =~? '^'.a:base
21809df3127SBram Moolenaar					call add(res, m.';')
21909df3127SBram Moolenaar				elseif m =~? a:base
22009df3127SBram Moolenaar					call add(res2, m.';')
22109df3127SBram Moolenaar				endif
22209df3127SBram Moolenaar			endfor
22309df3127SBram Moolenaar
22409df3127SBram Moolenaar			return res + res2
22509df3127SBram Moolenaar		endif
22609df3127SBram Moolenaar
227d5cdbeb8SBram Moolenaar
228d5cdbeb8SBram Moolenaar	endif
2298b6144bdSBram Moolenaar	" }}}
230d5cdbeb8SBram Moolenaar	if context =~ '>'
231d5cdbeb8SBram Moolenaar		" Generally if context contains > it means we are outside of tag and
232bfd8fc05SBram Moolenaar		" should abandon action - with one exception: <style> span { bo
233d5cdbeb8SBram Moolenaar		if context =~ 'style[^>]\{-}>[^<]\{-}$'
234d5cdbeb8SBram Moolenaar			return csscomplete#CompleteCSS(0, context)
235b8a7b560SBram Moolenaar		elseif context =~ 'script[^>]\{-}>[^<]\{-}$'
236b8a7b560SBram Moolenaar			let b:jsrange = [line('.'), search('<\/script\>', 'nW')]
237b8a7b560SBram Moolenaar			return javascriptcomplete#CompleteJS(0, context)
238bfd8fc05SBram Moolenaar		else
239f75a963eSBram Moolenaar			return []
240f75a963eSBram Moolenaar		endif
241bfd8fc05SBram Moolenaar	endif
242bfd8fc05SBram Moolenaar
243d5cdbeb8SBram Moolenaar	" If context contains > it means we are already outside of tag and we
244bfd8fc05SBram Moolenaar	" should abandon action
245d5cdbeb8SBram Moolenaar	" If context contains white space it is attribute.
2468b6144bdSBram Moolenaar	" It can be also value of attribute.
2478b6144bdSBram Moolenaar	" We have to get first word to offer proper completions
2485be4ceecSBram Moolenaar	if context =~ '^\s*$'
2495be4ceecSBram Moolenaar		" empty or whitespace line
250d5cdbeb8SBram Moolenaar		let tag = ''
251d5cdbeb8SBram Moolenaar	else
252d5cdbeb8SBram Moolenaar		let tag = split(context)[0]
2534a85b415SBram Moolenaar		" Detect if tag is uppercase to return in proper case,
2544a85b415SBram Moolenaar		" we need to make it lowercase for processing
2554a85b415SBram Moolenaar		if tag =~ '^[A-Z]*$'
2568424a624SBram Moolenaar			let uppercase_tag = 1
2578424a624SBram Moolenaar			let tag = tolower(tag)
2588424a624SBram Moolenaar		else
2598424a624SBram Moolenaar			let uppercase_tag = 0
260d5cdbeb8SBram Moolenaar		endif
2618424a624SBram Moolenaar	endif
262bfd8fc05SBram Moolenaar	" Get last word, it should be attr name
263d5cdbeb8SBram Moolenaar	let attr = matchstr(context, '.*\s\zs.*')
264f75a963eSBram Moolenaar	" Possible situations where any prediction would be difficult:
265f75a963eSBram Moolenaar	" 1. Events attributes
266d5cdbeb8SBram Moolenaar	if context =~ '\s'
267f75a963eSBram Moolenaar		" Sort out style, class, and on* cases
2688b6144bdSBram Moolenaar		if context =~? "\\(on[a-z]*\\|id\\|style\\|class\\)\\s*=\\s*[\"']"
2698b6144bdSBram Moolenaar			" Id, class completion {{{
2708b6144bdSBram Moolenaar			if context =~? "\\(id\\|class\\)\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
2718b6144bdSBram Moolenaar				if context =~? "class\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
2721e015460SBram Moolenaar					let search_for = "class"
2738b6144bdSBram Moolenaar				elseif context =~? "id\\s*=\\s*[\"'][a-zA-Z0-9_ -]*$"
2741e015460SBram Moolenaar					let search_for = "id"
2751e015460SBram Moolenaar				endif
276bfd8fc05SBram Moolenaar				" Handle class name completion
277bfd8fc05SBram Moolenaar				" 1. Find lines of <link stylesheet>
278bfd8fc05SBram Moolenaar				" 1a. Check file for @import
279bfd8fc05SBram Moolenaar				" 2. Extract filename(s?) of stylesheet,
280bfd8fc05SBram Moolenaar				call cursor(1,1)
281bfd8fc05SBram Moolenaar				let head = getline(search('<head\>'), search('<\/head>'))
282bfd8fc05SBram Moolenaar				let headjoined = join(copy(head), ' ')
283bfd8fc05SBram Moolenaar				if headjoined =~ '<style'
284a5792f58SBram Moolenaar					" Remove possibly confusing CSS operators
2851e015460SBram Moolenaar					let stylehead = substitute(headjoined, '+>\*[,', ' ', 'g')
2861e015460SBram Moolenaar					if search_for == 'class'
287bfd8fc05SBram Moolenaar						let styleheadlines = split(stylehead)
288bfd8fc05SBram Moolenaar						let headclasslines = filter(copy(styleheadlines), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
2891e015460SBram Moolenaar					else
2901e015460SBram Moolenaar						let stylesheet = split(headjoined, '[{}]')
2911e015460SBram Moolenaar						" Get all lines which fit id syntax
2921e015460SBram Moolenaar						let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
2931e015460SBram Moolenaar						" Filter out possible color definitions
2941e015460SBram Moolenaar						call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
2951e015460SBram Moolenaar						" Filter out complex border definitions
2961e015460SBram Moolenaar						call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
2971e015460SBram Moolenaar						let templines = join(classlines, ' ')
2981e015460SBram Moolenaar						let headclasslines = split(templines)
2991e015460SBram Moolenaar						call filter(headclasslines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
3001e015460SBram Moolenaar					endif
301bfd8fc05SBram Moolenaar					let internal = 1
302bfd8fc05SBram Moolenaar				else
303bfd8fc05SBram Moolenaar					let internal = 0
304bfd8fc05SBram Moolenaar				endif
305bfd8fc05SBram Moolenaar				let styletable = []
306bfd8fc05SBram Moolenaar				let secimportfiles = []
307bfd8fc05SBram Moolenaar				let filestable = filter(copy(head), "v:val =~ '\\(@import\\|link.*stylesheet\\)'")
308bfd8fc05SBram Moolenaar				for line in filestable
309bfd8fc05SBram Moolenaar					if line =~ "@import"
310bfd8fc05SBram Moolenaar						let styletable += [matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")]
311bfd8fc05SBram Moolenaar					elseif line =~ "<link"
312bfd8fc05SBram Moolenaar						let styletable += [matchstr(line, "href\\s*=\\s*[\"']\\zs\\f\\+\\ze")]
313bfd8fc05SBram Moolenaar					endif
314bfd8fc05SBram Moolenaar				endfor
3151e015460SBram Moolenaar				for file in styletable
316bfd8fc05SBram Moolenaar					if filereadable(file)
317bfd8fc05SBram Moolenaar						let stylesheet = readfile(file)
318bfd8fc05SBram Moolenaar						let secimport = filter(copy(stylesheet), "v:val =~ '@import'")
319bfd8fc05SBram Moolenaar						if len(secimport) > 0
320bfd8fc05SBram Moolenaar							for line in secimport
3211e015460SBram Moolenaar								let secfile = matchstr(line, "import\\s\\+\\(url(\\)\\?[\"']\\?\\zs\\f\\+\\ze")
3221e015460SBram Moolenaar								let secfile = fnamemodify(file, ":p:h").'/'.secfile
3231e015460SBram Moolenaar								let secimportfiles += [secfile]
324bfd8fc05SBram Moolenaar							endfor
325bfd8fc05SBram Moolenaar						endif
326bfd8fc05SBram Moolenaar					endif
327bfd8fc05SBram Moolenaar				endfor
328bfd8fc05SBram Moolenaar				let cssfiles = styletable + secimportfiles
329bfd8fc05SBram Moolenaar				let classes = []
330bfd8fc05SBram Moolenaar				for file in cssfiles
3318e5af3e5SBram Moolenaar				  	let classlines = []
332bfd8fc05SBram Moolenaar					if filereadable(file)
333bfd8fc05SBram Moolenaar						let stylesheet = readfile(file)
3341e015460SBram Moolenaar						let stylefile = join(stylesheet, ' ')
3351e015460SBram Moolenaar						let stylefile = substitute(stylefile, '+>\*[,', ' ', 'g')
3361e015460SBram Moolenaar						if search_for == 'class'
337bfd8fc05SBram Moolenaar							let stylesheet = split(stylefile)
338bfd8fc05SBram Moolenaar							let classlines = filter(copy(stylesheet), "v:val =~ '\\([a-zA-Z0-9:]\\+\\)\\?\\.[a-zA-Z0-9_-]\\+'")
3391e015460SBram Moolenaar						else
3401e015460SBram Moolenaar							let stylesheet = split(stylefile, '[{}]')
3411e015460SBram Moolenaar							" Get all lines which fit id syntax
3421e015460SBram Moolenaar							let classlines = filter(copy(stylesheet), "v:val =~ '#[a-zA-Z0-9_-]\\+'")
3431e015460SBram Moolenaar							" Filter out possible color definitions
3441e015460SBram Moolenaar							call filter(classlines, "v:val !~ ':\\s*#[a-zA-Z0-9_-]\\+'")
3451e015460SBram Moolenaar							" Filter out complex border definitions
3461e015460SBram Moolenaar							call filter(classlines, "v:val !~ '\\(none\\|hidden\\|dotted\\|dashed\\|solid\\|double\\|groove\\|ridge\\|inset\\|outset\\)\\s*#[a-zA-Z0-9_-]\\+'")
3471e015460SBram Moolenaar							let templines = join(classlines, ' ')
3481e015460SBram Moolenaar							let stylelines = split(templines)
3491e015460SBram Moolenaar							let classlines = filter(stylelines, "v:val =~ '#[a-zA-Z0-9_-]\\+'")
3501e015460SBram Moolenaar
3511e015460SBram Moolenaar						endif
352bfd8fc05SBram Moolenaar					endif
353bfd8fc05SBram Moolenaar					" We gathered classes definitions from all external files
354bfd8fc05SBram Moolenaar					let classes += classlines
355bfd8fc05SBram Moolenaar				endfor
356bfd8fc05SBram Moolenaar				if internal == 1
357bfd8fc05SBram Moolenaar					let classes += headclasslines
358bfd8fc05SBram Moolenaar				endif
3591e015460SBram Moolenaar
3601e015460SBram Moolenaar				if search_for == 'class'
361bfd8fc05SBram Moolenaar					let elements = {}
362bfd8fc05SBram Moolenaar					for element in classes
363bfd8fc05SBram Moolenaar						if element =~ '^\.'
364bfd8fc05SBram Moolenaar							let class = matchstr(element, '^\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
365bfd8fc05SBram Moolenaar							let class = substitute(class, ':.*', '', '')
3661e015460SBram Moolenaar							if has_key(elements, 'common')
3671e015460SBram Moolenaar								let elements['common'] .= ' '.class
368bfd8fc05SBram Moolenaar							else
3691e015460SBram Moolenaar								let elements['common'] = class
370bfd8fc05SBram Moolenaar							endif
371bfd8fc05SBram Moolenaar						else
372bfd8fc05SBram Moolenaar							let class = matchstr(element, '[a-zA-Z1-6]*\.\zs[a-zA-Z][a-zA-Z0-9_-]*\ze')
373bfd8fc05SBram Moolenaar							let tagname = tolower(matchstr(element, '[a-zA-Z1-6]*\ze.'))
374bfd8fc05SBram Moolenaar							if tagname != ''
375bfd8fc05SBram Moolenaar								if has_key(elements, tagname)
3761e015460SBram Moolenaar									let elements[tagname] .= ' '.class
377bfd8fc05SBram Moolenaar								else
378bfd8fc05SBram Moolenaar									let elements[tagname] = class
379bfd8fc05SBram Moolenaar								endif
380bfd8fc05SBram Moolenaar							endif
381bfd8fc05SBram Moolenaar						endif
382bfd8fc05SBram Moolenaar					endfor
383bfd8fc05SBram Moolenaar
3841e015460SBram Moolenaar					if has_key(elements, tag) && has_key(elements, 'common')
3851e015460SBram Moolenaar						let values = split(elements[tag]." ".elements['common'])
3861e015460SBram Moolenaar					elseif has_key(elements, tag) && !has_key(elements, 'common')
387bfd8fc05SBram Moolenaar						let values = split(elements[tag])
3881e015460SBram Moolenaar					elseif !has_key(elements, tag) && has_key(elements, 'common')
3891e015460SBram Moolenaar						let values = split(elements['common'])
390bfd8fc05SBram Moolenaar					else
391bfd8fc05SBram Moolenaar						return []
392bfd8fc05SBram Moolenaar					endif
393bfd8fc05SBram Moolenaar
3941e015460SBram Moolenaar				elseif search_for == 'id'
3951e015460SBram Moolenaar					" Find used IDs
3961e015460SBram Moolenaar					" 1. Catch whole file
3971e015460SBram Moolenaar					let filelines = getline(1, line('$'))
3981e015460SBram Moolenaar					" 2. Find lines with possible id
3991e015460SBram Moolenaar					let used_id_lines = filter(filelines, 'v:val =~ "id\\s*=\\s*[\"''][a-zA-Z0-9_-]\\+"')
4001e015460SBram Moolenaar					" 3a. Join all filtered lines
4011e015460SBram Moolenaar					let id_string = join(used_id_lines, ' ')
4021e015460SBram Moolenaar					" 3b. And split them to be sure each id is in separate item
4031e015460SBram Moolenaar					let id_list = split(id_string, 'id\s*=\s*')
4041e015460SBram Moolenaar					" 4. Extract id values
4051e015460SBram Moolenaar					let used_id = map(id_list, 'matchstr(v:val, "[\"'']\\zs[a-zA-Z0-9_-]\\+\\ze")')
4061e015460SBram Moolenaar					let joined_used_id = ','.join(used_id, ',').','
4071e015460SBram Moolenaar
4081e015460SBram Moolenaar					let allvalues = map(classes, 'matchstr(v:val, ".*#\\zs[a-zA-Z0-9_-]\\+")')
4091e015460SBram Moolenaar
4101e015460SBram Moolenaar					let values = []
4111e015460SBram Moolenaar
4121e015460SBram Moolenaar					for element in classes
4131e015460SBram Moolenaar						if joined_used_id !~ ','.element.','
4141e015460SBram Moolenaar							let values += [element]
4151e015460SBram Moolenaar						endif
4161e015460SBram Moolenaar
4171e015460SBram Moolenaar					endfor
4181e015460SBram Moolenaar
4191e015460SBram Moolenaar				endif
4201e015460SBram Moolenaar
421bfd8fc05SBram Moolenaar				" We need special version of sbase
422d5cdbeb8SBram Moolenaar				let classbase = matchstr(context, ".*[\"']")
423bfd8fc05SBram Moolenaar				let classquote = matchstr(classbase, '.$')
424bfd8fc05SBram Moolenaar
425bfd8fc05SBram Moolenaar				let entered_class = matchstr(attr, ".*=\\s*[\"']\\zs.*")
426bfd8fc05SBram Moolenaar
427bfd8fc05SBram Moolenaar				for m in sort(values)
428bfd8fc05SBram Moolenaar					if m =~? '^'.entered_class
429d5cdbeb8SBram Moolenaar						call add(res, m . classquote)
430bfd8fc05SBram Moolenaar					elseif m =~? entered_class
431d5cdbeb8SBram Moolenaar						call add(res2, m . classquote)
432bfd8fc05SBram Moolenaar					endif
433bfd8fc05SBram Moolenaar				endfor
434bfd8fc05SBram Moolenaar
435bfd8fc05SBram Moolenaar				return res + res2
436bfd8fc05SBram Moolenaar
4378b6144bdSBram Moolenaar			elseif context =~? "style\\s*=\\s*[\"'][^\"']*$"
438d5cdbeb8SBram Moolenaar				return csscomplete#CompleteCSS(0, context)
439bfd8fc05SBram Moolenaar
440bfd8fc05SBram Moolenaar			endif
4418b6144bdSBram Moolenaar			" }}}
4428b6144bdSBram Moolenaar			" Complete on-events {{{
4438b6144bdSBram Moolenaar			if context =~? 'on[a-z]*\s*=\s*\(''[^'']*\|"[^"]*\)$'
4448b6144bdSBram Moolenaar				" We have to:
4458b6144bdSBram Moolenaar				" 1. Find external files
4468b6144bdSBram Moolenaar				let b:js_extfiles = []
4478b6144bdSBram Moolenaar				let l = line('.')
4488b6144bdSBram Moolenaar				let c = col('.')
4498b6144bdSBram Moolenaar				call cursor(1,1)
4508b6144bdSBram Moolenaar				while search('<\@<=script\>', 'W') && line('.') <= l
4518b6144bdSBram Moolenaar					if synIDattr(synID(line('.'),col('.')-1,0),"name") !~? 'comment'
4528b6144bdSBram Moolenaar						let sname = matchstr(getline('.'), '<script[^>]*src\s*=\s*\([''"]\)\zs.\{-}\ze\1')
4538b6144bdSBram Moolenaar						if filereadable(sname)
4548b6144bdSBram Moolenaar							let b:js_extfiles += readfile(sname)
4558b6144bdSBram Moolenaar						endif
4568b6144bdSBram Moolenaar					endif
4578b6144bdSBram Moolenaar				endwhile
4588b6144bdSBram Moolenaar				" 2. Find at least one <script> tag
4598b6144bdSBram Moolenaar				call cursor(1,1)
4608b6144bdSBram Moolenaar				let js_scripttags = []
4618b6144bdSBram Moolenaar				while search('<script\>', 'W') && line('.') < l
4628b6144bdSBram Moolenaar					if matchstr(getline('.'), '<script[^>]*src') == ''
4638b6144bdSBram Moolenaar						let js_scripttag = getline(line('.'), search('</script>', 'W'))
4648b6144bdSBram Moolenaar						let js_scripttags += js_scripttag
4658b6144bdSBram Moolenaar					endif
4668b6144bdSBram Moolenaar				endwhile
4678b6144bdSBram Moolenaar				let b:js_extfiles += js_scripttags
4688b6144bdSBram Moolenaar
4698b6144bdSBram Moolenaar				" 3. Proper call for javascriptcomplete#CompleteJS
4708b6144bdSBram Moolenaar				call cursor(l,c)
4711d2ba7faSBram Moolenaar				let js_context = matchstr(a:base, '\k\+$')
4728b6144bdSBram Moolenaar				let js_shortcontext = substitute(a:base, js_context.'$', '', '')
4738b6144bdSBram Moolenaar				let b:compl_context = context
4748b6144bdSBram Moolenaar				let b:jsrange = [l, l]
4758b6144bdSBram Moolenaar				unlet! l c
4768b6144bdSBram Moolenaar				return javascriptcomplete#CompleteJS(0, js_context)
4778b6144bdSBram Moolenaar
4788b6144bdSBram Moolenaar			endif
4798b6144bdSBram Moolenaar
4808b6144bdSBram Moolenaar			" }}}
4818b6144bdSBram Moolenaar			let stripbase = matchstr(context, ".*\\(on[a-zA-Z]*\\|style\\|class\\)\\s*=\\s*[\"']\\zs.*")
482d5cdbeb8SBram Moolenaar			" Now we have context stripped from all chars up to style/class.
483f75a963eSBram Moolenaar			" It may fail with some strange style value combinations.
484f75a963eSBram Moolenaar			if stripbase !~ "[\"']"
485f75a963eSBram Moolenaar				return []
486f75a963eSBram Moolenaar			endif
487f75a963eSBram Moolenaar		endif
4888b6144bdSBram Moolenaar		" Value of attribute completion {{{
489*6c391a74SBram Moolenaar		" If attr contains =\s*[\"'] we match value of attribute
4908424a624SBram Moolenaar		if attr =~ "=\s*[\"']" || attr =~ "=\s*$"
491f75a963eSBram Moolenaar			" Let do attribute specific completion
492f75a963eSBram Moolenaar			let attrname = matchstr(attr, '.*\ze\s*=')
4938424a624SBram Moolenaar			let entered_value = matchstr(attr, ".*=\\s*[\"']\\?\\zs.*")
494f75a963eSBram Moolenaar			let values = []
495d5ab34bdSBram Moolenaar			" Load data {{{
496d5ab34bdSBram Moolenaar			if !exists("b:html_doctype")
497d5ab34bdSBram Moolenaar				call htmlcomplete#CheckDoctype()
498d5ab34bdSBram Moolenaar			endif
499d5ab34bdSBram Moolenaar			if !exists("b:html_omni")
500d5ab34bdSBram Moolenaar				"runtime! autoload/xml/xhtml10s.vim
501d5ab34bdSBram Moolenaar				call htmlcomplete#LoadData()
502d5ab34bdSBram Moolenaar			endif
503d5ab34bdSBram Moolenaar			" }}}
504c1e37901SBram Moolenaar			if attrname == 'href'
505f75a963eSBram Moolenaar				" Now we are looking for local anchors defined by name or id
506f75a963eSBram Moolenaar				if entered_value =~ '^#'
507f75a963eSBram Moolenaar					let file = join(getline(1, line('$')), ' ')
508f75a963eSBram Moolenaar					" Split it be sure there will be one id/name element in
509f75a963eSBram Moolenaar					" item, it will be also first word [a-zA-Z0-9_-] in element
510f75a963eSBram Moolenaar					let oneelement = split(file, "\\(meta \\)\\@<!\\(name\\|id\\)\\s*=\\s*[\"']")
511f75a963eSBram Moolenaar					for i in oneelement
512f75a963eSBram Moolenaar						let values += ['#'.matchstr(i, "^[a-zA-Z][a-zA-Z0-9%_-]*")]
513f75a963eSBram Moolenaar					endfor
514f75a963eSBram Moolenaar				endif
515c1e37901SBram Moolenaar			else
516fc1421ebSBram Moolenaar				if has_key(b:html_omni, tag) && has_key(b:html_omni[tag][1], attrname)
517fc1421ebSBram Moolenaar					let values = b:html_omni[tag][1][attrname]
518f75a963eSBram Moolenaar				else
519f75a963eSBram Moolenaar					return []
520f75a963eSBram Moolenaar				endif
521c1e37901SBram Moolenaar			endif
522f75a963eSBram Moolenaar
523f75a963eSBram Moolenaar			if len(values) == 0
524f75a963eSBram Moolenaar				return []
525f75a963eSBram Moolenaar			endif
526f75a963eSBram Moolenaar
527f75a963eSBram Moolenaar			" We need special version of sbase
528d5cdbeb8SBram Moolenaar			let attrbase = matchstr(context, ".*[\"']")
529bfd8fc05SBram Moolenaar			let attrquote = matchstr(attrbase, '.$')
5308424a624SBram Moolenaar			if attrquote !~ "['\"]"
5318424a624SBram Moolenaar				let attrquoteopen = '"'
5328424a624SBram Moolenaar				let attrquote = '"'
5338424a624SBram Moolenaar			else
5348424a624SBram Moolenaar				let attrquoteopen = ''
5358424a624SBram Moolenaar			endif
536f75a963eSBram Moolenaar
537f75a963eSBram Moolenaar			for m in values
538bfd8fc05SBram Moolenaar				" This if is needed to not offer all completions as-is
539bfd8fc05SBram Moolenaar				" alphabetically but sort them. Those beginning with entered
540bfd8fc05SBram Moolenaar				" part will be as first choices
541bfd8fc05SBram Moolenaar				if m =~ '^'.entered_value
542f9393ef5SBram Moolenaar					call add(res, attrquoteopen . m . attrquote)
543bfd8fc05SBram Moolenaar				elseif m =~ entered_value
544f9393ef5SBram Moolenaar					call add(res2, attrquoteopen . m . attrquote)
545f75a963eSBram Moolenaar				endif
546f75a963eSBram Moolenaar			endfor
547bfd8fc05SBram Moolenaar
548bfd8fc05SBram Moolenaar			return res + res2
549bfd8fc05SBram Moolenaar
550f75a963eSBram Moolenaar		endif
5518b6144bdSBram Moolenaar		" }}}
5528b6144bdSBram Moolenaar		" Attribute completion {{{
553d5cdbeb8SBram Moolenaar		" Shorten context to not include last word
554d5cdbeb8SBram Moolenaar		let sbase = matchstr(context, '.*\ze\s.*')
5551d2ba7faSBram Moolenaar
5561d2ba7faSBram Moolenaar		" Load data {{{
557f193fffdSBram Moolenaar		if !exists("b:html_doctype")
558f193fffdSBram Moolenaar			call htmlcomplete#CheckDoctype()
559f193fffdSBram Moolenaar		endif
560f193fffdSBram Moolenaar		if !exists("b:html_omni")
561c1e37901SBram Moolenaar			call htmlcomplete#LoadData()
562f75a963eSBram Moolenaar		endif
5631d2ba7faSBram Moolenaar		" }}}
5648424a624SBram Moolenaar
565fc1421ebSBram Moolenaar		if has_key(b:html_omni, tag)
566fc1421ebSBram Moolenaar			let attrs = keys(b:html_omni[tag][1])
5678424a624SBram Moolenaar		else
5688424a624SBram Moolenaar			return []
5698424a624SBram Moolenaar		endif
570f75a963eSBram Moolenaar
571f75a963eSBram Moolenaar		for m in sort(attrs)
572bfd8fc05SBram Moolenaar			if m =~ '^'.attr
573d5cdbeb8SBram Moolenaar				call add(res, m)
574bfd8fc05SBram Moolenaar			elseif m =~ attr
575d5cdbeb8SBram Moolenaar				call add(res2, m)
576f75a963eSBram Moolenaar			endif
577f75a963eSBram Moolenaar		endfor
5781d2ba7faSBram Moolenaar		let menu = res + res2
579fc1421ebSBram Moolenaar		if has_key(b:html_omni, 'vimxmlattrinfo')
5801d2ba7faSBram Moolenaar			let final_menu = []
5811d2ba7faSBram Moolenaar			for i in range(len(menu))
5821d2ba7faSBram Moolenaar				let item = menu[i]
583fc1421ebSBram Moolenaar				if has_key(b:html_omni['vimxmlattrinfo'], item)
584fc1421ebSBram Moolenaar					let m_menu = b:html_omni['vimxmlattrinfo'][item][0]
585fc1421ebSBram Moolenaar					let m_info = b:html_omni['vimxmlattrinfo'][item][1]
5861d2ba7faSBram Moolenaar				else
5871d2ba7faSBram Moolenaar					let m_menu = ''
5881d2ba7faSBram Moolenaar					let m_info = ''
589f193fffdSBram Moolenaar				endif
590f193fffdSBram Moolenaar				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
591f193fffdSBram Moolenaar					let item = item
592f193fffdSBram Moolenaar					let m_menu = 'Bool'
593f193fffdSBram Moolenaar				else
5941d2ba7faSBram Moolenaar					let item .= '="'
5951d2ba7faSBram Moolenaar				endif
5961d2ba7faSBram Moolenaar				let final_menu += [{'word':item, 'menu':m_menu, 'info':m_info}]
5971d2ba7faSBram Moolenaar			endfor
5981d2ba7faSBram Moolenaar		else
599f193fffdSBram Moolenaar			let final_menu = []
600f193fffdSBram Moolenaar			for i in range(len(menu))
601f193fffdSBram Moolenaar				let item = menu[i]
602f193fffdSBram Moolenaar				if len(b:html_omni[tag][1][item]) > 0 && b:html_omni[tag][1][item][0] =~ '^\(BOOL\|'.item.'\)$'
603f193fffdSBram Moolenaar					let item = item
604f193fffdSBram Moolenaar				else
605f193fffdSBram Moolenaar					let item .= '="'
606f193fffdSBram Moolenaar				endif
607f193fffdSBram Moolenaar				let final_menu += [item]
608f193fffdSBram Moolenaar			endfor
609f193fffdSBram Moolenaar			return final_menu
610f193fffdSBram Moolenaar
6111d2ba7faSBram Moolenaar		endif
6121d2ba7faSBram Moolenaar		return final_menu
613bfd8fc05SBram Moolenaar
614f75a963eSBram Moolenaar	endif
6158b6144bdSBram Moolenaar	" }}}
6168b6144bdSBram Moolenaar	" Close tag {{{
6174c903f98SBram Moolenaar	let b:unaryTagsStack = "base meta link hr br param img area input col"
618d5cdbeb8SBram Moolenaar	if context =~ '^\/'
619362e1a30SBram Moolenaar		if context =~ '^\/.'
620362e1a30SBram Moolenaar			return []
621362e1a30SBram Moolenaar		else
622a5792f58SBram Moolenaar			let opentag = xmlcomplete#GetLastOpenTag("b:unaryTagsStack")
623d5cdbeb8SBram Moolenaar			return [opentag.">"]
6244c903f98SBram Moolenaar		endif
625362e1a30SBram Moolenaar	endif
6268424a624SBram Moolenaar	" }}}
6278b6144bdSBram Moolenaar	" Load data {{{
628f193fffdSBram Moolenaar	if !exists("b:html_doctype")
629f193fffdSBram Moolenaar		call htmlcomplete#CheckDoctype()
630f193fffdSBram Moolenaar	endif
631fc1421ebSBram Moolenaar	if !exists("b:html_omni")
632c1e37901SBram Moolenaar		"runtime! autoload/xml/xhtml10s.vim
633c1e37901SBram Moolenaar		call htmlcomplete#LoadData()
6344c903f98SBram Moolenaar	endif
6358b6144bdSBram Moolenaar	" }}}
6368b6144bdSBram Moolenaar	" Tag completion {{{
6377e8fd636SBram Moolenaar	" Deal with tag completion.
6388424a624SBram Moolenaar	let opentag = tolower(xmlcomplete#GetLastOpenTag("b:unaryTagsStack"))
639362e1a30SBram Moolenaar	" MM: TODO: GLOT works always the same but with some weird situation it
640362e1a30SBram Moolenaar	" behaves as intended in HTML but screws in PHP
6414a85b415SBram Moolenaar	if opentag == '' || &filetype == 'php' && !has_key(b:html_omni, opentag)
6427e8fd636SBram Moolenaar		" Hack for sometimes failing GetLastOpenTag.
6437e8fd636SBram Moolenaar		" As far as I tested fail isn't GLOT fault but problem
6447e8fd636SBram Moolenaar		" of invalid document - not properly closed tags and other mish-mash.
6457e8fd636SBram Moolenaar		" Also when document is empty. Return list of *all* tags.
646fc1421ebSBram Moolenaar	    let tags = keys(b:html_omni)
6477e8fd636SBram Moolenaar		call filter(tags, 'v:val !~ "^vimxml"')
6487e8fd636SBram Moolenaar	else
649fc1421ebSBram Moolenaar		if has_key(b:html_omni, opentag)
650fc1421ebSBram Moolenaar			let tags = b:html_omni[opentag][0]
6518424a624SBram Moolenaar		else
6528424a624SBram Moolenaar			return []
6538424a624SBram Moolenaar		endif
6547e8fd636SBram Moolenaar	endif
6557e8fd636SBram Moolenaar	" }}}
6567e8fd636SBram Moolenaar
6578424a624SBram Moolenaar	if exists("uppercase_tag") && uppercase_tag == 1
6588424a624SBram Moolenaar		let context = tolower(context)
6598424a624SBram Moolenaar	endif
660d5ab34bdSBram Moolenaar	" Handle XML keywords: DOCTYPE
661f193fffdSBram Moolenaar	if opentag == ''
6624a85b415SBram Moolenaar		let tags += [
663d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">',
664d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">',
665d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">',
666d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">',
667d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
668d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
669d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
670d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
671d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
672d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
673d5ab34bdSBram Moolenaar				\ '!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/1999/xhtml">'
6744a85b415SBram Moolenaar				\ ]
6754a85b415SBram Moolenaar	endif
6768424a624SBram Moolenaar
677cef9dcc3SBram Moolenaar	for m in sort(tags)
678d5cdbeb8SBram Moolenaar		if m =~ '^'.context
6794c903f98SBram Moolenaar			call add(res, m)
680d5cdbeb8SBram Moolenaar		elseif m =~ context
681bfd8fc05SBram Moolenaar			call add(res2, m)
6824c903f98SBram Moolenaar		endif
6834c903f98SBram Moolenaar	endfor
6841d2ba7faSBram Moolenaar	let menu = res + res2
685fc1421ebSBram Moolenaar	if has_key(b:html_omni, 'vimxmltaginfo')
6861d2ba7faSBram Moolenaar		let final_menu = []
6871d2ba7faSBram Moolenaar		for i in range(len(menu))
6881d2ba7faSBram Moolenaar			let item = menu[i]
689fc1421ebSBram Moolenaar			if has_key(b:html_omni['vimxmltaginfo'], item)
690fc1421ebSBram Moolenaar				let m_menu = b:html_omni['vimxmltaginfo'][item][0]
691fc1421ebSBram Moolenaar				let m_info = b:html_omni['vimxmltaginfo'][item][1]
6921d2ba7faSBram Moolenaar			else
6931d2ba7faSBram Moolenaar				let m_menu = ''
6941d2ba7faSBram Moolenaar				let m_info = ''
6951d2ba7faSBram Moolenaar			endif
6964a85b415SBram Moolenaar			if &filetype == 'html' && exists("uppercase_tag") && uppercase_tag == 1 && item !~ 'DOCTYPE'
697fc1421ebSBram Moolenaar				let item = toupper(item)
698fc1421ebSBram Moolenaar			endif
6994a85b415SBram Moolenaar			if item =~ 'DOCTYPE'
7004a85b415SBram Moolenaar				let abbr = 'DOCTYPE '.matchstr(item, 'DTD \zsX\?HTML .\{-}\ze\/\/')
7014a85b415SBram Moolenaar			else
7024a85b415SBram Moolenaar				let abbr = item
7034a85b415SBram Moolenaar			endif
7044a85b415SBram Moolenaar			let final_menu += [{'abbr':abbr, 'word':item, 'menu':m_menu, 'info':m_info}]
7051d2ba7faSBram Moolenaar		endfor
7061d2ba7faSBram Moolenaar	else
7071d2ba7faSBram Moolenaar		let final_menu = menu
7081d2ba7faSBram Moolenaar	endif
7091d2ba7faSBram Moolenaar	return final_menu
7106b730e11SBram Moolenaar
7118b6144bdSBram Moolenaar	" }}}
7124c903f98SBram Moolenaar  endif
7136b730e11SBram Moolenaarendfunction
714c1e37901SBram Moolenaar
715c1e37901SBram Moolenaarfunction! htmlcomplete#LoadData() " {{{
716fc1421ebSBram Moolenaar	if !exists("b:html_omni_flavor")
7174a85b415SBram Moolenaar		if &filetype == 'html'
718fc1421ebSBram Moolenaar			let b:html_omni_flavor = 'html401t'
719fc1421ebSBram Moolenaar		else
720fc1421ebSBram Moolenaar			let b:html_omni_flavor = 'xhtml10s'
721c1e37901SBram Moolenaar		endif
722fc1421ebSBram Moolenaar	endif
723fc1421ebSBram Moolenaar	" With that if we still have bloated memory but create new buffer
724fc1421ebSBram Moolenaar	" variables only by linking to existing g:variable, not sourcing whole
725fc1421ebSBram Moolenaar	" file.
726fc1421ebSBram Moolenaar	if exists('g:xmldata_'.b:html_omni_flavor)
727fc1421ebSBram Moolenaar		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
728fc1421ebSBram Moolenaar	else
729fc1421ebSBram Moolenaar		exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
730f193fffdSBram Moolenaar		exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
731fc1421ebSBram Moolenaar	endif
732f193fffdSBram Moolenaarendfunction
733f193fffdSBram Moolenaar" }}}
734f193fffdSBram Moolenaarfunction! htmlcomplete#CheckDoctype() " {{{
735f193fffdSBram Moolenaar	if exists('b:html_omni_flavor')
736f193fffdSBram Moolenaar		let old_flavor = b:html_omni_flavor
737fc1421ebSBram Moolenaar	else
738f193fffdSBram Moolenaar		let old_flavor = ''
739f193fffdSBram Moolenaar	endif
740f193fffdSBram Moolenaar	let i = 1
741f193fffdSBram Moolenaar	while i < 10 && i < line("$")
742f193fffdSBram Moolenaar		let line = getline(i)
743f193fffdSBram Moolenaar		if line =~ '<!DOCTYPE.*\<DTD HTML 3\.2'
744f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html32'
745f193fffdSBram Moolenaar			let b:html_doctype = 1
746f193fffdSBram Moolenaar			break
747f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Transitional'
748f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html40t'
749f193fffdSBram Moolenaar			let b:html_doctype = 1
750f193fffdSBram Moolenaar			break
751f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0 Frameset'
752f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html40f'
753f193fffdSBram Moolenaar			let b:html_doctype = 1
754f193fffdSBram Moolenaar			break
755f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.0'
756f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html40s'
757f193fffdSBram Moolenaar			let b:html_doctype = 1
758f193fffdSBram Moolenaar			break
759f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Transitional'
760f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html401t'
761f193fffdSBram Moolenaar			let b:html_doctype = 1
762f193fffdSBram Moolenaar			break
763f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01 Frameset'
764f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html401f'
765f193fffdSBram Moolenaar			let b:html_doctype = 1
766f193fffdSBram Moolenaar			break
767f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD HTML 4\.01'
768f193fffdSBram Moolenaar			let b:html_omni_flavor = 'html401s'
769f193fffdSBram Moolenaar			let b:html_doctype = 1
770f193fffdSBram Moolenaar			break
771f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Transitional'
772f193fffdSBram Moolenaar			let b:html_omni_flavor = 'xhtml10t'
773f193fffdSBram Moolenaar			let b:html_doctype = 1
774f193fffdSBram Moolenaar			break
775f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Frameset'
776f193fffdSBram Moolenaar			let b:html_omni_flavor = 'xhtml10f'
777f193fffdSBram Moolenaar			let b:html_doctype = 1
778f193fffdSBram Moolenaar			break
779f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.0 Strict'
780fc1421ebSBram Moolenaar			let b:html_omni_flavor = 'xhtml10s'
781f193fffdSBram Moolenaar			let b:html_doctype = 1
782f193fffdSBram Moolenaar			break
783f193fffdSBram Moolenaar		elseif line =~ '<!DOCTYPE.*\<DTD XHTML 1\.1'
784f193fffdSBram Moolenaar			let b:html_omni_flavor = 'xhtml11'
785f193fffdSBram Moolenaar			let b:html_doctype = 1
786f193fffdSBram Moolenaar			break
787c1e37901SBram Moolenaar		endif
788f193fffdSBram Moolenaar		let i += 1
789f193fffdSBram Moolenaar	endwhile
790f193fffdSBram Moolenaar	if !exists("b:html_doctype")
791f193fffdSBram Moolenaar		return
792f193fffdSBram Moolenaar	else
793f193fffdSBram Moolenaar		" Tie g:xmldata with b:html_omni this way we need to sourca data file only
794f193fffdSBram Moolenaar		" once, not every time per buffer.
795f193fffdSBram Moolenaar		if old_flavor == b:html_omni_flavor
796f193fffdSBram Moolenaar			return
797f193fffdSBram Moolenaar		else
798fc1421ebSBram Moolenaar			if exists('g:xmldata_'.b:html_omni_flavor)
799fc1421ebSBram Moolenaar				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
800fc1421ebSBram Moolenaar			else
801fc1421ebSBram Moolenaar				exe 'runtime! autoload/xml/'.b:html_omni_flavor.'.vim'
802fc1421ebSBram Moolenaar				exe 'let b:html_omni = g:xmldata_'.b:html_omni_flavor
803fc1421ebSBram Moolenaar			endif
804f193fffdSBram Moolenaar			return
805f193fffdSBram Moolenaar		endif
806f193fffdSBram Moolenaar	endif
807c1e37901SBram Moolenaarendfunction
808c1e37901SBram Moolenaar" }}}
8098b6144bdSBram Moolenaar" vim:set foldmethod=marker:
810